File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / contrib / wrt / dhcp_release.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 29 19:37:40 2013 UTC (10 years, 11 months ago) by misho
Branches: elwix, MAIN
CVS tags: v2_71, v2_66p0, v2_66, HEAD
dnsmasq

    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>