dnsmasq: re-apply original Tomato specific code
[tomato.git] / release / src / router / dnsmasq / src / bpf.c
blob69dea132c09f88d2a32edd5cbd3d41d075bbf1d8
1 /* dnsmasq is Copyright (c) 2000-2016 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 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
24 #include <sys/sysctl.h>
25 #endif
26 #include <net/if.h>
27 #include <net/route.h>
28 #include <net/if_dl.h>
29 #include <netinet/if_ether.h>
30 #if defined(__FreeBSD__)
31 # include <net/if_var.h>
32 #endif
33 #include <netinet/in_var.h>
34 #ifdef HAVE_IPV6
35 # include <netinet6/in6_var.h>
36 #endif
38 #ifndef SA_SIZE
39 #define SA_SIZE(sa) \
40 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
41 sizeof(long) : \
42 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
43 #endif
45 #ifdef HAVE_BSD_NETWORK
46 static int del_family = 0;
47 static struct all_addr del_addr;
48 #endif
50 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
52 int arp_enumerate(void *parm, int (*callback)())
54 int mib[6];
55 size_t needed;
56 char *next;
57 struct rt_msghdr *rtm;
58 struct sockaddr_inarp *sin2;
59 struct sockaddr_dl *sdl;
60 struct iovec buff;
61 int rc;
63 buff.iov_base = NULL;
64 buff.iov_len = 0;
66 mib[0] = CTL_NET;
67 mib[1] = PF_ROUTE;
68 mib[2] = 0;
69 mib[3] = AF_INET;
70 mib[4] = NET_RT_FLAGS;
71 #ifdef RTF_LLINFO
72 mib[5] = RTF_LLINFO;
73 #else
74 mib[5] = 0;
75 #endif
76 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
77 return 0;
79 while (1)
81 if (!expand_buf(&buff, needed))
82 return 0;
83 if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
84 errno != ENOMEM)
85 break;
86 needed += needed / 8;
88 if (rc == -1)
89 return 0;
91 for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
93 rtm = (struct rt_msghdr *)next;
94 sin2 = (struct sockaddr_inarp *)(rtm + 1);
95 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
96 if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
97 return 0;
100 return 1;
102 #endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
105 int iface_enumerate(int family, void *parm, int (*callback)())
107 struct ifaddrs *head, *addrs;
108 int errsav, fd = -1, ret = 0;
110 if (family == AF_UNSPEC)
111 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
112 return arp_enumerate(parm, callback);
113 #else
114 return 0; /* need code for Solaris and MacOS*/
115 #endif
117 /* AF_LINK doesn't exist in Linux, so we can't use it in our API */
118 if (family == AF_LOCAL)
119 family = AF_LINK;
121 if (getifaddrs(&head) == -1)
122 return 0;
124 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_IPV6)
125 if (family == AF_INET6)
126 fd = socket(PF_INET6, SOCK_DGRAM, 0);
127 #endif
129 for (addrs = head; addrs; addrs = addrs->ifa_next)
131 if (addrs->ifa_addr->sa_family == family)
133 int iface_index = if_nametoindex(addrs->ifa_name);
135 if (iface_index == 0 || !addrs->ifa_addr ||
136 (!addrs->ifa_netmask && family != AF_LINK))
137 continue;
139 if (family == AF_INET)
141 struct in_addr addr, netmask, broadcast;
142 addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
143 #ifdef HAVE_BSD_NETWORK
144 if (del_family == AF_INET && del_addr.addr.addr4.s_addr == addr.s_addr)
145 continue;
146 #endif
147 netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
148 if (addrs->ifa_broadaddr)
149 broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
150 else
151 broadcast.s_addr = 0;
152 if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm)))
153 goto err;
155 #ifdef HAVE_IPV6
156 else if (family == AF_INET6)
158 struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
159 unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
160 int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
161 int i, j, prefix = 0;
162 u32 valid = 0xffffffff, preferred = 0xffffffff;
163 int flags = 0;
164 #ifdef HAVE_BSD_NETWORK
165 if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr.addr6, addr))
166 continue;
167 #endif
168 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
169 struct in6_ifreq ifr6;
171 memset(&ifr6, 0, sizeof(ifr6));
172 strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
174 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
175 if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
177 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
178 flags |= IFACE_TENTATIVE;
180 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
181 flags |= IFACE_DEPRECATED;
183 #ifdef IN6_IFF_TEMPORARY
184 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
185 flags |= IFACE_PERMANENT;
186 #endif
188 #ifdef IN6_IFF_PRIVACY
189 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
190 flags |= IFACE_PERMANENT;
191 #endif
194 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
195 if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
197 valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
198 preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
200 #endif
202 for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
203 if (netmask[i] != 0xff)
204 break;
206 if (i != IN6ADDRSZ && netmask[i])
207 for (j = 7; j > 0; j--, prefix++)
208 if ((netmask[i] & (1 << j)) == 0)
209 break;
211 /* voodoo to clear interface field in address */
212 if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
214 addr->s6_addr[2] = 0;
215 addr->s6_addr[3] = 0;
218 if (!((*callback)(addr, prefix, scope_id, iface_index, flags,
219 (int) preferred, (int)valid, parm)))
220 goto err;
222 #endif /* HAVE_IPV6 */
224 #ifdef HAVE_DHCP6
225 else if (family == AF_LINK)
227 /* Assume ethernet again here */
228 struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
229 if (sdl->sdl_alen != 0 &&
230 !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
231 goto err;
233 #endif
237 ret = 1;
239 err:
240 errsav = errno;
241 freeifaddrs(head);
242 if (fd != -1)
243 close(fd);
244 errno = errsav;
246 return ret;
248 #endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */
251 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
252 #include <net/bpf.h>
254 void init_bpf(void)
256 int i = 0;
258 while (1)
260 sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
261 if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
262 return;
264 if (errno != EBUSY)
265 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
269 void send_via_bpf(struct dhcp_packet *mess, size_t len,
270 struct in_addr iface_addr, struct ifreq *ifr)
272 /* Hairy stuff, packet either has to go to the
273 net broadcast or the destination can't reply to ARP yet,
274 but we do know the physical address.
275 Build the packet by steam, and send directly, bypassing
276 the kernel IP stack */
278 struct ether_header ether;
279 struct ip ip;
280 struct udphdr {
281 u16 uh_sport; /* source port */
282 u16 uh_dport; /* destination port */
283 u16 uh_ulen; /* udp length */
284 u16 uh_sum; /* udp checksum */
285 } udp;
287 u32 i, sum;
288 struct iovec iov[4];
290 /* Only know how to do ethernet on *BSD */
291 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
293 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
294 mess->htype, ifr->ifr_name);
295 return;
298 ifr->ifr_addr.sa_family = AF_LINK;
299 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
300 return;
302 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
303 ether.ether_type = htons(ETHERTYPE_IP);
305 if (ntohs(mess->flags) & 0x8000)
307 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
308 ip.ip_dst.s_addr = INADDR_BROADCAST;
310 else
312 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
313 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
316 ip.ip_p = IPPROTO_UDP;
317 ip.ip_src.s_addr = iface_addr.s_addr;
318 ip.ip_len = htons(sizeof(struct ip) +
319 sizeof(struct udphdr) +
320 len) ;
321 ip.ip_hl = sizeof(struct ip) / 4;
322 ip.ip_v = IPVERSION;
323 ip.ip_tos = 0;
324 ip.ip_id = htons(0);
325 ip.ip_off = htons(0x4000); /* don't fragment */
326 ip.ip_ttl = IPDEFTTL;
327 ip.ip_sum = 0;
328 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
329 sum += ((u16 *)&ip)[i];
330 while (sum>>16)
331 sum = (sum & 0xffff) + (sum >> 16);
332 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
334 udp.uh_sport = htons(daemon->dhcp_server_port);
335 udp.uh_dport = htons(daemon->dhcp_client_port);
336 if (len & 1)
337 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
338 udp.uh_sum = 0;
339 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
340 sum += htons(IPPROTO_UDP);
341 sum += ip.ip_src.s_addr & 0xffff;
342 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
343 sum += ip.ip_dst.s_addr & 0xffff;
344 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
345 for (i = 0; i < sizeof(struct udphdr)/2; i++)
346 sum += ((u16 *)&udp)[i];
347 for (i = 0; i < (len + 1) / 2; i++)
348 sum += ((u16 *)mess)[i];
349 while (sum>>16)
350 sum = (sum & 0xffff) + (sum >> 16);
351 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
353 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
355 iov[0].iov_base = &ether;
356 iov[0].iov_len = sizeof(ether);
357 iov[1].iov_base = &ip;
358 iov[1].iov_len = sizeof(ip);
359 iov[2].iov_base = &udp;
360 iov[2].iov_len = sizeof(udp);
361 iov[3].iov_base = mess;
362 iov[3].iov_len = len;
364 while (retry_send(writev(daemon->dhcp_raw_fd, iov, 4)));
367 #endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */
370 #ifdef HAVE_BSD_NETWORK
372 void route_init(void)
374 /* AF_UNSPEC: all addr families */
375 daemon->routefd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
377 if (daemon->routefd == -1 || !fix_fd(daemon->routefd))
378 die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
381 void route_sock(void)
383 struct if_msghdr *msg;
384 int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
386 if (rc < 4)
387 return;
389 msg = (struct if_msghdr *)daemon->packet;
391 if (rc < msg->ifm_msglen)
392 return;
394 if (msg->ifm_version != RTM_VERSION)
396 static int warned = 0;
397 if (!warned)
399 my_syslog(LOG_WARNING, _("Unknown protocol version from route socket"));
400 warned = 1;
403 else if (msg->ifm_type == RTM_NEWADDR)
405 del_family = 0;
406 queue_event(EVENT_NEWADDR);
408 else if (msg->ifm_type == RTM_DELADDR)
410 /* There's a race in the kernel, such that if we run iface_enumerate() immediately
411 we get a DELADDR event, the deleted address still appears. Here we store the deleted address
412 in a static variable, and omit it from the set returned by iface_enumerate() */
413 int mask = ((struct ifa_msghdr *)msg)->ifam_addrs;
414 int maskvec[] = { RTA_DST, RTA_GATEWAY, RTA_NETMASK, RTA_GENMASK,
415 RTA_IFP, RTA_IFA, RTA_AUTHOR, RTA_BRD };
416 int of;
417 unsigned int i;
419 for (i = 0, of = sizeof(struct ifa_msghdr); of < rc && i < sizeof(maskvec)/sizeof(maskvec[0]); i++)
420 if (mask & maskvec[i])
422 struct sockaddr *sa = (struct sockaddr *)((char *)msg + of);
423 size_t diff = (sa->sa_len != 0) ? sa->sa_len : sizeof(long);
425 if (maskvec[i] == RTA_IFA)
427 del_family = sa->sa_family;
428 if (del_family == AF_INET)
429 del_addr.addr.addr4 = ((struct sockaddr_in *)sa)->sin_addr;
430 #ifdef HAVE_IPV6
431 else if (del_family == AF_INET6)
432 del_addr.addr.addr6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
433 #endif
434 else
435 del_family = 0;
438 of += diff;
439 /* round up as needed */
440 if (diff & (sizeof(long) - 1))
441 of += sizeof(long) - (diff & (sizeof(long) - 1));
444 queue_event(EVENT_NEWADDR);
448 #endif /* HAVE_BSD_NETWORK */