]>
Commit | Line | Data |
---|---|---|
aff33962 | 1 | /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley |
5e9e0efb SK |
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 | |
824af85b SK |
5 | the Free Software Foundation; version 2 dated June, 1991, or |
6 | (at your option) version 3 dated 29 June, 2007. | |
7 | ||
5e9e0efb SK |
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. | |
824af85b | 12 | |
73a08a24 SK |
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/>. | |
5e9e0efb SK |
15 | */ |
16 | ||
17 | #include "dnsmasq.h" | |
18 | ||
824af85b | 19 | #if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) |
70969c17 | 20 | #include <ifaddrs.h> |
5e9e0efb | 21 | |
132255b5 | 22 | #include <sys/param.h> |
28866e95 | 23 | #include <sys/sysctl.h> |
1ee9be4c | 24 | #include <net/if.h> |
28866e95 SK |
25 | #include <net/route.h> |
26 | #include <net/if_dl.h> | |
27 | #include <netinet/if_ether.h> | |
8c3bdb4f VG |
28 | #if defined(__FreeBSD__) |
29 | # include <net/if_var.h> | |
30 | #endif | |
31 | #include <netinet/in_var.h> | |
1ee9be4c SK |
32 | #ifdef HAVE_IPV6 |
33 | # include <netinet6/in6_var.h> | |
34 | #endif | |
28866e95 | 35 | |
7de060b0 SK |
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 | ||
1ee9be4c SK |
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 | ||
28866e95 SK |
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; | |
08456c61 | 58 | struct iovec buff; |
28866e95 | 59 | int rc; |
08456c61 SK |
60 | |
61 | buff.iov_base = NULL; | |
96fafe2e | 62 | buff.iov_len = 0; |
08456c61 | 63 | |
28866e95 SK |
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 | { | |
08456c61 | 79 | if (!expand_buf(&buff, needed)) |
28866e95 | 80 | return 0; |
08456c61 | 81 | if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 || |
28866e95 SK |
82 | errno != ENOMEM) |
83 | break; | |
84 | needed += needed / 8; | |
85 | } | |
86 | if (rc == -1) | |
87 | return 0; | |
88 | ||
08456c61 | 89 | for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen) |
28866e95 SK |
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 | } | |
1ee9be4c | 100 | #endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */ |
28866e95 SK |
101 | |
102 | ||
103 | int iface_enumerate(int family, void *parm, int (*callback)()) | |
824af85b | 104 | { |
08456c61 | 105 | struct ifaddrs *head, *addrs; |
8c3bdb4f | 106 | int errsav, fd = -1, ret = 0; |
96fafe2e | 107 | |
28866e95 SK |
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 | ||
6aef600d SK |
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 | ||
08456c61 | 119 | if (getifaddrs(&head) == -1) |
824af85b | 120 | return 0; |
08456c61 | 121 | |
8c3bdb4f VG |
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 | ||
08456c61 | 127 | for (addrs = head; addrs; addrs = addrs->ifa_next) |
824af85b | 128 | { |
96fafe2e | 129 | if (addrs->ifa_addr->sa_family == family) |
824af85b | 130 | { |
08456c61 SK |
131 | int iface_index = if_nametoindex(addrs->ifa_name); |
132 | ||
fc4c4fda SK |
133 | if (iface_index == 0 || !addrs->ifa_addr || |
134 | (!addrs->ifa_netmask && family != AF_LINK)) | |
08456c61 SK |
135 | continue; |
136 | ||
28866e95 SK |
137 | if (family == AF_INET) |
138 | { | |
139 | struct in_addr addr, netmask, broadcast; | |
08456c61 | 140 | addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr; |
1ee9be4c SK |
141 | #ifdef HAVE_BSD_NETWORK |
142 | if (del_family == AF_INET && del_addr.addr.addr4.s_addr == addr.s_addr) | |
143 | continue; | |
144 | #endif | |
08456c61 | 145 | netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr; |
39f6a04c SK |
146 | if (addrs->ifa_broadaddr) |
147 | broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr; | |
148 | else | |
149 | broadcast.s_addr = 0; | |
3f2873d4 | 150 | if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm))) |
28866e95 SK |
151 | goto err; |
152 | } | |
824af85b | 153 | #ifdef HAVE_IPV6 |
28866e95 | 154 | else if (family == AF_INET6) |
824af85b | 155 | { |
08456c61 | 156 | struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr; |
96fafe2e SK |
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; | |
08456c61 | 159 | int i, j, prefix = 0; |
8c3bdb4f VG |
160 | u32 valid = 0xffffffff, preferred = 0xffffffff; |
161 | int flags = 0; | |
1ee9be4c SK |
162 | #ifdef HAVE_BSD_NETWORK |
163 | if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr.addr6, addr)) | |
164 | continue; | |
165 | #endif | |
50d7f721 | 166 | #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) |
8c3bdb4f VG |
167 | struct in6_ifreq ifr6; |
168 | ||
169 | memset(&ifr6, 0, sizeof(ifr6)); | |
170 | strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name)); | |
08456c61 | 171 | |
8c3bdb4f VG |
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; | |
4568a6f8 | 180 | |
1b55190d | 181 | #ifdef IN6_IFF_TEMPORARY |
4568a6f8 VG |
182 | if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY))) |
183 | flags |= IFACE_PERMANENT; | |
1b55190d | 184 | #endif |
4568a6f8 | 185 | |
1b55190d SK |
186 | #ifdef IN6_IFF_PRIVACY |
187 | if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY))) | |
188 | flags |= IFACE_PERMANENT; | |
189 | #endif | |
8c3bdb4f VG |
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 | ||
96fafe2e | 200 | for (i = 0; i < IN6ADDRSZ; i++, prefix += 8) |
08456c61 SK |
201 | if (netmask[i] != 0xff) |
202 | break; | |
8c3bdb4f | 203 | |
96fafe2e | 204 | if (i != IN6ADDRSZ && netmask[i]) |
08456c61 SK |
205 | for (j = 7; j > 0; j--, prefix++) |
206 | if ((netmask[i] & (1 << j)) == 0) | |
207 | break; | |
208 | ||
28866e95 SK |
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; | |
8c3bdb4f VG |
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 | ||
6aef600d SK |
222 | #ifdef HAVE_DHCP6 |
223 | else if (family == AF_LINK) | |
224 | { | |
225 | /* Assume ethernet again here */ | |
96fafe2e | 226 | struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr; |
08456c61 SK |
227 | if (sdl->sdl_alen != 0 && |
228 | !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm))) | |
6aef600d SK |
229 | goto err; |
230 | } | |
231 | #endif | |
28866e95 | 232 | } |
824af85b SK |
233 | } |
234 | ||
235 | ret = 1; | |
236 | ||
237 | err: | |
238 | errsav = errno; | |
8c3bdb4f VG |
239 | freeifaddrs(head); |
240 | if (fd != -1) | |
241 | close(fd); | |
824af85b SK |
242 | errno = errsav; |
243 | ||
244 | return ret; | |
245 | } | |
1ee9be4c | 246 | #endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */ |
824af85b SK |
247 | |
248 | ||
7622fc06 | 249 | #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) |
824af85b SK |
250 | #include <net/bpf.h> |
251 | ||
5aabfc78 | 252 | void init_bpf(void) |
5e9e0efb SK |
253 | { |
254 | int i = 0; | |
255 | ||
256 | while (1) | |
257 | { | |
08456c61 SK |
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 | ||
5e9e0efb | 262 | if (errno != EBUSY) |
5aabfc78 | 263 | die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET); |
5e9e0efb SK |
264 | } |
265 | } | |
266 | ||
5aabfc78 | 267 | void send_via_bpf(struct dhcp_packet *mess, size_t len, |
5e9e0efb SK |
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 | ||
849a8357 SK |
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 | ||
5e9e0efb | 285 | u32 i, sum; |
849a8357 | 286 | struct iovec iov[4]; |
5e9e0efb SK |
287 | |
288 | /* Only know how to do ethernet on *BSD */ | |
289 | if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN) | |
290 | { | |
7622fc06 | 291 | my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"), |
f2621c7f | 292 | mess->htype, ifr->ifr_name); |
5e9e0efb SK |
293 | return; |
294 | } | |
295 | ||
296 | ifr->ifr_addr.sa_family = AF_LINK; | |
297 | if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0) | |
298 | return; | |
299 | ||
849a8357 SK |
300 | memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN); |
301 | ether.ether_type = htons(ETHERTYPE_IP); | |
5e9e0efb SK |
302 | |
303 | if (ntohs(mess->flags) & 0x8000) | |
304 | { | |
849a8357 SK |
305 | memset(ether.ether_dhost, 255, ETHER_ADDR_LEN); |
306 | ip.ip_dst.s_addr = INADDR_BROADCAST; | |
5e9e0efb SK |
307 | } |
308 | else | |
309 | { | |
849a8357 SK |
310 | memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN); |
311 | ip.ip_dst.s_addr = mess->yiaddr.s_addr; | |
5e9e0efb SK |
312 | } |
313 | ||
849a8357 SK |
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; | |
5e9e0efb | 326 | for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++) |
849a8357 | 327 | sum += ((u16 *)&ip)[i]; |
5e9e0efb SK |
328 | while (sum>>16) |
329 | sum = (sum & 0xffff) + (sum >> 16); | |
849a8357 | 330 | ip.ip_sum = (sum == 0xffff) ? sum : ~sum; |
5e9e0efb | 331 | |
9e038946 SK |
332 | udp.uh_sport = htons(daemon->dhcp_server_port); |
333 | udp.uh_dport = htons(daemon->dhcp_client_port); | |
5e9e0efb SK |
334 | if (len & 1) |
335 | ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */ | |
849a8357 SK |
336 | udp.uh_sum = 0; |
337 | udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len); | |
5e9e0efb | 338 | sum += htons(IPPROTO_UDP); |
824af85b SK |
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; | |
5e9e0efb | 343 | for (i = 0; i < sizeof(struct udphdr)/2; i++) |
849a8357 | 344 | sum += ((u16 *)&udp)[i]; |
5e9e0efb SK |
345 | for (i = 0; i < (len + 1) / 2; i++) |
346 | sum += ((u16 *)mess)[i]; | |
347 | while (sum>>16) | |
348 | sum = (sum & 0xffff) + (sum >> 16); | |
849a8357 | 349 | udp.uh_sum = (sum == 0xffff) ? sum : ~sum; |
5e9e0efb SK |
350 | |
351 | ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr); | |
352 | ||
849a8357 SK |
353 | iov[0].iov_base = ðer; |
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 | ||
ff841ebf | 362 | while (retry_send(writev(daemon->dhcp_raw_fd, iov, 4))); |
5e9e0efb SK |
363 | } |
364 | ||
1ee9be4c SK |
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 | ||
a0358e5d | 379 | void route_sock(void) |
1ee9be4c SK |
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; | |
47a95169 | 404 | queue_event(EVENT_NEWADDR); |
1ee9be4c SK |
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; | |
5e9e0efb | 431 | #endif |
1ee9be4c SK |
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 | ||
47a95169 | 442 | queue_event(EVENT_NEWADDR); |
1ee9be4c SK |
443 | } |
444 | } | |
445 | ||
446 | #endif /* HAVE_BSD_NETWORK */ | |
5e9e0efb | 447 | |
5e9e0efb | 448 |