Annotation of embedaddon/dnsmasq/src/bpf.c, revision 1.1

1.1     ! misho       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>