Annotation of embedaddon/dnsmasq/contrib/lease-tools/dhcp_release.c, revision 1.1.1.2
1.1 misho 1: /* Copyright (c) 2006 Simon Kelley
2:
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.
6:
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.
11: */
12:
13: /* dhcp_release <interface> <address> <MAC address> <client_id>
14: MUST be run as root - will fail otherwise. */
15:
16: /* Send a DHCPRELEASE message via the specified interface
17: to tell the local DHCP server to delete a particular lease.
18:
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.
22:
23: The address argument is a dotted-quad IP addresses and mandatory.
24:
25: The MAC address is colon separated hex, and is mandatory. It may be
26: prefixed by an address-type byte followed by -, eg
27:
28: 10-11:22:33:44:55:66
29:
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.
32:
33: The client-id is optional. If it is "*" then it treated as being missing.
34: */
35:
36: #include <sys/types.h>
37: #include <netinet/in.h>
38: #include <net/if.h>
39: #include <arpa/inet.h>
40: #include <sys/socket.h>
41: #include <unistd.h>
42: #include <stdio.h>
43: #include <string.h>
44: #include <stdlib.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>
50: #include <errno.h>
51:
52: #define DHCP_CHADDR_MAX 16
53: #define BOOTREQUEST 1
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
59: #define DHCPRELEASE 7
60: #define DHCP_SERVER_PORT 67
61:
62: typedef unsigned char u8;
63: typedef unsigned short u16;
64: typedef unsigned int u32;
65:
66: struct dhcp_packet {
67: u8 op, htype, hlen, hops;
68: u32 xid;
69: u16 secs, flags;
70: struct in_addr ciaddr, yiaddr, siaddr, giaddr;
71: u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
72: u32 cookie;
73: unsigned char options[308];
74: };
75:
76: static struct iovec iov;
77:
78: static int expand_buf(struct iovec *iov, size_t size)
79: {
80: void *new;
81:
82: if (size <= iov->iov_len)
83: return 1;
84:
85: if (!(new = malloc(size)))
86: {
87: errno = ENOMEM;
88: return 0;
89: }
90:
91: if (iov->iov_base)
92: {
93: memcpy(new, iov->iov_base, iov->iov_len);
94: free(iov->iov_base);
95: }
96:
97: iov->iov_base = new;
98: iov->iov_len = size;
99:
100: return 1;
101: }
102:
103: static ssize_t netlink_recv(int fd)
104: {
105: struct msghdr msg;
106: ssize_t rc;
107:
108: msg.msg_control = NULL;
109: msg.msg_controllen = 0;
110: msg.msg_name = NULL;
111: msg.msg_namelen = 0;
112: msg.msg_iov = &iov;
113: msg.msg_iovlen = 1;
114:
115: while (1)
116: {
117: msg.msg_flags = 0;
118: while ((rc = recvmsg(fd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
119:
1.1.1.2 ! misho 120: /* 2.2.x doesn't support MSG_PEEK at all, returning EOPNOTSUPP, so we just grab a
1.1 misho 121: big buffer and pray in that case. */
122: if (rc == -1 && errno == EOPNOTSUPP)
123: {
124: if (!expand_buf(&iov, 2000))
125: return -1;
126: break;
127: }
128:
129: if (rc == -1 || !(msg.msg_flags & MSG_TRUNC))
130: break;
131:
132: if (!expand_buf(&iov, iov.iov_len + 100))
133: return -1;
134: }
135:
136: /* finally, read it for real */
137: while ((rc = recvmsg(fd, &msg, 0)) == -1 && errno == EINTR);
138:
139: return rc;
140: }
141:
142: static int parse_hex(char *in, unsigned char *out, int maxlen, int *mac_type)
143: {
144: int i = 0;
145: char *r;
146:
147: if (mac_type)
148: *mac_type = 0;
149:
150: while (maxlen == -1 || i < maxlen)
151: {
152: for (r = in; *r != 0 && *r != ':' && *r != '-'; r++);
153: if (*r == 0)
154: maxlen = i;
155:
156: if (r != in )
157: {
158: if (*r == '-' && i == 0 && mac_type)
159: {
160: *r = 0;
161: *mac_type = strtol(in, NULL, 16);
162: mac_type = NULL;
163: }
164: else
165: {
166: *r = 0;
167: out[i] = strtol(in, NULL, 16);
168: i++;
169: }
170: }
171: in = r+1;
172: }
173: return i;
174: }
175:
176: static int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
177: {
178: return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
179: }
180:
1.1.1.2 ! misho 181: static struct in_addr find_interface(struct in_addr client, int fd, unsigned int index, int ifrfd, struct ifreq *ifr)
1.1 misho 182: {
183: struct sockaddr_nl addr;
184: struct nlmsghdr *h;
185: ssize_t len;
186:
187: struct {
188: struct nlmsghdr nlh;
189: struct rtgenmsg g;
190: } req;
191:
192: addr.nl_family = AF_NETLINK;
193: addr.nl_pad = 0;
194: addr.nl_groups = 0;
195: addr.nl_pid = 0; /* address to kernel */
196:
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;
203:
204: if (sendto(fd, (void *)&req, sizeof(req), 0,
205: (struct sockaddr *)&addr, sizeof(addr)) == -1)
206: {
207: perror("sendto failed");
208: exit(1);
209: }
210:
211: while (1)
212: {
213: if ((len = netlink_recv(fd)) == -1)
214: {
215: perror("netlink");
216: exit(1);
217: }
218:
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)
1.1.1.2 ! misho 221: {
! 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;
! 226: else
! 227: {
! 228: fprintf(stderr, "error: local IPv4 address not found\n");
! 229: exit(1);
! 230: }
! 231: }
1.1 misho 232: else if (h->nlmsg_type == RTM_NEWADDR)
233: {
234: struct ifaddrmsg *ifa = NLMSG_DATA(h);
235: struct rtattr *rta;
236: unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
237:
238: if (ifa->ifa_index == index && ifa->ifa_family == AF_INET)
239: {
240: struct in_addr netmask, addr;
241:
242: netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
243: addr.s_addr = 0;
244:
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));
248:
249: if (addr.s_addr && is_same_net(addr, client, netmask))
250: return addr;
251: }
252: }
253: }
254:
255: exit(0);
256: }
257:
258: int main(int argc, char **argv)
259: {
260: struct in_addr server, lease;
261: int mac_type;
262: struct dhcp_packet packet;
263: unsigned char *p = packet.options;
264: struct sockaddr_in dest;
265: struct ifreq ifr;
266: int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
267: int nl = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
268:
269: if (argc < 4 || argc > 5)
270: {
271: fprintf(stderr, "usage: dhcp_release <interface> <addr> <mac> [<client_id>]\n");
272: exit(1);
273: }
274:
275: if (fd == -1 || nl == -1)
276: {
277: perror("cannot create socket");
278: exit(1);
279: }
280:
281: /* This voodoo fakes up a packet coming from the correct interface, which really matters for
282: a DHCP server */
1.1.1.2 ! misho 283: strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
! 284: ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
1.1 misho 285: if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
286: {
287: perror("cannot setup interface");
288: exit(1);
289: }
290:
291: if (inet_addr(argv[2]) == INADDR_NONE)
292: {
293: perror("invalid ip address");
294: exit(1);
295: }
296:
297: lease.s_addr = inet_addr(argv[2]);
1.1.1.2 ! misho 298: server = find_interface(lease, nl, if_nametoindex(argv[1]), fd, &ifr);
1.1 misho 299:
300: memset(&packet, 0, sizeof(packet));
301:
302: packet.hlen = parse_hex(argv[3], packet.chaddr, DHCP_CHADDR_MAX, &mac_type);
303: if (mac_type == 0)
304: packet.htype = ARPHRD_ETHER;
305: else
306: packet.htype = mac_type;
307:
308: packet.op = BOOTREQUEST;
309: packet.ciaddr = lease;
310: packet.cookie = htonl(DHCP_COOKIE);
311:
312: *(p++) = OPTION_MESSAGE_TYPE;
313: *(p++) = 1;
314: *(p++) = DHCPRELEASE;
315:
316: *(p++) = OPTION_SERVER_IDENTIFIER;
317: *(p++) = sizeof(server);
318: memcpy(p, &server, sizeof(server));
319: p += sizeof(server);
320:
321: if (argc == 5 && strcmp(argv[4], "*") != 0)
322: {
323: unsigned int clid_len = parse_hex(argv[4], p+2, 255, NULL);
324: *(p++) = OPTION_CLIENT_ID;
325: *(p++) = clid_len;
326: p += clid_len;
327: }
328:
329: *(p++) = OPTION_END;
330:
331: dest.sin_family = AF_INET;
332: dest.sin_port = ntohs(DHCP_SERVER_PORT);
333: dest.sin_addr = server;
334:
335: if (sendto(fd, &packet, sizeof(packet), 0,
336: (struct sockaddr *)&dest, sizeof(dest)) == -1)
337: {
338: perror("sendto failed");
339: exit(1);
340: }
341:
342: return 0;
343: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>