]> git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/bpf.c
Add donate button to doc.html.
[people/ms/dnsmasq.git] / src / bpf.c
1 /* dnsmasq is Copyright (c) 2000-2014 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 #include <ifaddrs.h>
21
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
35
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
42
43 #ifdef HAVE_BSD_NETWORK
44 static int del_family = 0;
45 static struct all_addr del_addr;
46 #endif
47
48 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
49
50 int arp_enumerate(void *parm, int (*callback)())
51 {
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;
60
61 buff.iov_base = NULL;
62 buff.iov_len = 0;
63
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;
76
77 while (1)
78 {
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;
85 }
86 if (rc == -1)
87 return 0;
88
89 for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
90 {
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;
96 }
97
98 return 1;
99 }
100 #endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
101
102
103 int iface_enumerate(int family, void *parm, int (*callback)())
104 {
105 struct ifaddrs *head, *addrs;
106 int errsav, fd = -1, ret = 0;
107
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
114
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;
118
119 if (getifaddrs(&head) == -1)
120 return 0;
121
122 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_IPV6)
123 if (family == AF_INET6)
124 fd = socket(PF_INET6, SOCK_DGRAM, 0);
125 #endif
126
127 for (addrs = head; addrs; addrs = addrs->ifa_next)
128 {
129 if (addrs->ifa_addr->sa_family == family)
130 {
131 int iface_index = if_nametoindex(addrs->ifa_name);
132
133 if (iface_index == 0 || !addrs->ifa_addr ||
134 (!addrs->ifa_netmask && family != AF_LINK))
135 continue;
136
137 if (family == AF_INET)
138 {
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;
152 }
153 #ifdef HAVE_IPV6
154 else if (family == AF_INET6)
155 {
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;
168
169 memset(&ifr6, 0, sizeof(ifr6));
170 strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
171
172 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
173 if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
174 {
175 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
176 flags |= IFACE_TENTATIVE;
177
178 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
179 flags |= IFACE_DEPRECATED;
180
181 #ifdef IN6_IFF_TEMPORARY
182 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
183 flags |= IFACE_PERMANENT;
184 #endif
185
186 #ifdef IN6_IFF_PRIVACY
187 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
188 flags |= IFACE_PERMANENT;
189 #endif
190 }
191
192 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
193 if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
194 {
195 valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
196 preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
197 }
198 #endif
199
200 for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
201 if (netmask[i] != 0xff)
202 break;
203
204 if (i != IN6ADDRSZ && netmask[i])
205 for (j = 7; j > 0; j--, prefix++)
206 if ((netmask[i] & (1 << j)) == 0)
207 break;
208
209 /* voodoo to clear interface field in address */
210 if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
211 {
212 addr->s6_addr[2] = 0;
213 addr->s6_addr[3] = 0;
214 }
215
216 if (!((*callback)(addr, prefix, scope_id, iface_index, flags,
217 (int) preferred, (int)valid, parm)))
218 goto err;
219 }
220 #endif /* HAVE_IPV6 */
221
222 #ifdef HAVE_DHCP6
223 else if (family == AF_LINK)
224 {
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;
230 }
231 #endif
232 }
233 }
234
235 ret = 1;
236
237 err:
238 errsav = errno;
239 freeifaddrs(head);
240 if (fd != -1)
241 close(fd);
242 errno = errsav;
243
244 return ret;
245 }
246 #endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */
247
248
249 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
250 #include <net/bpf.h>
251
252 void init_bpf(void)
253 {
254 int i = 0;
255
256 while (1)
257 {
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;
261
262 if (errno != EBUSY)
263 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
264 }
265 }
266
267 void send_via_bpf(struct dhcp_packet *mess, size_t len,
268 struct in_addr iface_addr, struct ifreq *ifr)
269 {
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 */
275
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;
284
285 u32 i, sum;
286 struct iovec iov[4];
287
288 /* Only know how to do ethernet on *BSD */
289 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
290 {
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;
294 }
295
296 ifr->ifr_addr.sa_family = AF_LINK;
297 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
298 return;
299
300 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
301 ether.ether_type = htons(ETHERTYPE_IP);
302
303 if (ntohs(mess->flags) & 0x8000)
304 {
305 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
306 ip.ip_dst.s_addr = INADDR_BROADCAST;
307 }
308 else
309 {
310 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
311 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
312 }
313
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;
331
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;
350
351 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
352
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;
361
362 while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
363 }
364
365 #endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */
366
367
368 #ifdef HAVE_BSD_NETWORK
369
370 void route_init(void)
371 {
372 /* AF_UNSPEC: all addr families */
373 daemon->routefd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
374
375 if (daemon->routefd == -1 || !fix_fd(daemon->routefd))
376 die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
377 }
378
379 void route_sock(time_t now)
380 {
381 struct if_msghdr *msg;
382 int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
383
384 if (rc < 4)
385 return;
386
387 msg = (struct if_msghdr *)daemon->packet;
388
389 if (rc < msg->ifm_msglen)
390 return;
391
392 if (msg->ifm_version != RTM_VERSION)
393 {
394 static int warned = 0;
395 if (!warned)
396 {
397 my_syslog(LOG_WARNING, _("Unknown protocol version from route socket"));
398 warned = 1;
399 }
400 }
401 else if (msg->ifm_type == RTM_NEWADDR)
402 {
403 del_family = 0;
404 newaddress(now);
405 }
406 else if (msg->ifm_type == RTM_DELADDR)
407 {
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;
416
417 for (i = 0, of = sizeof(struct ifa_msghdr); of < rc && i < sizeof(maskvec)/sizeof(maskvec[0]); i++)
418 if (mask & maskvec[i])
419 {
420 struct sockaddr *sa = (struct sockaddr *)((char *)msg + of);
421 size_t diff = (sa->sa_len != 0) ? sa->sa_len : sizeof(long);
422
423 if (maskvec[i] == RTA_IFA)
424 {
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;
434 }
435
436 of += diff;
437 /* round up as needed */
438 if (diff & (sizeof(long) - 1))
439 of += sizeof(long) - (diff & (sizeof(long) - 1));
440 }
441
442 newaddress(now);
443 }
444 }
445
446 #endif /* HAVE_BSD_NETWORK */
447
448