Annotation of embedaddon/dnsmasq/contrib/lease-tools/dhcp_release.c, revision 1.1
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:
! 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)
! 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:
! 181: static struct in_addr find_interface(struct in_addr client, int fd, unsigned int index)
! 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)
! 221: exit(0);
! 222: else if (h->nlmsg_type == RTM_NEWADDR)
! 223: {
! 224: struct ifaddrmsg *ifa = NLMSG_DATA(h);
! 225: struct rtattr *rta;
! 226: unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
! 227:
! 228: if (ifa->ifa_index == index && ifa->ifa_family == AF_INET)
! 229: {
! 230: struct in_addr netmask, addr;
! 231:
! 232: netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
! 233: addr.s_addr = 0;
! 234:
! 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));
! 238:
! 239: if (addr.s_addr && is_same_net(addr, client, netmask))
! 240: return addr;
! 241: }
! 242: }
! 243: }
! 244:
! 245: exit(0);
! 246: }
! 247:
! 248: int main(int argc, char **argv)
! 249: {
! 250: struct in_addr server, lease;
! 251: int mac_type;
! 252: struct dhcp_packet packet;
! 253: unsigned char *p = packet.options;
! 254: struct sockaddr_in dest;
! 255: struct ifreq ifr;
! 256: int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
! 257: int nl = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
! 258:
! 259: if (argc < 4 || argc > 5)
! 260: {
! 261: fprintf(stderr, "usage: dhcp_release <interface> <addr> <mac> [<client_id>]\n");
! 262: exit(1);
! 263: }
! 264:
! 265: if (fd == -1 || nl == -1)
! 266: {
! 267: perror("cannot create socket");
! 268: exit(1);
! 269: }
! 270:
! 271: /* This voodoo fakes up a packet coming from the correct interface, which really matters for
! 272: a DHCP server */
! 273: strcpy(ifr.ifr_name, argv[1]);
! 274: if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
! 275: {
! 276: perror("cannot setup interface");
! 277: exit(1);
! 278: }
! 279:
! 280: if (inet_addr(argv[2]) == INADDR_NONE)
! 281: {
! 282: perror("invalid ip address");
! 283: exit(1);
! 284: }
! 285:
! 286: lease.s_addr = inet_addr(argv[2]);
! 287: server = find_interface(lease, nl, if_nametoindex(argv[1]));
! 288:
! 289: memset(&packet, 0, sizeof(packet));
! 290:
! 291: packet.hlen = parse_hex(argv[3], packet.chaddr, DHCP_CHADDR_MAX, &mac_type);
! 292: if (mac_type == 0)
! 293: packet.htype = ARPHRD_ETHER;
! 294: else
! 295: packet.htype = mac_type;
! 296:
! 297: packet.op = BOOTREQUEST;
! 298: packet.ciaddr = lease;
! 299: packet.cookie = htonl(DHCP_COOKIE);
! 300:
! 301: *(p++) = OPTION_MESSAGE_TYPE;
! 302: *(p++) = 1;
! 303: *(p++) = DHCPRELEASE;
! 304:
! 305: *(p++) = OPTION_SERVER_IDENTIFIER;
! 306: *(p++) = sizeof(server);
! 307: memcpy(p, &server, sizeof(server));
! 308: p += sizeof(server);
! 309:
! 310: if (argc == 5 && strcmp(argv[4], "*") != 0)
! 311: {
! 312: unsigned int clid_len = parse_hex(argv[4], p+2, 255, NULL);
! 313: *(p++) = OPTION_CLIENT_ID;
! 314: *(p++) = clid_len;
! 315: p += clid_len;
! 316: }
! 317:
! 318: *(p++) = OPTION_END;
! 319:
! 320: dest.sin_family = AF_INET;
! 321: dest.sin_port = ntohs(DHCP_SERVER_PORT);
! 322: dest.sin_addr = server;
! 323:
! 324: if (sendto(fd, &packet, sizeof(packet), 0,
! 325: (struct sockaddr *)&dest, sizeof(dest)) == -1)
! 326: {
! 327: perror("sendto failed");
! 328: exit(1);
! 329: }
! 330:
! 331: return 0;
! 332: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>