1 /* Copyright (c) 2006 Simon Kelley
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
13 /* dhcp_release <interface> <address> <MAC address> <client_id>
14 MUST be run as root - will fail otherwise. */
16 /* Send a DHCPRELEASE message via the specified interface
17 to tell the local DHCP server to delete a particular lease.
19 The interface argument is the interface in which a DHCP
20 request _would_ be received if it was coming from the client,
21 rather than being faked up here.
23 The address argument is a dotted-quad IP addresses and mandatory.
25 The MAC address is colon separated hex, and is mandatory. It may be
26 prefixed by an address-type byte followed by -, eg
30 but if the address-type byte is missing it is assumed to be 1, the type
31 for ethernet. This encoding is the one used in dnsmasq lease files.
33 The client-id is optional. If it is "*" then it treated as being missing.
36 #include <sys/types.h>
37 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <sys/socket.h>
45 #include <net/if_arp.h>
46 #include <sys/ioctl.h>
47 #include <linux/types.h>
48 #include <linux/netlink.h>
49 #include <linux/rtnetlink.h>
52 #define DHCP_CHADDR_MAX 16
54 #define DHCP_COOKIE 0x63825363
55 #define OPTION_SERVER_IDENTIFIER 54
56 #define OPTION_CLIENT_ID 61
57 #define OPTION_MESSAGE_TYPE 53
58 #define OPTION_END 255
60 #define DHCP_SERVER_PORT 67
62 typedef unsigned char u8
;
63 typedef unsigned short u16
;
64 typedef unsigned int u32
;
67 u8 op
, htype
, hlen
, hops
;
70 struct in_addr ciaddr
, yiaddr
, siaddr
, giaddr
;
71 u8 chaddr
[DHCP_CHADDR_MAX
], sname
[64], file
[128];
73 unsigned char options
[308];
76 static struct iovec iov
;
78 static int expand_buf(struct iovec
*iov
, size_t size
)
82 if (size
<= iov
->iov_len
)
85 if (!(new = malloc(size
)))
93 memcpy(new, iov
->iov_base
, iov
->iov_len
);
103 static ssize_t
netlink_recv(int fd
)
108 msg
.msg_control
= NULL
;
109 msg
.msg_controllen
= 0;
118 while ((rc
= recvmsg(fd
, &msg
, MSG_PEEK
)) == -1 && errno
== EINTR
);
120 /* 2.2.x doesn't suport MSG_PEEK at all, returning EOPNOTSUPP, so we just grab a
121 big buffer and pray in that case. */
122 if (rc
== -1 && errno
== EOPNOTSUPP
)
124 if (!expand_buf(&iov
, 2000))
129 if (rc
== -1 || !(msg
.msg_flags
& MSG_TRUNC
))
132 if (!expand_buf(&iov
, iov
.iov_len
+ 100))
136 /* finally, read it for real */
137 while ((rc
= recvmsg(fd
, &msg
, 0)) == -1 && errno
== EINTR
);
142 static int parse_hex(char *in
, unsigned char *out
, int maxlen
, int *mac_type
)
150 while (maxlen
== -1 || i
< maxlen
)
152 for (r
= in
; *r
!= 0 && *r
!= ':' && *r
!= '-'; r
++);
158 if (*r
== '-' && i
== 0 && mac_type
)
161 *mac_type
= strtol(in
, NULL
, 16);
167 out
[i
] = strtol(in
, NULL
, 16);
176 static int is_same_net(struct in_addr a
, struct in_addr b
, struct in_addr mask
)
178 return (a
.s_addr
& mask
.s_addr
) == (b
.s_addr
& mask
.s_addr
);
181 static struct in_addr
find_interface(struct in_addr client
, int fd
, unsigned int index
)
183 struct sockaddr_nl addr
;
192 addr
.nl_family
= AF_NETLINK
;
195 addr
.nl_pid
= 0; /* address to kernel */
197 req
.nlh
.nlmsg_len
= sizeof(req
);
198 req
.nlh
.nlmsg_type
= RTM_GETADDR
;
199 req
.nlh
.nlmsg_flags
= NLM_F_ROOT
| NLM_F_MATCH
| NLM_F_REQUEST
| NLM_F_ACK
;
200 req
.nlh
.nlmsg_pid
= 0;
201 req
.nlh
.nlmsg_seq
= 1;
202 req
.g
.rtgen_family
= AF_INET
;
204 if (sendto(fd
, (void *)&req
, sizeof(req
), 0,
205 (struct sockaddr
*)&addr
, sizeof(addr
)) == -1)
207 perror("sendto failed");
213 if ((len
= netlink_recv(fd
)) == -1)
219 for (h
= (struct nlmsghdr
*)iov
.iov_base
; NLMSG_OK(h
, (size_t)len
); h
= NLMSG_NEXT(h
, len
))
220 if (h
->nlmsg_type
== NLMSG_DONE
)
222 else if (h
->nlmsg_type
== RTM_NEWADDR
)
224 struct ifaddrmsg
*ifa
= NLMSG_DATA(h
);
226 unsigned int len1
= h
->nlmsg_len
- NLMSG_LENGTH(sizeof(*ifa
));
228 if (ifa
->ifa_index
== index
&& ifa
->ifa_family
== AF_INET
)
230 struct in_addr netmask
, addr
;
232 netmask
.s_addr
= htonl(0xffffffff << (32 - ifa
->ifa_prefixlen
));
235 for (rta
= IFA_RTA(ifa
); RTA_OK(rta
, len1
); rta
= RTA_NEXT(rta
, len1
))
236 if (rta
->rta_type
== IFA_LOCAL
)
237 addr
= *((struct in_addr
*)(rta
+1));
239 if (addr
.s_addr
&& is_same_net(addr
, client
, netmask
))
248 int main(int argc
, char **argv
)
250 struct in_addr server
, lease
;
252 struct dhcp_packet packet
;
253 unsigned char *p
= packet
.options
;
254 struct sockaddr_in dest
;
256 int fd
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
257 int nl
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
259 if (argc
< 4 || argc
> 5)
261 fprintf(stderr
, "usage: dhcp_release <interface> <addr> <mac> [<client_id>]\n");
265 if (fd
== -1 || nl
== -1)
267 perror("cannot create socket");
271 /* This voodoo fakes up a packet coming from the correct interface, which really matters for
273 strcpy(ifr
.ifr_name
, argv
[1]);
274 if (setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, &ifr
, sizeof(ifr
)) == -1)
276 perror("cannot setup interface");
281 lease
.s_addr
= inet_addr(argv
[2]);
282 server
= find_interface(lease
, nl
, if_nametoindex(argv
[1]));
284 memset(&packet
, 0, sizeof(packet
));
286 packet
.hlen
= parse_hex(argv
[3], packet
.chaddr
, DHCP_CHADDR_MAX
, &mac_type
);
288 packet
.htype
= ARPHRD_ETHER
;
290 packet
.htype
= mac_type
;
292 packet
.op
= BOOTREQUEST
;
293 packet
.ciaddr
= lease
;
294 packet
.cookie
= htonl(DHCP_COOKIE
);
296 *(p
++) = OPTION_MESSAGE_TYPE
;
298 *(p
++) = DHCPRELEASE
;
300 *(p
++) = OPTION_SERVER_IDENTIFIER
;
301 *(p
++) = sizeof(server
);
302 memcpy(p
, &server
, sizeof(server
));
305 if (argc
== 5 && strcmp(argv
[4], "*") != 0)
307 unsigned int clid_len
= parse_hex(argv
[4], p
+2, 255, NULL
);
308 *(p
++) = OPTION_CLIENT_ID
;
315 dest
.sin_family
= AF_INET
;
316 dest
.sin_port
= ntohs(DHCP_SERVER_PORT
);
317 dest
.sin_addr
= server
;
319 if (sendto(fd
, &packet
, sizeof(packet
), 0,
320 (struct sockaddr
*)&dest
, sizeof(dest
)) == -1)
322 perror("sendto failed");