blob: 15c42fc322d6a3fd584d788d115a0c03f09b4ad2 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* dnsmasq is Copyright (c) 2000-2021 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#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#include <netinet6/in6_var.h>
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
44static int del_family = 0;
45static union all_addr del_addr;
46#endif
47
48#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
49
50int 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
103int iface_enumerate(int family, void *parm, int (*callback)())
104{
105 struct ifaddrs *head, *addrs;
106 int errsave, 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)
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.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 else if (family == AF_INET6)
154 {
155 struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
156 unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
157 int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
158 int i, j, prefix = 0;
159 u32 valid = 0xffffffff, preferred = 0xffffffff;
160 int flags = 0;
161#ifdef HAVE_BSD_NETWORK
162 if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr6, addr))
163 continue;
164#endif
165#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
166 struct in6_ifreq ifr6;
167
168 memset(&ifr6, 0, sizeof(ifr6));
169 safe_strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
170
171 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
172 if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
173 {
174 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
175 flags |= IFACE_TENTATIVE;
176
177 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
178 flags |= IFACE_DEPRECATED;
179
180#ifdef IN6_IFF_TEMPORARY
181 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
182 flags |= IFACE_PERMANENT;
183#endif
184
185#ifdef IN6_IFF_PRIVACY
186 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
187 flags |= IFACE_PERMANENT;
188#endif
189 }
190
191 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
192 if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
193 {
194 valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
195 preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
196 }
197#endif
198
199 for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
200 if (netmask[i] != 0xff)
201 break;
202
203 if (i != IN6ADDRSZ && netmask[i])
204 for (j = 7; j > 0; j--, prefix++)
205 if ((netmask[i] & (1 << j)) == 0)
206 break;
207
208 /* voodoo to clear interface field in address */
209 if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
210 {
211 addr->s6_addr[2] = 0;
212 addr->s6_addr[3] = 0;
213 }
214
215 if (!((*callback)(addr, prefix, scope_id, iface_index, flags,
216 (int) preferred, (int)valid, parm)))
217 goto err;
218 }
219
220#ifdef HAVE_DHCP6
221 else if (family == AF_LINK)
222 {
223 /* Assume ethernet again here */
224 struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
225 if (sdl->sdl_alen != 0 &&
226 !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
227 goto err;
228 }
229#endif
230 }
231 }
232
233 ret = 1;
234
235 err:
236 errsave = errno;
237 freeifaddrs(head);
238 if (fd != -1)
239 close(fd);
240 errno = errsave;
241
242 return ret;
243}
244#endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */
245
246
247#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
248#include <net/bpf.h>
249
250void init_bpf(void)
251{
252 int i = 0;
253
254 while (1)
255 {
256 sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
257 if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
258 return;
259
260 if (errno != EBUSY)
261 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
262 }
263}
264
265void send_via_bpf(struct dhcp_packet *mess, size_t len,
266 struct in_addr iface_addr, struct ifreq *ifr)
267{
268 /* Hairy stuff, packet either has to go to the
269 net broadcast or the destination can't reply to ARP yet,
270 but we do know the physical address.
271 Build the packet by steam, and send directly, bypassing
272 the kernel IP stack */
273
274 struct ether_header ether;
275 struct ip ip;
276 struct udphdr {
277 u16 uh_sport; /* source port */
278 u16 uh_dport; /* destination port */
279 u16 uh_ulen; /* udp length */
280 u16 uh_sum; /* udp checksum */
281 } udp;
282
283 u32 i, sum;
284 struct iovec iov[4];
285
286 /* Only know how to do ethernet on *BSD */
287 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
288 {
289 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
290 mess->htype, ifr->ifr_name);
291 return;
292 }
293
294 ifr->ifr_addr.sa_family = AF_LINK;
295 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
296 return;
297
298 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
299 ether.ether_type = htons(ETHERTYPE_IP);
300
301 if (ntohs(mess->flags) & 0x8000)
302 {
303 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
304 ip.ip_dst.s_addr = INADDR_BROADCAST;
305 }
306 else
307 {
308 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
309 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
310 }
311
312 ip.ip_p = IPPROTO_UDP;
313 ip.ip_src.s_addr = iface_addr.s_addr;
314 ip.ip_len = htons(sizeof(struct ip) +
315 sizeof(struct udphdr) +
316 len) ;
317 ip.ip_hl = sizeof(struct ip) / 4;
318 ip.ip_v = IPVERSION;
319 ip.ip_tos = 0;
320 ip.ip_id = htons(0);
321 ip.ip_off = htons(0x4000); /* don't fragment */
322 ip.ip_ttl = IPDEFTTL;
323 ip.ip_sum = 0;
324 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
325 sum += ((u16 *)&ip)[i];
326 while (sum>>16)
327 sum = (sum & 0xffff) + (sum >> 16);
328 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
329
330 udp.uh_sport = htons(daemon->dhcp_server_port);
331 udp.uh_dport = htons(daemon->dhcp_client_port);
332 if (len & 1)
333 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
334 udp.uh_sum = 0;
335 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
336 sum += htons(IPPROTO_UDP);
337 sum += ip.ip_src.s_addr & 0xffff;
338 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
339 sum += ip.ip_dst.s_addr & 0xffff;
340 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
341 for (i = 0; i < sizeof(struct udphdr)/2; i++)
342 sum += ((u16 *)&udp)[i];
343 for (i = 0; i < (len + 1) / 2; i++)
344 sum += ((u16 *)mess)[i];
345 while (sum>>16)
346 sum = (sum & 0xffff) + (sum >> 16);
347 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
348
349 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
350
351 iov[0].iov_base = &ether;
352 iov[0].iov_len = sizeof(ether);
353 iov[1].iov_base = &ip;
354 iov[1].iov_len = sizeof(ip);
355 iov[2].iov_base = &udp;
356 iov[2].iov_len = sizeof(udp);
357 iov[3].iov_base = mess;
358 iov[3].iov_len = len;
359
360 while (retry_send(writev(daemon->dhcp_raw_fd, iov, 4)));
361}
362
363#endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */
364
365
366#ifdef HAVE_BSD_NETWORK
367
368void route_init(void)
369{
370 /* AF_UNSPEC: all addr families */
371 daemon->routefd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
372
373 if (daemon->routefd == -1 || !fix_fd(daemon->routefd))
374 die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
375}
376
377void route_sock(void)
378{
379 struct if_msghdr *msg;
380 int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
381
382 if (rc < 4)
383 return;
384
385 msg = (struct if_msghdr *)daemon->packet;
386
387 if (rc < msg->ifm_msglen)
388 return;
389
390 if (msg->ifm_version != RTM_VERSION)
391 {
392 static int warned = 0;
393 if (!warned)
394 {
395 my_syslog(LOG_WARNING, _("Unknown protocol version from route socket"));
396 warned = 1;
397 }
398 }
399 else if (msg->ifm_type == RTM_NEWADDR)
400 {
401 del_family = 0;
402 queue_event(EVENT_NEWADDR);
403 }
404 else if (msg->ifm_type == RTM_DELADDR)
405 {
406 /* There's a race in the kernel, such that if we run iface_enumerate() immediately
407 we get a DELADDR event, the deleted address still appears. Here we store the deleted address
408 in a static variable, and omit it from the set returned by iface_enumerate() */
409 int mask = ((struct ifa_msghdr *)msg)->ifam_addrs;
410 int maskvec[] = { RTA_DST, RTA_GATEWAY, RTA_NETMASK, RTA_GENMASK,
411 RTA_IFP, RTA_IFA, RTA_AUTHOR, RTA_BRD };
412 int of;
413 unsigned int i;
414
415 for (i = 0, of = sizeof(struct ifa_msghdr); of < rc && i < sizeof(maskvec)/sizeof(maskvec[0]); i++)
416 if (mask & maskvec[i])
417 {
418 struct sockaddr *sa = (struct sockaddr *)((char *)msg + of);
419 size_t diff = (sa->sa_len != 0) ? sa->sa_len : sizeof(long);
420
421 if (maskvec[i] == RTA_IFA)
422 {
423 del_family = sa->sa_family;
424 if (del_family == AF_INET)
425 del_addr.addr4 = ((struct sockaddr_in *)sa)->sin_addr;
426 else if (del_family == AF_INET6)
427 del_addr.addr6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
428 else
429 del_family = 0;
430 }
431
432 of += diff;
433 /* round up as needed */
434 if (diff & (sizeof(long) - 1))
435 of += sizeof(long) - (diff & (sizeof(long) - 1));
436 }
437
438 queue_event(EVENT_NEWADDR);
439 }
440}
441
442#endif /* HAVE_BSD_NETWORK */