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 support 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, int ifrfd, struct ifreq *ifr)
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 /* No match found, return first address as src/dhcp.c code does */
223 ifr->ifr_addr.sa_family = AF_INET;
224 if (ioctl(ifrfd, SIOCGIFADDR, ifr) != -1)
225 return ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
228 fprintf(stderr, "error: local IPv4 address not found\n");
232 else if (h->nlmsg_type == RTM_NEWADDR)
234 struct ifaddrmsg *ifa = NLMSG_DATA(h);
236 unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
238 if (ifa->ifa_index == index && ifa->ifa_family == AF_INET)
240 struct in_addr netmask, addr;
242 netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
245 for (rta = IFA_RTA(ifa); RTA_OK(rta, len1); rta = RTA_NEXT(rta, len1))
246 if (rta->rta_type == IFA_LOCAL)
247 addr = *((struct in_addr *)(rta+1));
249 if (addr.s_addr && is_same_net(addr, client, netmask))
258 int main(int argc, char **argv)
260 struct in_addr server, lease;
262 struct dhcp_packet packet;
263 unsigned char *p = packet.options;
264 struct sockaddr_in dest;
266 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
267 int nl = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
269 if (argc < 4 || argc > 5)
271 fprintf(stderr, "usage: dhcp_release <interface> <addr> <mac> [<client_id>]\n");
275 if (fd == -1 || nl == -1)
277 perror("cannot create socket");
281 /* This voodoo fakes up a packet coming from the correct interface, which really matters for
283 memset(&ifr, 0, sizeof(ifr));
284 strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
285 ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
286 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
288 perror("cannot setup interface");
292 if (inet_pton(AF_INET, argv[2], &lease.s_addr) < 1)
294 perror("invalid ip address");
298 server = find_interface(lease, nl, if_nametoindex(argv[1]), fd, &ifr);
300 memset(&packet, 0, sizeof(packet));
302 packet.hlen = parse_hex(argv[3], packet.chaddr, DHCP_CHADDR_MAX, &mac_type);
304 packet.htype = ARPHRD_ETHER;
306 packet.htype = mac_type;
308 packet.op = BOOTREQUEST;
309 packet.ciaddr = lease;
310 packet.cookie = htonl(DHCP_COOKIE);
312 *(p++) = OPTION_MESSAGE_TYPE;
314 *(p++) = DHCPRELEASE;
316 *(p++) = OPTION_SERVER_IDENTIFIER;
317 *(p++) = sizeof(server);
318 memcpy(p, &server, sizeof(server));
321 if (argc == 5 && strcmp(argv[4], "*") != 0)
323 unsigned int clid_len = parse_hex(argv[4], p+2, 255, NULL);
324 *(p++) = OPTION_CLIENT_ID;
331 dest.sin_family = AF_INET;
332 dest.sin_port = ntohs(DHCP_SERVER_PORT);
333 dest.sin_addr = server;
335 if (sendto(fd, &packet, sizeof(packet), 0,
336 (struct sockaddr *)&dest, sizeof(dest)) == -1)
338 perror("sendto failed");