Home | History | Annotate | Download | only in src
      1 /* dnsmasq is Copyright (c) 2000-2009 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 
     21 static struct iovec ifconf = {
     22   .iov_base = NULL,
     23   .iov_len = 0
     24 };
     25 
     26 static struct iovec ifreq = {
     27   .iov_base = NULL,
     28   .iov_len = 0
     29 };
     30 
     31 int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
     32 {
     33   char *ptr;
     34   struct ifreq *ifr;
     35   struct ifconf ifc;
     36   int fd, errsav, ret = 0;
     37   int lastlen = 0;
     38   size_t len = 0;
     39 
     40   if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
     41     return 0;
     42 
     43   while(1)
     44     {
     45       len += 10*sizeof(struct ifreq);
     46 
     47       if (!expand_buf(&ifconf, len))
     48 	goto err;
     49 
     50       ifc.ifc_len = len;
     51       ifc.ifc_buf = ifconf.iov_base;
     52 
     53       if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
     54 	{
     55 	  if (errno != EINVAL || lastlen != 0)
     56 	    goto err;
     57 	}
     58       else
     59 	{
     60 	  if (ifc.ifc_len == lastlen)
     61 	    break; /* got a big enough buffer now */
     62 	  lastlen = ifc.ifc_len;
     63 	}
     64     }
     65 
     66   for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
     67     {
     68       /* subsequent entries may not be aligned, so copy into
     69 	 an aligned buffer to avoid nasty complaints about
     70 	 unaligned accesses. */
     71 
     72       len = sizeof(struct ifreq);
     73 
     74 #ifdef HAVE_SOCKADDR_SA_LEN
     75       ifr = (struct ifreq *)ptr;
     76       if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
     77 	len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
     78 #endif
     79 
     80       if (!expand_buf(&ifreq, len))
     81 	goto err;
     82 
     83       ifr = (struct ifreq *)ifreq.iov_base;
     84       memcpy(ifr, ptr, len);
     85 
     86       if (ifr->ifr_addr.sa_family == AF_INET && ipv4_callback)
     87 	{
     88 	  struct in_addr addr, netmask, broadcast;
     89 	  broadcast.s_addr = 0;
     90 	  addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
     91 	  if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
     92 	    continue;
     93 	  netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
     94 	  if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
     95 	    broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
     96 	  if (!((*ipv4_callback)(addr,
     97 				 (int)if_nametoindex(ifr->ifr_name),
     98 				 netmask, broadcast,
     99 				 parm)))
    100 	    goto err;
    101 	}
    102 #ifdef HAVE_IPV6
    103       else if (ifr->ifr_addr.sa_family == AF_INET6 && ipv6_callback)
    104 	{
    105 	  struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
    106 	  /* voodoo to clear interface field in address */
    107 	  if (!(daemon->options & OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
    108 	    {
    109 	      addr->s6_addr[2] = 0;
    110 	      addr->s6_addr[3] = 0;
    111 	    }
    112 	  if (!((*ipv6_callback)(addr,
    113 				 (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
    114 				 (int)if_nametoindex(ifr->ifr_name),
    115 				 parm)))
    116 	    goto err;
    117 	}
    118 #endif
    119     }
    120 
    121   ret = 1;
    122 
    123  err:
    124   errsav = errno;
    125   close(fd);
    126   errno = errsav;
    127 
    128   return ret;
    129 }
    130 #endif
    131 
    132 
    133 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
    134 #include <net/bpf.h>
    135 
    136 void init_bpf(void)
    137 {
    138   int i = 0;
    139 
    140   while (1)
    141     {
    142       /* useful size which happens to be sufficient */
    143       if (expand_buf(&ifreq, sizeof(struct ifreq)))
    144 	{
    145 	  sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
    146 	  if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
    147 	    return;
    148 	}
    149       if (errno != EBUSY)
    150 	die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
    151     }
    152 }
    153 
    154 void send_via_bpf(struct dhcp_packet *mess, size_t len,
    155 		  struct in_addr iface_addr, struct ifreq *ifr)
    156 {
    157    /* Hairy stuff, packet either has to go to the
    158       net broadcast or the destination can't reply to ARP yet,
    159       but we do know the physical address.
    160       Build the packet by steam, and send directly, bypassing
    161       the kernel IP stack */
    162 
    163   struct ether_header ether;
    164   struct ip ip;
    165   struct udphdr {
    166     u16 uh_sport;               /* source port */
    167     u16 uh_dport;               /* destination port */
    168     u16 uh_ulen;                /* udp length */
    169     u16 uh_sum;                 /* udp checksum */
    170   } udp;
    171 
    172   u32 i, sum;
    173   struct iovec iov[4];
    174 
    175   /* Only know how to do ethernet on *BSD */
    176   if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
    177     {
    178       my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
    179 		mess->htype, ifr->ifr_name);
    180       return;
    181     }
    182 
    183   ifr->ifr_addr.sa_family = AF_LINK;
    184   if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
    185     return;
    186 
    187   memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
    188   ether.ether_type = htons(ETHERTYPE_IP);
    189 
    190   if (ntohs(mess->flags) & 0x8000)
    191     {
    192       memset(ether.ether_dhost, 255,  ETHER_ADDR_LEN);
    193       ip.ip_dst.s_addr = INADDR_BROADCAST;
    194     }
    195   else
    196     {
    197       memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
    198       ip.ip_dst.s_addr = mess->yiaddr.s_addr;
    199     }
    200 
    201   ip.ip_p = IPPROTO_UDP;
    202   ip.ip_src.s_addr = iface_addr.s_addr;
    203   ip.ip_len = htons(sizeof(struct ip) +
    204 		    sizeof(struct udphdr) +
    205 		    len) ;
    206   ip.ip_hl = sizeof(struct ip) / 4;
    207   ip.ip_v = IPVERSION;
    208   ip.ip_tos = 0;
    209   ip.ip_id = htons(0);
    210   ip.ip_off = htons(0x4000); /* don't fragment */
    211   ip.ip_ttl = IPDEFTTL;
    212   ip.ip_sum = 0;
    213   for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
    214     sum += ((u16 *)&ip)[i];
    215   while (sum>>16)
    216     sum = (sum & 0xffff) + (sum >> 16);
    217   ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
    218 
    219   udp.uh_sport = htons(daemon->dhcp_server_port);
    220   udp.uh_dport = htons(daemon->dhcp_client_port);
    221   if (len & 1)
    222     ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
    223   udp.uh_sum = 0;
    224   udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
    225   sum += htons(IPPROTO_UDP);
    226   sum += ip.ip_src.s_addr & 0xffff;
    227   sum += (ip.ip_src.s_addr >> 16) & 0xffff;
    228   sum += ip.ip_dst.s_addr & 0xffff;
    229   sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
    230   for (i = 0; i < sizeof(struct udphdr)/2; i++)
    231     sum += ((u16 *)&udp)[i];
    232   for (i = 0; i < (len + 1) / 2; i++)
    233     sum += ((u16 *)mess)[i];
    234   while (sum>>16)
    235     sum = (sum & 0xffff) + (sum >> 16);
    236   udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
    237 
    238   ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
    239 
    240   iov[0].iov_base = &ether;
    241   iov[0].iov_len = sizeof(ether);
    242   iov[1].iov_base = &ip;
    243   iov[1].iov_len = sizeof(ip);
    244   iov[2].iov_base = &udp;
    245   iov[2].iov_len = sizeof(udp);
    246   iov[3].iov_base = mess;
    247   iov[3].iov_len = len;
    248 
    249   while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
    250 }
    251 
    252 #endif
    253 
    254 
    255