dnsmasq 2.72+ up to December 9 2014
[tomato.git] / release / src / router / dnsmasq / src / bpf.c
blob4416b1c0728746f47f63ed893b7e7c36496ad147
1 /* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
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.
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.
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/>.
17 #include "dnsmasq.h"
19 #if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
20 #include <ifaddrs.h>
22 #include <sys/param.h>
23 #include <sys/sysctl.h>
24 #include <net/if.h>
25 #include <net/route.h>
26 #include <net/if_dl.h>
27 #include <netinet/if_ether.h>
28 #if defined(__FreeBSD__)
29 # include <net/if_var.h>
30 #endif
31 #include <netinet/in_var.h>
32 #ifdef HAVE_IPV6
33 # include <netinet6/in6_var.h>
34 #endif
36 #ifndef SA_SIZE
37 #define SA_SIZE(sa) \
38 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
39 sizeof(long) : \
40 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
41 #endif
43 #ifdef HAVE_BSD_NETWORK
44 static int del_family = 0;
45 static struct all_addr del_addr;
46 #endif
48 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
50 int arp_enumerate(void *parm, int (*callback)())
52 int mib[6];
53 size_t needed;
54 char *next;
55 struct rt_msghdr *rtm;
56 struct sockaddr_inarp *sin2;
57 struct sockaddr_dl *sdl;
58 struct iovec buff;
59 int rc;
61 buff.iov_base = NULL;
62 buff.iov_len = 0;
64 mib[0] = CTL_NET;
65 mib[1] = PF_ROUTE;
66 mib[2] = 0;
67 mib[3] = AF_INET;
68 mib[4] = NET_RT_FLAGS;
69 #ifdef RTF_LLINFO
70 mib[5] = RTF_LLINFO;
71 #else
72 mib[5] = 0;
73 #endif
74 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
75 return 0;
77 while (1)
79 if (!expand_buf(&buff, needed))
80 return 0;
81 if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
82 errno != ENOMEM)
83 break;
84 needed += needed / 8;
86 if (rc == -1)
87 return 0;
89 for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
91 rtm = (struct rt_msghdr *)next;
92 sin2 = (struct sockaddr_inarp *)(rtm + 1);
93 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
94 if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
95 return 0;
98 return 1;
100 #endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
103 int iface_enumerate(int family, void *parm, int (*callback)())
105 struct ifaddrs *head, *addrs;
106 int errsav, fd = -1, ret = 0;
108 if (family == AF_UNSPEC)
109 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
110 return arp_enumerate(parm, callback);
111 #else
112 return 0; /* need code for Solaris and MacOS*/
113 #endif
115 /* AF_LINK doesn't exist in Linux, so we can't use it in our API */
116 if (family == AF_LOCAL)
117 family = AF_LINK;
119 if (getifaddrs(&head) == -1)
120 return 0;
122 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_IPV6)
123 if (family == AF_INET6)
124 fd = socket(PF_INET6, SOCK_DGRAM, 0);
125 #endif
127 for (addrs = head; addrs; addrs = addrs->ifa_next)
129 if (addrs->ifa_addr->sa_family == family)
131 int iface_index = if_nametoindex(addrs->ifa_name);
133 if (iface_index == 0 || !addrs->ifa_addr ||
134 (!addrs->ifa_netmask && family != AF_LINK))
135 continue;
137 if (family == AF_INET)
139 struct in_addr addr, netmask, broadcast;
140 addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
141 #ifdef HAVE_BSD_NETWORK
142 if (del_family == AF_INET && del_addr.addr.addr4.s_addr == addr.s_addr)
143 continue;
144 #endif
145 netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
146 if (addrs->ifa_broadaddr)
147 broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
148 else
149 broadcast.s_addr = 0;
150 if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm)))
151 goto err;
153 #ifdef HAVE_IPV6
154 else if (family == AF_INET6)
156 struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
157 unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
158 int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
159 int i, j, prefix = 0;
160 u32 valid = 0xffffffff, preferred = 0xffffffff;
161 int flags = 0;
162 #ifdef HAVE_BSD_NETWORK
163 if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr.addr6, addr))
164 continue;
165 #endif
166 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
167 struct in6_ifreq ifr6;
169 memset(&ifr6, 0, sizeof(ifr6));
170 strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
172 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
173 if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
175 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
176 flags |= IFACE_TENTATIVE;
178 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
179 flags |= IFACE_DEPRECATED;
181 #ifdef IN6_IFF_TEMPORARY
182 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
183 flags |= IFACE_PERMANENT;
184 #endif
186 #ifdef IN6_IFF_PRIVACY
187 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
188 flags |= IFACE_PERMANENT;
189 #endif
192 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
193 if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
195 valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
196 preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
198 #endif
200 for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
201 if (netmask[i] != 0xff)
202 break;
204 if (i != IN6ADDRSZ && netmask[i])
205 for (j = 7; j > 0; j--, prefix++)
206 if ((netmask[i] & (1 << j)) == 0)
207 break;
209 /* voodoo to clear interface field in address */
210 if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
212 addr->s6_addr[2] = 0;
213 addr->s6_addr[3] = 0;
216 if (!((*callback)(addr, prefix, scope_id, iface_index, flags,
217 (int) preferred, (int)valid, parm)))
218 goto err;
220 #endif /* HAVE_IPV6 */
222 #ifdef HAVE_DHCP6
223 else if (family == AF_LINK)
225 /* Assume ethernet again here */
226 struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
227 if (sdl->sdl_alen != 0 &&
228 !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
229 goto err;
231 #endif
235 ret = 1;
237 err:
238 errsav = errno;
239 freeifaddrs(head);
240 if (fd != -1)
241 close(fd);
242 errno = errsav;
244 return ret;
246 #endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */
249 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
250 #include <net/bpf.h>
252 void init_bpf(void)
254 int i = 0;
256 while (1)
258 sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
259 if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
260 return;
262 if (errno != EBUSY)
263 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
267 void send_via_bpf(struct dhcp_packet *mess, size_t len,
268 struct in_addr iface_addr, struct ifreq *ifr)
270 /* Hairy stuff, packet either has to go to the
271 net broadcast or the destination can't reply to ARP yet,
272 but we do know the physical address.
273 Build the packet by steam, and send directly, bypassing
274 the kernel IP stack */
276 struct ether_header ether;
277 struct ip ip;
278 struct udphdr {
279 u16 uh_sport; /* source port */
280 u16 uh_dport; /* destination port */
281 u16 uh_ulen; /* udp length */
282 u16 uh_sum; /* udp checksum */
283 } udp;
285 u32 i, sum;
286 struct iovec iov[4];
288 /* Only know how to do ethernet on *BSD */
289 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
291 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
292 mess->htype, ifr->ifr_name);
293 return;
296 ifr->ifr_addr.sa_family = AF_LINK;
297 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
298 return;
300 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
301 ether.ether_type = htons(ETHERTYPE_IP);
303 if (ntohs(mess->flags) & 0x8000)
305 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
306 ip.ip_dst.s_addr = INADDR_BROADCAST;
308 else
310 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
311 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
314 ip.ip_p = IPPROTO_UDP;
315 ip.ip_src.s_addr = iface_addr.s_addr;
316 ip.ip_len = htons(sizeof(struct ip) +
317 sizeof(struct udphdr) +
318 len) ;
319 ip.ip_hl = sizeof(struct ip) / 4;
320 ip.ip_v = IPVERSION;
321 ip.ip_tos = 0;
322 ip.ip_id = htons(0);
323 ip.ip_off = htons(0x4000); /* don't fragment */
324 ip.ip_ttl = IPDEFTTL;
325 ip.ip_sum = 0;
326 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
327 sum += ((u16 *)&ip)[i];
328 while (sum>>16)
329 sum = (sum & 0xffff) + (sum >> 16);
330 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
332 udp.uh_sport = htons(daemon->dhcp_server_port);
333 udp.uh_dport = htons(daemon->dhcp_client_port);
334 if (len & 1)
335 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
336 udp.uh_sum = 0;
337 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
338 sum += htons(IPPROTO_UDP);
339 sum += ip.ip_src.s_addr & 0xffff;
340 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
341 sum += ip.ip_dst.s_addr & 0xffff;
342 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
343 for (i = 0; i < sizeof(struct udphdr)/2; i++)
344 sum += ((u16 *)&udp)[i];
345 for (i = 0; i < (len + 1) / 2; i++)
346 sum += ((u16 *)mess)[i];
347 while (sum>>16)
348 sum = (sum & 0xffff) + (sum >> 16);
349 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
351 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
353 iov[0].iov_base = &ether;
354 iov[0].iov_len = sizeof(ether);
355 iov[1].iov_base = &ip;
356 iov[1].iov_len = sizeof(ip);
357 iov[2].iov_base = &udp;
358 iov[2].iov_len = sizeof(udp);
359 iov[3].iov_base = mess;
360 iov[3].iov_len = len;
362 while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
365 #endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */
368 #ifdef HAVE_BSD_NETWORK
370 void route_init(void)
372 /* AF_UNSPEC: all addr families */
373 daemon->routefd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
375 if (daemon->routefd == -1 || !fix_fd(daemon->routefd))
376 die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
379 void route_sock(void)
381 struct if_msghdr *msg;
382 int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
384 if (rc < 4)
385 return;
387 msg = (struct if_msghdr *)daemon->packet;
389 if (rc < msg->ifm_msglen)
390 return;
392 if (msg->ifm_version != RTM_VERSION)
394 static int warned = 0;
395 if (!warned)
397 my_syslog(LOG_WARNING, _("Unknown protocol version from route socket"));
398 warned = 1;
401 else if (msg->ifm_type == RTM_NEWADDR)
403 del_family = 0;
404 queue_event(EVENT_NEWADDR);
406 else if (msg->ifm_type == RTM_DELADDR)
408 /* There's a race in the kernel, such that if we run iface_enumerate() immediately
409 we get a DELADDR event, the deleted address still appears. Here we store the deleted address
410 in a static variable, and omit it from the set returned by iface_enumerate() */
411 int mask = ((struct ifa_msghdr *)msg)->ifam_addrs;
412 int maskvec[] = { RTA_DST, RTA_GATEWAY, RTA_NETMASK, RTA_GENMASK,
413 RTA_IFP, RTA_IFA, RTA_AUTHOR, RTA_BRD };
414 int of;
415 unsigned int i;
417 for (i = 0, of = sizeof(struct ifa_msghdr); of < rc && i < sizeof(maskvec)/sizeof(maskvec[0]); i++)
418 if (mask & maskvec[i])
420 struct sockaddr *sa = (struct sockaddr *)((char *)msg + of);
421 size_t diff = (sa->sa_len != 0) ? sa->sa_len : sizeof(long);
423 if (maskvec[i] == RTA_IFA)
425 del_family = sa->sa_family;
426 if (del_family == AF_INET)
427 del_addr.addr.addr4 = ((struct sockaddr_in *)sa)->sin_addr;
428 #ifdef HAVE_IPV6
429 else if (del_family == AF_INET6)
430 del_addr.addr.addr6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
431 #endif
432 else
433 del_family = 0;
436 of += diff;
437 /* round up as needed */
438 if (diff & (sizeof(long) - 1))
439 of += sizeof(long) - (diff & (sizeof(long) - 1));
442 queue_event(EVENT_NEWADDR);
446 #endif /* HAVE_BSD_NETWORK */