dnsmasq: v2.67test16 patch Sept.25th/2013.
[tomato.git] / release / src / router / dnsmasq / src / bpf.c
blob390d0767f6a08a6dca30d54ed2ff6f048e83f854
1 /* dnsmasq is Copyright (c) 2000-2013 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 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
23 #include <sys/param.h>
24 #include <sys/sysctl.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>
33 #ifndef SA_SIZE
34 #define SA_SIZE(sa) \
35 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
36 sizeof(long) : \
37 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
38 #endif
40 int arp_enumerate(void *parm, int (*callback)())
42 int mib[6];
43 size_t needed;
44 char *next;
45 struct rt_msghdr *rtm;
46 struct sockaddr_inarp *sin2;
47 struct sockaddr_dl *sdl;
48 struct iovec buff;
49 int rc;
51 buff.iov_base = NULL;
52 buff.iov_len = 0;
54 mib[0] = CTL_NET;
55 mib[1] = PF_ROUTE;
56 mib[2] = 0;
57 mib[3] = AF_INET;
58 mib[4] = NET_RT_FLAGS;
59 #ifdef RTF_LLINFO
60 mib[5] = RTF_LLINFO;
61 #else
62 mib[5] = 0;
63 #endif
64 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
65 return 0;
67 while (1)
69 if (!expand_buf(&buff, needed))
70 return 0;
71 if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
72 errno != ENOMEM)
73 break;
74 needed += needed / 8;
76 if (rc == -1)
77 return 0;
79 for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
81 rtm = (struct rt_msghdr *)next;
82 sin2 = (struct sockaddr_inarp *)(rtm + 1);
83 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
84 if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
85 return 0;
88 return 1;
90 #endif
93 int iface_enumerate(int family, void *parm, int (*callback)())
95 struct ifaddrs *head, *addrs;
96 int errsav, fd = -1, ret = 0;
98 if (family == AF_UNSPEC)
99 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
100 return arp_enumerate(parm, callback);
101 #else
102 return 0; /* need code for Solaris and MacOS*/
103 #endif
105 /* AF_LINK doesn't exist in Linux, so we can't use it in our API */
106 if (family == AF_LOCAL)
107 family = AF_LINK;
109 if (getifaddrs(&head) == -1)
110 return 0;
112 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_IPV6)
113 if (family == AF_INET6)
114 fd = socket(PF_INET6, SOCK_DGRAM, 0);
115 #endif
117 for (addrs = head; addrs; addrs = addrs->ifa_next)
119 if (addrs->ifa_addr->sa_family == family)
121 int iface_index = if_nametoindex(addrs->ifa_name);
123 if (iface_index == 0 || !addrs->ifa_addr ||
124 (!addrs->ifa_netmask && family != AF_LINK))
125 continue;
127 if (family == AF_INET)
129 struct in_addr addr, netmask, broadcast;
130 addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
131 netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
132 if (addrs->ifa_broadaddr)
133 broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
134 else
135 broadcast.s_addr = 0;
136 if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm)))
137 goto err;
139 #ifdef HAVE_IPV6
140 else if (family == AF_INET6)
142 struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
143 unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
144 int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
145 int i, j, prefix = 0;
146 u32 valid = 0xffffffff, preferred = 0xffffffff;
147 int flags = 0;
148 #ifdef HAVE_BSD_NETWORK
149 struct in6_ifreq ifr6;
151 memset(&ifr6, 0, sizeof(ifr6));
152 strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
154 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
155 if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
157 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
158 flags |= IFACE_TENTATIVE;
160 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
161 flags |= IFACE_DEPRECATED;
163 #ifdef IN6_IFF_TEMPORARY
164 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
165 flags |= IFACE_PERMANENT;
166 #endif
168 #ifdef IN6_IFF_PRIVACY
169 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
170 flags |= IFACE_PERMANENT;
171 #endif
174 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
175 if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
177 valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
178 preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
180 #endif
182 for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
183 if (netmask[i] != 0xff)
184 break;
186 if (i != IN6ADDRSZ && netmask[i])
187 for (j = 7; j > 0; j--, prefix++)
188 if ((netmask[i] & (1 << j)) == 0)
189 break;
191 /* voodoo to clear interface field in address */
192 if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
194 addr->s6_addr[2] = 0;
195 addr->s6_addr[3] = 0;
198 if (!((*callback)(addr, prefix, scope_id, iface_index, flags,
199 (int) preferred, (int)valid, parm)))
200 goto err;
202 #endif /* HAVE_IPV6 */
204 #ifdef HAVE_DHCP6
205 else if (family == AF_LINK)
207 /* Assume ethernet again here */
208 struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
209 if (sdl->sdl_alen != 0 &&
210 !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
211 goto err;
213 #endif
217 ret = 1;
219 err:
220 errsav = errno;
221 freeifaddrs(head);
222 if (fd != -1)
223 close(fd);
224 errno = errsav;
226 return ret;
228 #endif
231 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
232 #include <net/bpf.h>
234 void init_bpf(void)
236 int i = 0;
238 while (1)
240 sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
241 if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
242 return;
244 if (errno != EBUSY)
245 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
249 void send_via_bpf(struct dhcp_packet *mess, size_t len,
250 struct in_addr iface_addr, struct ifreq *ifr)
252 /* Hairy stuff, packet either has to go to the
253 net broadcast or the destination can't reply to ARP yet,
254 but we do know the physical address.
255 Build the packet by steam, and send directly, bypassing
256 the kernel IP stack */
258 struct ether_header ether;
259 struct ip ip;
260 struct udphdr {
261 u16 uh_sport; /* source port */
262 u16 uh_dport; /* destination port */
263 u16 uh_ulen; /* udp length */
264 u16 uh_sum; /* udp checksum */
265 } udp;
267 u32 i, sum;
268 struct iovec iov[4];
270 /* Only know how to do ethernet on *BSD */
271 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
273 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
274 mess->htype, ifr->ifr_name);
275 return;
278 ifr->ifr_addr.sa_family = AF_LINK;
279 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
280 return;
282 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
283 ether.ether_type = htons(ETHERTYPE_IP);
285 if (ntohs(mess->flags) & 0x8000)
287 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
288 ip.ip_dst.s_addr = INADDR_BROADCAST;
290 else
292 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
293 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
296 ip.ip_p = IPPROTO_UDP;
297 ip.ip_src.s_addr = iface_addr.s_addr;
298 ip.ip_len = htons(sizeof(struct ip) +
299 sizeof(struct udphdr) +
300 len) ;
301 ip.ip_hl = sizeof(struct ip) / 4;
302 ip.ip_v = IPVERSION;
303 ip.ip_tos = 0;
304 ip.ip_id = htons(0);
305 ip.ip_off = htons(0x4000); /* don't fragment */
306 ip.ip_ttl = IPDEFTTL;
307 ip.ip_sum = 0;
308 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
309 sum += ((u16 *)&ip)[i];
310 while (sum>>16)
311 sum = (sum & 0xffff) + (sum >> 16);
312 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
314 udp.uh_sport = htons(daemon->dhcp_server_port);
315 udp.uh_dport = htons(daemon->dhcp_client_port);
316 if (len & 1)
317 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
318 udp.uh_sum = 0;
319 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
320 sum += htons(IPPROTO_UDP);
321 sum += ip.ip_src.s_addr & 0xffff;
322 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
323 sum += ip.ip_dst.s_addr & 0xffff;
324 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
325 for (i = 0; i < sizeof(struct udphdr)/2; i++)
326 sum += ((u16 *)&udp)[i];
327 for (i = 0; i < (len + 1) / 2; i++)
328 sum += ((u16 *)mess)[i];
329 while (sum>>16)
330 sum = (sum & 0xffff) + (sum >> 16);
331 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
333 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
335 iov[0].iov_base = &ether;
336 iov[0].iov_len = sizeof(ether);
337 iov[1].iov_base = &ip;
338 iov[1].iov_len = sizeof(ip);
339 iov[2].iov_base = &udp;
340 iov[2].iov_len = sizeof(udp);
341 iov[3].iov_base = mess;
342 iov[3].iov_len = len;
344 while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
347 #endif