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>