Annotation of embedaddon/dnsmasq/contrib/wrt/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:   
        !           281:   lease.s_addr = inet_addr(argv[2]);
        !           282:   server = find_interface(lease, nl, if_nametoindex(argv[1]));
        !           283:   
        !           284:   memset(&packet, 0, sizeof(packet));
        !           285:  
        !           286:   packet.hlen = parse_hex(argv[3], packet.chaddr, DHCP_CHADDR_MAX, &mac_type);
        !           287:   if (mac_type == 0)
        !           288:     packet.htype = ARPHRD_ETHER;
        !           289:   else
        !           290:     packet.htype = mac_type;
        !           291: 
        !           292:   packet.op = BOOTREQUEST;
        !           293:   packet.ciaddr = lease;
        !           294:   packet.cookie = htonl(DHCP_COOKIE);
        !           295: 
        !           296:   *(p++) = OPTION_MESSAGE_TYPE;
        !           297:   *(p++) = 1;
        !           298:   *(p++) = DHCPRELEASE;
        !           299: 
        !           300:   *(p++) = OPTION_SERVER_IDENTIFIER;
        !           301:   *(p++) = sizeof(server);
        !           302:   memcpy(p, &server, sizeof(server));
        !           303:   p += sizeof(server);
        !           304: 
        !           305:   if (argc == 5 && strcmp(argv[4], "*") != 0)
        !           306:     {
        !           307:       unsigned int clid_len = parse_hex(argv[4], p+2, 255, NULL);
        !           308:       *(p++) = OPTION_CLIENT_ID;
        !           309:       *(p++) = clid_len;
        !           310:       p += clid_len;
        !           311:     }
        !           312:   
        !           313:   *(p++) = OPTION_END;
        !           314:  
        !           315:   dest.sin_family = AF_INET;
        !           316:   dest.sin_port = ntohs(DHCP_SERVER_PORT);
        !           317:   dest.sin_addr = server;
        !           318: 
        !           319:   if (sendto(fd, &packet, sizeof(packet), 0, 
        !           320:             (struct sockaddr *)&dest, sizeof(dest)) == -1)
        !           321:     {
        !           322:       perror("sendto failed");
        !           323:       exit(1);
        !           324:     }
        !           325: 
        !           326:   return 0;
        !           327: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>