Source code upload
[framework/connectivity/dnsmasq.git] / src / bpf.c
1 /* dnsmasq is Copyright (c) 2000-2011 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 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
32
33 #include <sys/sysctl.h>
34 #include <net/route.h>
35 #include <net/if_dl.h>
36 #include <netinet/if_ether.h>
37
38 int arp_enumerate(void *parm, int (*callback)())
39 {
40   int mib[6];
41   size_t needed;
42   char *next;
43   struct rt_msghdr *rtm;
44   struct sockaddr_inarp *sin2;
45   struct sockaddr_dl *sdl;
46   int rc;
47   
48   mib[0] = CTL_NET;
49   mib[1] = PF_ROUTE;
50   mib[2] = 0;
51   mib[3] = AF_INET;
52   mib[4] = NET_RT_FLAGS;
53 #ifdef RTF_LLINFO
54   mib[5] = RTF_LLINFO;
55 #else
56   mib[5] = 0;
57 #endif  
58   if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
59     return 0;
60
61   while (1) 
62     {
63       if (!expand_buf(&ifconf, needed))
64         return 0;
65       if ((rc = sysctl(mib, 6, ifconf.iov_base, &needed, NULL, 0)) == 0 ||
66           errno != ENOMEM)
67         break;
68       needed += needed / 8;
69     }
70   if (rc == -1)
71     return 0;
72   
73   for (next = ifconf.iov_base ; next < (char *)ifconf.iov_base + needed; next += rtm->rtm_msglen)
74     {
75       rtm = (struct rt_msghdr *)next;
76       sin2 = (struct sockaddr_inarp *)(rtm + 1);
77       sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
78       if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
79         return 0;
80     }
81
82   return 1;
83 }
84
85 #endif
86
87
88 int iface_enumerate(int family, void *parm, int (*callback)())
89 {
90   char *ptr;
91   struct ifreq *ifr;
92   struct ifconf ifc;
93   int fd, errsav, ret = 0;
94   int lastlen = 0;
95   size_t len = 0;
96   
97   if (family == AF_UNSPEC)
98 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
99     return  arp_enumerate(parm, callback);
100 #else
101   return 0; /* need code for Solaris and MacOS*/
102 #endif
103
104   if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
105     return 0;
106   
107   while(1)
108     {
109       len += 10*sizeof(struct ifreq);
110       
111       if (!expand_buf(&ifconf, len))
112         goto err;
113       
114       ifc.ifc_len = len;
115       ifc.ifc_buf = ifconf.iov_base;
116       
117       if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
118         {
119           if (errno != EINVAL || lastlen != 0)
120             goto err;
121         }
122       else
123         {
124           if (ifc.ifc_len == lastlen)
125             break; /* got a big enough buffer now */
126           lastlen = ifc.ifc_len;
127         }
128     }
129   
130   for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
131     {
132       /* subsequent entries may not be aligned, so copy into
133          an aligned buffer to avoid nasty complaints about 
134          unaligned accesses. */
135
136       len = sizeof(struct ifreq);
137       
138 #ifdef HAVE_SOCKADDR_SA_LEN
139       ifr = (struct ifreq *)ptr;
140       if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
141         len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
142 #endif
143       
144       if (!expand_buf(&ifreq, len))
145         goto err;
146
147       ifr = (struct ifreq *)ifreq.iov_base;
148       memcpy(ifr, ptr, len);
149            
150       if (ifr->ifr_addr.sa_family == family)
151         {
152           if (family == AF_INET)
153             {
154               struct in_addr addr, netmask, broadcast;
155               broadcast.s_addr = 0;
156               addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
157               if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
158                 continue;
159               netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
160               if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
161                 broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr; 
162               if (!((*callback)(addr, 
163                                 (int)if_nametoindex(ifr->ifr_name),
164                                 netmask, broadcast, 
165                                 parm)))
166                 goto err;
167             }
168 #ifdef HAVE_IPV6
169           else if (family == AF_INET6)
170             {
171               struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
172               /* voodoo to clear interface field in address */
173               if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
174                 {
175                   addr->s6_addr[2] = 0;
176                   addr->s6_addr[3] = 0;
177                 }
178               if (!((*callback)(addr,
179                                 (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
180                                 (int)if_nametoindex(ifr->ifr_name),
181                                 parm)))
182                 goto err;
183             }
184 #endif
185         }
186     }
187   
188   ret = 1;
189
190  err:
191   errsav = errno;
192   close(fd);  
193   errno = errsav;
194
195   return ret;
196 }
197 #endif
198
199
200 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
201 #include <net/bpf.h>
202
203 void init_bpf(void)
204 {
205   int i = 0;
206
207   while (1) 
208     {
209       /* useful size which happens to be sufficient */
210       if (expand_buf(&ifreq, sizeof(struct ifreq)))
211         {
212           sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
213           if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
214             return;
215         }
216       if (errno != EBUSY)
217         die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
218     }        
219 }
220
221 void send_via_bpf(struct dhcp_packet *mess, size_t len,
222                   struct in_addr iface_addr, struct ifreq *ifr)
223 {
224    /* Hairy stuff, packet either has to go to the
225       net broadcast or the destination can't reply to ARP yet,
226       but we do know the physical address. 
227       Build the packet by steam, and send directly, bypassing
228       the kernel IP stack */
229   
230   struct ether_header ether; 
231   struct ip ip;
232   struct udphdr {
233     u16 uh_sport;               /* source port */
234     u16 uh_dport;               /* destination port */
235     u16 uh_ulen;                /* udp length */
236     u16 uh_sum;                 /* udp checksum */
237   } udp;
238   
239   u32 i, sum;
240   struct iovec iov[4];
241
242   /* Only know how to do ethernet on *BSD */
243   if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
244     {
245       my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"), 
246                 mess->htype, ifr->ifr_name);
247       return;
248     }
249    
250   ifr->ifr_addr.sa_family = AF_LINK;
251   if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
252     return;
253   
254   memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
255   ether.ether_type = htons(ETHERTYPE_IP);
256   
257   if (ntohs(mess->flags) & 0x8000)
258     {
259       memset(ether.ether_dhost, 255,  ETHER_ADDR_LEN);
260       ip.ip_dst.s_addr = INADDR_BROADCAST;
261     }
262   else
263     {
264       memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN); 
265       ip.ip_dst.s_addr = mess->yiaddr.s_addr;
266     }
267   
268   ip.ip_p = IPPROTO_UDP;
269   ip.ip_src.s_addr = iface_addr.s_addr;
270   ip.ip_len = htons(sizeof(struct ip) + 
271                     sizeof(struct udphdr) +
272                     len) ;
273   ip.ip_hl = sizeof(struct ip) / 4;
274   ip.ip_v = IPVERSION;
275   ip.ip_tos = 0;
276   ip.ip_id = htons(0);
277   ip.ip_off = htons(0x4000); /* don't fragment */
278   ip.ip_ttl = IPDEFTTL;
279   ip.ip_sum = 0;
280   for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
281     sum += ((u16 *)&ip)[i];
282   while (sum>>16)
283     sum = (sum & 0xffff) + (sum >> 16);  
284   ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
285   
286   udp.uh_sport = htons(daemon->dhcp_server_port);
287   udp.uh_dport = htons(daemon->dhcp_client_port);
288   if (len & 1)
289     ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
290   udp.uh_sum = 0;
291   udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
292   sum += htons(IPPROTO_UDP);
293   sum += ip.ip_src.s_addr & 0xffff;
294   sum += (ip.ip_src.s_addr >> 16) & 0xffff;
295   sum += ip.ip_dst.s_addr & 0xffff;
296   sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
297   for (i = 0; i < sizeof(struct udphdr)/2; i++)
298     sum += ((u16 *)&udp)[i];
299   for (i = 0; i < (len + 1) / 2; i++)
300     sum += ((u16 *)mess)[i];
301   while (sum>>16)
302     sum = (sum & 0xffff) + (sum >> 16);
303   udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
304   
305   ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
306   
307   iov[0].iov_base = &ether;
308   iov[0].iov_len = sizeof(ether);
309   iov[1].iov_base = &ip;
310   iov[1].iov_len = sizeof(ip);
311   iov[2].iov_base = &udp;
312   iov[2].iov_len = sizeof(udp);
313   iov[3].iov_base = mess;
314   iov[3].iov_len = len;
315
316   while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
317 }
318
319 #endif
320
321