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 
iface_enumerate(void * parm,int (* ipv4_callback)(),int (* ipv6_callback)())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 
init_bpf(void)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 
send_via_bpf(struct dhcp_packet * mess,size_t len,struct in_addr iface_addr,struct ifreq * ifr)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