File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / src / bpf.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Mon Jul 29 19:37:40 2013 UTC (10 years, 11 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: /* dnsmasq is Copyright (c) 2000-2013 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, or
    6:    (at your option) version 3 dated 29 June, 2007.
    7:  
    8:    This program is distributed in the hope that it will be useful,
    9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11:    GNU General Public License for more details.
   12:      
   13:    You should have received a copy of the GNU General Public License
   14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   15: */
   16: 
   17: #include "dnsmasq.h"
   18: 
   19: #if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
   20: #include <ifaddrs.h>
   21: 
   22: #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
   23: #include <sys/param.h>
   24: #include <sys/sysctl.h>
   25: #include <net/route.h>
   26: #include <net/if_dl.h>
   27: #include <netinet/if_ether.h>
   28: 
   29: #ifndef SA_SIZE
   30: #define SA_SIZE(sa)                                             \
   31:     (  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?      \
   32:         sizeof(long)            :                               \
   33:         1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
   34: #endif
   35: 
   36: int arp_enumerate(void *parm, int (*callback)())
   37: {
   38:   int mib[6];
   39:   size_t needed;
   40:   char *next;
   41:   struct rt_msghdr *rtm;
   42:   struct sockaddr_inarp *sin2;
   43:   struct sockaddr_dl *sdl;
   44:   struct iovec buff;
   45:   int rc;
   46: 
   47:   buff.iov_base = NULL;
   48:   buff.iov_len = 0;
   49: 
   50:   mib[0] = CTL_NET;
   51:   mib[1] = PF_ROUTE;
   52:   mib[2] = 0;
   53:   mib[3] = AF_INET;
   54:   mib[4] = NET_RT_FLAGS;
   55: #ifdef RTF_LLINFO
   56:   mib[5] = RTF_LLINFO;
   57: #else
   58:   mib[5] = 0;
   59: #endif	
   60:   if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
   61:     return 0;
   62: 
   63:   while (1) 
   64:     {
   65:       if (!expand_buf(&buff, needed))
   66: 	return 0;
   67:       if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
   68: 	  errno != ENOMEM)
   69: 	break;
   70:       needed += needed / 8;
   71:     }
   72:   if (rc == -1)
   73:     return 0;
   74:   
   75:   for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
   76:     {
   77:       rtm = (struct rt_msghdr *)next;
   78:       sin2 = (struct sockaddr_inarp *)(rtm + 1);
   79:       sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
   80:       if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
   81: 	return 0;
   82:     }
   83: 
   84:   return 1;
   85: }
   86: #endif
   87: 
   88: 
   89: int iface_enumerate(int family, void *parm, int (*callback)())
   90: {
   91:   struct ifaddrs *head, *addrs;
   92:   int errsav, ret = 0;
   93: 
   94:   if (family == AF_UNSPEC)
   95: #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
   96:     return  arp_enumerate(parm, callback);
   97: #else
   98:   return 0; /* need code for Solaris and MacOS*/
   99: #endif
  100: 
  101:   /* AF_LINK doesn't exist in Linux, so we can't use it in our API */
  102:   if (family == AF_LOCAL)
  103:     family = AF_LINK;
  104: 
  105:   if (getifaddrs(&head) == -1)
  106:     return 0;
  107: 
  108:   for (addrs = head; addrs; addrs = addrs->ifa_next)
  109:     {
  110:       if (addrs->ifa_addr->sa_family == family)
  111: 	{
  112: 	  int iface_index = if_nametoindex(addrs->ifa_name);
  113: 
  114: 	  if (iface_index == 0 || !addrs->ifa_addr || !addrs->ifa_netmask)
  115: 	    continue;
  116: 
  117: 	  if (family == AF_INET)
  118: 	    {
  119: 	      struct in_addr addr, netmask, broadcast;
  120: 	      addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
  121: 	      netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
  122: 	      if (addrs->ifa_broadaddr)
  123: 		broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr; 
  124: 	      else 
  125: 		broadcast.s_addr = 0;	      
  126: 	      if (!((*callback)(addr, iface_index, netmask, broadcast, parm)))
  127: 		goto err;
  128: 	    }
  129: #ifdef HAVE_IPV6
  130: 	  else if (family == AF_INET6)
  131: 	    {
  132: 	      struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
  133: 	      unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
  134: 	      int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
  135: 	      int i, j, prefix = 0;
  136: 	      
  137: 	      for (i = 0; i < IN6ADDRSZ; i++, prefix += 8) 
  138:                 if (netmask[i] != 0xff)
  139: 		  break;
  140:        
  141: 	      if (i != IN6ADDRSZ && netmask[i]) 
  142:                 for (j = 7; j > 0; j--, prefix++) 
  143: 		  if ((netmask[i] & (1 << j)) == 0)
  144: 		    break;
  145: 	      
  146: 	      /* voodoo to clear interface field in address */
  147: 	      if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
  148: 		{
  149: 		  addr->s6_addr[2] = 0;
  150: 		  addr->s6_addr[3] = 0;
  151: 		}
  152: 	      
  153: 	      /* preferred and valid times == forever until we known how to dtermine them. */
  154: 	      if (!((*callback)(addr, prefix, scope_id, iface_index, 0, -1, -1, parm)))
  155: 		goto err;
  156: 	}
  157: #endif
  158: #ifdef HAVE_DHCP6      
  159: 	  else if (family == AF_LINK)
  160: 	    { 
  161: 	      /* Assume ethernet again here */
  162: 	      struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
  163: 	      if (sdl->sdl_alen != 0 && 
  164: 		  !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
  165: 		goto err;
  166: 	    }
  167: #endif 
  168: 	}
  169:     }
  170:   
  171:   ret = 1;
  172: 
  173:  err:
  174:   errsav = errno;
  175:   freeifaddrs(head);  
  176:   errno = errsav;
  177: 
  178:   return ret;
  179: }
  180: #endif
  181: 
  182: 
  183: #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
  184: #include <net/bpf.h>
  185: 
  186: void init_bpf(void)
  187: {
  188:   int i = 0;
  189: 
  190:   while (1) 
  191:     {
  192:       sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
  193:       if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
  194: 	return;
  195: 
  196:       if (errno != EBUSY)
  197: 	die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
  198:     }	     
  199: }
  200: 
  201: void send_via_bpf(struct dhcp_packet *mess, size_t len,
  202: 		  struct in_addr iface_addr, struct ifreq *ifr)
  203: {
  204:    /* Hairy stuff, packet either has to go to the
  205:       net broadcast or the destination can't reply to ARP yet,
  206:       but we do know the physical address. 
  207:       Build the packet by steam, and send directly, bypassing
  208:       the kernel IP stack */
  209:   
  210:   struct ether_header ether; 
  211:   struct ip ip;
  212:   struct udphdr {
  213:     u16 uh_sport;               /* source port */
  214:     u16 uh_dport;               /* destination port */
  215:     u16 uh_ulen;                /* udp length */
  216:     u16 uh_sum;                 /* udp checksum */
  217:   } udp;
  218:   
  219:   u32 i, sum;
  220:   struct iovec iov[4];
  221: 
  222:   /* Only know how to do ethernet on *BSD */
  223:   if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
  224:     {
  225:       my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"), 
  226: 		mess->htype, ifr->ifr_name);
  227:       return;
  228:     }
  229:    
  230:   ifr->ifr_addr.sa_family = AF_LINK;
  231:   if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
  232:     return;
  233:   
  234:   memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
  235:   ether.ether_type = htons(ETHERTYPE_IP);
  236:   
  237:   if (ntohs(mess->flags) & 0x8000)
  238:     {
  239:       memset(ether.ether_dhost, 255,  ETHER_ADDR_LEN);
  240:       ip.ip_dst.s_addr = INADDR_BROADCAST;
  241:     }
  242:   else
  243:     {
  244:       memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN); 
  245:       ip.ip_dst.s_addr = mess->yiaddr.s_addr;
  246:     }
  247:   
  248:   ip.ip_p = IPPROTO_UDP;
  249:   ip.ip_src.s_addr = iface_addr.s_addr;
  250:   ip.ip_len = htons(sizeof(struct ip) + 
  251: 		    sizeof(struct udphdr) +
  252: 		    len) ;
  253:   ip.ip_hl = sizeof(struct ip) / 4;
  254:   ip.ip_v = IPVERSION;
  255:   ip.ip_tos = 0;
  256:   ip.ip_id = htons(0);
  257:   ip.ip_off = htons(0x4000); /* don't fragment */
  258:   ip.ip_ttl = IPDEFTTL;
  259:   ip.ip_sum = 0;
  260:   for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
  261:     sum += ((u16 *)&ip)[i];
  262:   while (sum>>16)
  263:     sum = (sum & 0xffff) + (sum >> 16);  
  264:   ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
  265:   
  266:   udp.uh_sport = htons(daemon->dhcp_server_port);
  267:   udp.uh_dport = htons(daemon->dhcp_client_port);
  268:   if (len & 1)
  269:     ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
  270:   udp.uh_sum = 0;
  271:   udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
  272:   sum += htons(IPPROTO_UDP);
  273:   sum += ip.ip_src.s_addr & 0xffff;
  274:   sum += (ip.ip_src.s_addr >> 16) & 0xffff;
  275:   sum += ip.ip_dst.s_addr & 0xffff;
  276:   sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
  277:   for (i = 0; i < sizeof(struct udphdr)/2; i++)
  278:     sum += ((u16 *)&udp)[i];
  279:   for (i = 0; i < (len + 1) / 2; i++)
  280:     sum += ((u16 *)mess)[i];
  281:   while (sum>>16)
  282:     sum = (sum & 0xffff) + (sum >> 16);
  283:   udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
  284:   
  285:   ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
  286:   
  287:   iov[0].iov_base = &ether;
  288:   iov[0].iov_len = sizeof(ether);
  289:   iov[1].iov_base = &ip;
  290:   iov[1].iov_len = sizeof(ip);
  291:   iov[2].iov_base = &udp;
  292:   iov[2].iov_len = sizeof(udp);
  293:   iov[3].iov_base = mess;
  294:   iov[3].iov_len = len;
  295: 
  296:   while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
  297: }
  298: 
  299: #endif
  300: 
  301: 

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