Import dhcpcd-8.0.4 to vendor branch.
[dragonfly.git] / contrib / dhcpcd / src / if-bsd.c
blob8348d42dcfae67b6794360fe90f8bf0f778d5b83
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3 * BSD interface driver for dhcpcd
4 * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
5 * All rights reserved
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
29 #include <sys/ioctl.h>
30 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 #include <sys/sysctl.h>
34 #include <sys/time.h>
35 #include <sys/types.h>
36 #include <sys/uio.h>
37 #include <sys/utsname.h>
39 #include "config.h"
41 #include <arpa/inet.h>
42 #include <net/bpf.h>
43 #include <net/if.h>
44 #include <net/if_dl.h>
45 #include <net/if_media.h>
46 #include <net/route.h>
47 #include <netinet/if_ether.h>
48 #include <netinet/in.h>
49 #include <netinet/in_var.h>
50 #include <netinet6/in6_var.h>
51 #include <netinet6/nd6.h>
52 #ifdef __NetBSD__
53 #include <net/if_vlanvar.h> /* Needs netinet/if_ether.h */
54 #elif defined(__DragonFly__)
55 #include <net/vlan/if_vlan_var.h>
56 #else
57 #include <net/if_vlan_var.h>
58 #endif
59 #ifdef __DragonFly__
60 # include <netproto/802_11/ieee80211_ioctl.h>
61 #elif __APPLE__
62 /* FIXME: Add apple includes so we can work out SSID */
63 #else
64 # include <net80211/ieee80211.h>
65 # include <net80211/ieee80211_ioctl.h>
66 #endif
68 #include <assert.h>
69 #include <errno.h>
70 #include <fcntl.h>
71 #include <fnmatch.h>
72 #include <paths.h>
73 #include <stddef.h>
74 #include <stdio.h>
75 #include <stdlib.h>
76 #include <string.h>
77 #include <unistd.h>
79 #if defined(OpenBSD) && OpenBSD >= 201411
80 /* OpenBSD dropped the global setting from sysctl but left the #define
81 * which causes a EPERM error when trying to use it.
82 * I think both the error and keeping the define are wrong, so we #undef it. */
83 #undef IPV6CTL_ACCEPT_RTADV
84 #endif
86 #include "common.h"
87 #include "dhcp.h"
88 #include "if.h"
89 #include "if-options.h"
90 #include "ipv4.h"
91 #include "ipv4ll.h"
92 #include "ipv6.h"
93 #include "ipv6nd.h"
94 #include "logerr.h"
95 #include "route.h"
96 #include "sa.h"
98 #ifndef RT_ROUNDUP
99 #define RT_ROUNDUP(a) \
100 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
101 #define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
102 #endif
104 #ifdef INET6
105 static void ifa_setscope(struct sockaddr_in6 *, unsigned int);
106 static unsigned int ifa_getscope(const struct sockaddr_in6 *);
107 #endif
109 struct priv {
110 int pf_inet6_fd;
113 struct rtm
115 struct rt_msghdr hdr;
116 char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
120 if_init(__unused struct interface *iface)
122 /* BSD promotes secondary address by default */
123 return 0;
127 if_conf(__unused struct interface *iface)
129 /* No extra checks needed on BSD */
130 return 0;
134 if_opensockets_os(struct dhcpcd_ctx *ctx)
136 struct priv *priv;
137 int n;
138 #if defined(RO_MSGFILTER) || defined(ROUTE_MSGFILTER)
139 unsigned char msgfilter[] = {
140 RTM_IFINFO,
141 #ifdef RTM_IFANNOUNCE
142 RTM_IFANNOUNCE,
143 #endif
144 RTM_ADD, RTM_CHANGE, RTM_DELETE, RTM_MISS,
145 #ifdef RTM_CHGADDR
146 RTM_CHGADDR,
147 #endif
148 RTM_NEWADDR, RTM_DELADDR
150 #ifdef ROUTE_MSGFILTER
151 unsigned int i, msgfilter_mask;
152 #endif
153 #endif
155 if ((priv = malloc(sizeof(*priv))) == NULL)
156 return -1;
157 ctx->priv = priv;
159 #ifdef INET6
160 priv->pf_inet6_fd = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
161 /* Don't return an error so we at least work on kernels witout INET6
162 * even though we expect INET6 support.
163 * We will fail noisily elsewhere anyway. */
164 #else
165 priv->pf_inet6_fd = -1;
166 #endif
168 #define SOCK_FLAGS (SOCK_CLOEXEC | SOCK_NONBLOCK)
169 ctx->link_fd = xsocket(PF_ROUTE, SOCK_RAW | SOCK_FLAGS, AF_UNSPEC);
170 #undef SOCK_FLAGS
171 if (ctx->link_fd == -1)
172 return -1;
174 /* Ignore our own route(4) messages.
175 * Sadly there is no way of doing this for route(4) messages
176 * generated from addresses we add/delete. */
177 n = 0;
178 if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_USELOOPBACK,
179 &n, sizeof(n)) == -1)
180 logerr("%s: SO_USELOOPBACK", __func__);
182 #if defined(RO_MSGFILTER)
183 if (setsockopt(ctx->link_fd, PF_ROUTE, RO_MSGFILTER,
184 &msgfilter, sizeof(msgfilter)) == -1)
185 logerr(__func__);
186 #elif defined(ROUTE_MSGFILTER)
187 /* Convert the array into a bitmask. */
188 msgfilter_mask = 0;
189 for (i = 0; i < __arraycount(msgfilter); i++)
190 msgfilter_mask |= ROUTE_FILTER(msgfilter[i]);
191 if (setsockopt(ctx->link_fd, PF_ROUTE, ROUTE_MSGFILTER,
192 &msgfilter_mask, sizeof(msgfilter_mask)) == -1)
193 logerr(__func__);
194 #else
195 #warning kernel does not support route message filtering
196 #endif
198 return 0;
201 void
202 if_closesockets_os(struct dhcpcd_ctx *ctx)
204 struct priv *priv;
206 priv = (struct priv *)ctx->priv;
207 if (priv->pf_inet6_fd != -1)
208 close(priv->pf_inet6_fd);
212 if_carrier(struct interface *ifp)
214 struct ifmediareq ifmr = { .ifm_status = 0 };
216 strlcpy(ifmr.ifm_name, ifp->name, sizeof(ifmr.ifm_name));
217 if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr) == -1 ||
218 !(ifmr.ifm_status & IFM_AVALID))
219 return LINK_UNKNOWN;
221 return (ifmr.ifm_status & IFM_ACTIVE) ? LINK_UP : LINK_DOWN;
224 static void
225 if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp)
228 memset(sdl, 0, sizeof(*sdl));
229 sdl->sdl_family = AF_LINK;
230 sdl->sdl_len = sizeof(*sdl);
231 sdl->sdl_nlen = sdl->sdl_alen = sdl->sdl_slen = 0;
232 sdl->sdl_index = (unsigned short)ifp->index;
235 #if defined(SIOCG80211NWID) || defined(SIOCGETVLAN)
236 static int if_direct_ioctl(int s, const char *ifname,
237 unsigned long cmd, void *data)
240 strlcpy(data, ifname, IFNAMSIZ);
241 return ioctl(s, cmd, data);
244 static int if_indirect_ioctl(int s, const char *ifname,
245 unsigned long cmd, void *data)
247 struct ifreq ifr;
249 memset(&ifr, 0, sizeof(ifr));
250 ifr.ifr_data = data;
251 return if_direct_ioctl(s, ifname, cmd, &ifr);
253 #endif
255 static int
256 if_getssid1(int s, const char *ifname, void *ssid)
258 int retval = -1;
259 #if defined(SIOCG80211NWID)
260 struct ieee80211_nwid nwid;
261 #elif defined(IEEE80211_IOC_SSID)
262 struct ieee80211req ireq;
263 char nwid[IEEE80211_NWID_LEN];
264 #endif
266 #if defined(SIOCG80211NWID) /* NetBSD */
267 memset(&nwid, 0, sizeof(nwid));
268 if (if_indirect_ioctl(s, ifname, SIOCG80211NWID, &nwid) == 0) {
269 if (ssid == NULL)
270 retval = nwid.i_len;
271 else if (nwid.i_len > IF_SSIDLEN)
272 errno = ENOBUFS;
273 else {
274 retval = nwid.i_len;
275 memcpy(ssid, nwid.i_nwid, nwid.i_len);
278 #elif defined(IEEE80211_IOC_SSID) /* FreeBSD */
279 memset(&ireq, 0, sizeof(ireq));
280 strlcpy(ireq.i_name, ifname, sizeof(ireq.i_name));
281 ireq.i_type = IEEE80211_IOC_SSID;
282 ireq.i_val = -1;
283 memset(nwid, 0, sizeof(nwid));
284 ireq.i_data = &nwid;
285 if (ioctl(s, SIOCG80211, &ireq) == 0) {
286 if (ssid == NULL)
287 retval = ireq.i_len;
288 else if (ireq.i_len > IF_SSIDLEN)
289 errno = ENOBUFS;
290 else {
291 retval = ireq.i_len;
292 memcpy(ssid, nwid, ireq.i_len);
295 #else
296 errno = ENOSYS;
297 #endif
299 return retval;
303 if_getssid(struct interface *ifp)
305 int r;
307 r = if_getssid1(ifp->ctx->pf_inet_fd, ifp->name, ifp->ssid);
308 if (r != -1)
309 ifp->ssid_len = (unsigned int)r;
310 else
311 ifp->ssid_len = 0;
312 ifp->ssid[ifp->ssid_len] = '\0';
313 return r;
317 * FreeBSD allows for Virtual Access Points
318 * We need to check if the interface is a Virtual Interface Master
319 * and if so, don't use it.
320 * This check is made by virtue of being a IEEE80211 device but
321 * returning the SSID gives an error.
324 if_vimaster(const struct dhcpcd_ctx *ctx, const char *ifname)
326 int r;
327 struct ifmediareq ifmr;
329 memset(&ifmr, 0, sizeof(ifmr));
330 strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
331 r = ioctl(ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr);
332 if (r == -1)
333 return -1;
334 if (ifmr.ifm_status & IFM_AVALID &&
335 IFM_TYPE(ifmr.ifm_active) == IFM_IEEE80211)
337 if (if_getssid1(ctx->pf_inet_fd, ifname, NULL) == -1)
338 return 1;
340 return 0;
343 unsigned short
344 if_vlanid(const struct interface *ifp)
346 #ifdef SIOCGETVLAN
347 struct vlanreq vlr;
349 memset(&vlr, 0, sizeof(vlr));
350 if (if_indirect_ioctl(ifp->ctx->pf_inet_fd,
351 ifp->name, SIOCGETVLAN, &vlr) != 0)
352 return 0; /* 0 means no VLANID */
353 return vlr.vlr_tag;
354 #elif defined(SIOCGVNETID)
355 struct ifreq ifr;
357 memset(&ifr, 0, sizeof(ifr));
358 strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
359 if (ioctl(ifp->ctx->pf_inet_fd, SIOCGVNETID, &ifr) != 0)
360 return 0; /* 0 means no VLANID */
361 return ifr.ifr_vnetid;
362 #else
363 UNUSED(ifp);
364 return 0; /* 0 means no VLANID */
365 #endif
368 static int
369 get_addrs(int type, const void *data, size_t data_len,
370 const struct sockaddr **sa)
372 const char *cp, *ep;
373 int i;
375 cp = data;
376 ep = cp + data_len;
377 for (i = 0; i < RTAX_MAX; i++) {
378 if (type & (1 << i)) {
379 if (cp >= ep) {
380 errno = EINVAL;
381 return -1;
383 sa[i] = (const struct sockaddr *)cp;
384 RT_ADVANCE(cp, sa[i]);
385 } else
386 sa[i] = NULL;
389 return 0;
392 static struct interface *
393 if_findsdl(struct dhcpcd_ctx *ctx, const struct sockaddr_dl *sdl)
396 if (sdl->sdl_index)
397 return if_findindex(ctx->ifaces, sdl->sdl_index);
399 if (sdl->sdl_nlen) {
400 char ifname[IF_NAMESIZE];
402 memcpy(ifname, sdl->sdl_data, sdl->sdl_nlen);
403 ifname[sdl->sdl_nlen] = '\0';
404 return if_find(ctx->ifaces, ifname);
406 if (sdl->sdl_alen) {
407 struct interface *ifp;
409 TAILQ_FOREACH(ifp, ctx->ifaces, next) {
410 if (ifp->hwlen == sdl->sdl_alen &&
411 memcmp(ifp->hwaddr,
412 sdl->sdl_data, sdl->sdl_alen) == 0)
413 return ifp;
417 errno = ENOENT;
418 return NULL;
421 static struct interface *
422 if_findsa(struct dhcpcd_ctx *ctx, const struct sockaddr *sa)
424 if (sa == NULL) {
425 errno = EINVAL;
426 return NULL;
429 switch (sa->sa_family) {
430 case AF_LINK:
432 const struct sockaddr_dl *sdl;
434 sdl = (const void *)sa;
435 return if_findsdl(ctx, sdl);
437 #ifdef INET
438 case AF_INET:
440 const struct sockaddr_in *sin;
441 struct ipv4_addr *ia;
443 sin = (const void *)sa;
444 if ((ia = ipv4_findmaskaddr(ctx, &sin->sin_addr)))
445 return ia->iface;
446 break;
448 #endif
449 #ifdef INET6
450 case AF_INET6:
452 const struct sockaddr_in6 *sin;
453 unsigned int scope;
454 struct ipv6_addr *ia;
456 sin = (const void *)sa;
457 scope = ifa_getscope(sin);
458 if (scope != 0)
459 return if_findindex(ctx->ifaces, scope);
460 if ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr)))
461 return ia->iface;
462 break;
464 #endif
465 default:
466 errno = EAFNOSUPPORT;
467 return NULL;
470 errno = ENOENT;
471 return NULL;
474 static void
475 if_copysa(struct sockaddr *dst, const struct sockaddr *src)
478 assert(dst != NULL);
479 assert(src != NULL);
481 memcpy(dst, src, src->sa_len);
482 #if defined(INET6) && defined(__KAME__)
483 if (dst->sa_family == AF_INET6) {
484 struct in6_addr *in6;
486 in6 = &satosin6(dst)->sin6_addr;
487 if (IN6_IS_ADDR_LINKLOCAL(in6))
488 in6->s6_addr[2] = in6->s6_addr[3] = '\0';
490 #endif
494 if_route(unsigned char cmd, const struct rt *rt)
496 struct dhcpcd_ctx *ctx;
497 struct rtm rtmsg;
498 struct rt_msghdr *rtm = &rtmsg.hdr;
499 char *bp = rtmsg.buffer;
500 struct sockaddr_dl sdl;
501 bool gateway_unspec;
503 assert(rt != NULL);
504 assert(rt->rt_ifp != NULL);
505 assert(rt->rt_ifp->ctx != NULL);
506 ctx = rt->rt_ifp->ctx;
508 #define ADDSA(sa) do { \
509 memcpy(bp, (sa), (sa)->sa_len); \
510 bp += RT_ROUNDUP((sa)->sa_len); \
511 } while (0 /* CONSTCOND */)
513 memset(&rtmsg, 0, sizeof(rtmsg));
514 rtm->rtm_version = RTM_VERSION;
515 rtm->rtm_type = cmd;
516 #ifdef __OpenBSD__
517 rtm->rtm_pid = getpid();
518 #endif
519 rtm->rtm_seq = ++ctx->seq;
520 rtm->rtm_flags = (int)rt->rt_flags;
521 rtm->rtm_addrs = RTA_DST;
522 #ifdef RTF_PINNED
523 if (cmd != RTM_ADD)
524 rtm->rtm_flags |= RTF_PINNED;
525 #endif
527 gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
529 if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
530 bool netmask_bcast = sa_is_allones(&rt->rt_netmask);
532 rtm->rtm_flags |= RTF_UP;
533 rtm->rtm_addrs |= RTA_GATEWAY;
534 if (!(rtm->rtm_flags & RTF_REJECT) &&
535 !sa_is_loopback(&rt->rt_gateway))
537 rtm->rtm_index = (unsigned short)rt->rt_ifp->index;
539 * OpenBSD rejects the message for on-link routes.
540 * FreeBSD-12 kernel apparently panics.
541 * I can't replicate the panic, but better safe than sorry!
542 * https://roy.marples.name/archives/dhcpcd-discuss/0002286.html
544 * Neither OS currently allows IPv6 address sharing anyway, so let's
545 * try to encourage someone to fix that by logging a waring during compile.
547 #if defined(__FreeBSD__) || defined(__OpenBSD__)
548 #warning kernel does not allow IPv6 address sharing
549 if (!gateway_unspec || rt->rt_dest.sa_family!=AF_INET6)
550 #endif
551 rtm->rtm_addrs |= RTA_IFP;
552 if (!sa_is_unspecified(&rt->rt_ifa))
553 rtm->rtm_addrs |= RTA_IFA;
555 if (netmask_bcast)
556 rtm->rtm_flags |= RTF_HOST;
557 /* Network routes are cloning or connected if supported.
558 * All other routes are static. */
559 if (gateway_unspec) {
560 #ifdef RTF_CLONING
561 rtm->rtm_flags |= RTF_CLONING;
562 #endif
563 #ifdef RTF_CONNECTED
564 rtm->rtm_flags |= RTF_CONNECTED;
565 #endif
566 #ifdef RTP_CONNECTED
567 rtm->rtm_priority = RTP_CONNECTED;
568 #endif
569 #ifdef RTF_CLONING
570 if (netmask_bcast) {
572 * We add a cloning network route for a single
573 * host. Traffic to the host will generate a
574 * cloned route and the hardware address will
575 * resolve correctly.
576 * It might be more correct to use RTF_HOST
577 * instead of RTF_CLONING, and that does work,
578 * but some OS generate an arp warning
579 * diagnostic which we don't want to do.
581 rtm->rtm_flags &= ~RTF_HOST;
583 #endif
584 } else
585 rtm->rtm_flags |= RTF_GATEWAY;
587 /* Emulate the kernel by marking address generated
588 * network routes non-static. */
589 if (!(rt->rt_dflags & RTDF_IFA_ROUTE))
590 rtm->rtm_flags |= RTF_STATIC;
592 if (rt->rt_mtu != 0) {
593 rtm->rtm_inits |= RTV_MTU;
594 rtm->rtm_rmx.rmx_mtu = rt->rt_mtu;
598 if (!(rtm->rtm_flags & RTF_HOST))
599 rtm->rtm_addrs |= RTA_NETMASK;
601 if_linkaddr(&sdl, rt->rt_ifp);
603 ADDSA(&rt->rt_dest);
605 if (rtm->rtm_addrs & RTA_GATEWAY) {
606 if (gateway_unspec)
607 ADDSA((struct sockaddr *)&sdl);
608 else {
609 union sa_ss gateway;
611 if_copysa(&gateway.sa, &rt->rt_gateway);
612 #ifdef INET6
613 if (gateway.sa.sa_family == AF_INET6)
614 ifa_setscope(&gateway.sin6, rt->rt_ifp->index);
615 #endif
616 ADDSA(&gateway.sa);
620 if (rtm->rtm_addrs & RTA_NETMASK)
621 ADDSA(&rt->rt_netmask);
623 if (rtm->rtm_addrs & RTA_IFP)
624 ADDSA((struct sockaddr *)&sdl);
626 if (rtm->rtm_addrs & RTA_IFA)
627 ADDSA(&rt->rt_ifa);
629 #undef ADDSA
631 rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm);
632 if (write(ctx->link_fd, rtm, rtm->rtm_msglen) == -1)
633 return -1;
634 return 0;
637 static int
638 if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
640 const struct sockaddr *rti_info[RTAX_MAX];
642 if (!(rtm->rtm_addrs & RTA_DST)) {
643 errno = EINVAL;
644 return -1;
646 if (rtm->rtm_type != RTM_MISS && !(rtm->rtm_addrs & RTA_GATEWAY)) {
647 errno = EINVAL;
648 return -1;
650 #ifdef RTF_CLONED
651 if (rtm->rtm_flags & RTF_CLONED) {
652 errno = ENOTSUP;
653 return -1;
655 #endif
656 #ifdef RTF_LOCAL
657 if (rtm->rtm_flags & RTF_LOCAL) {
658 errno = ENOTSUP;
659 return -1;
661 #endif
662 #ifdef RTF_BROADCAST
663 if (rtm->rtm_flags & RTF_BROADCAST) {
664 errno = ENOTSUP;
665 return -1;
667 #endif
669 if (get_addrs(rtm->rtm_addrs, (const char *)rtm + sizeof(*rtm),
670 rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)
671 return -1;
672 memset(rt, 0, sizeof(*rt));
674 rt->rt_flags = (unsigned int)rtm->rtm_flags;
675 if_copysa(&rt->rt_dest, rti_info[RTAX_DST]);
676 if (rtm->rtm_addrs & RTA_NETMASK) {
677 if_copysa(&rt->rt_netmask, rti_info[RTAX_NETMASK]);
678 if (rt->rt_netmask.sa_family == 255) /* Why? */
679 rt->rt_netmask.sa_family = rt->rt_dest.sa_family;
682 /* dhcpcd likes an unspecified gateway to indicate via the link.
683 * However we need to know if gateway was a link with an address. */
684 if (rtm->rtm_addrs & RTA_GATEWAY) {
685 if (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) {
686 const struct sockaddr_dl *sdl;
688 sdl = (const struct sockaddr_dl*)
689 (const void *)rti_info[RTAX_GATEWAY];
690 if (sdl->sdl_alen != 0)
691 rt->rt_dflags |= RTDF_GATELINK;
692 } else if (rtm->rtm_flags & RTF_GATEWAY)
693 if_copysa(&rt->rt_gateway, rti_info[RTAX_GATEWAY]);
696 if (rtm->rtm_addrs & RTA_IFA)
697 if_copysa(&rt->rt_ifa, rti_info[RTAX_IFA]);
699 rt->rt_mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
701 if (rtm->rtm_index)
702 rt->rt_ifp = if_findindex(ctx->ifaces, rtm->rtm_index);
703 else if (rtm->rtm_addrs & RTA_IFP)
704 rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_IFP]);
705 else if (rtm->rtm_addrs & RTA_GATEWAY)
706 rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
707 else
708 rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_DST]);
710 if (rt->rt_ifp == NULL && rtm->rtm_type == RTM_MISS)
711 rt->rt_ifp = if_find(ctx->ifaces, "lo0");
713 if (rt->rt_ifp == NULL) {
714 errno = ESRCH;
715 return -1;
717 return 0;
721 if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)
723 struct rt_msghdr *rtm;
724 int mib[6];
725 size_t needed;
726 char *buf, *p, *end;
727 struct rt rt, *rtn;
729 mib[0] = CTL_NET;
730 mib[1] = PF_ROUTE;
731 mib[2] = 0;
732 mib[3] = af;
733 mib[4] = NET_RT_DUMP;
734 mib[5] = 0;
736 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
737 return -1;
738 if (needed == 0)
739 return 0;
740 if ((buf = malloc(needed)) == NULL)
741 return -1;
742 if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) {
743 free(buf);
744 return -1;
747 end = buf + needed;
748 for (p = buf; p < end; p += rtm->rtm_msglen) {
749 rtm = (void *)p;
750 if (p + rtm->rtm_msglen >= end) {
751 errno = EINVAL;
752 break;
754 if (if_copyrt(ctx, &rt, rtm) != 0)
755 continue;
756 if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
757 logerr(__func__);
758 break;
760 memcpy(rtn, &rt, sizeof(*rtn));
761 if (rb_tree_insert_node(kroutes, rtn) != rtn)
762 rt_free(rtn);
764 free(buf);
765 return p == end ? 0 : -1;
768 #ifdef INET
770 if_address(unsigned char cmd, const struct ipv4_addr *ia)
772 int r;
773 struct in_aliasreq ifra;
775 memset(&ifra, 0, sizeof(ifra));
776 strlcpy(ifra.ifra_name, ia->iface->name, sizeof(ifra.ifra_name));
778 #define ADDADDR(var, addr) do { \
779 (var)->sin_family = AF_INET; \
780 (var)->sin_len = sizeof(*(var)); \
781 (var)->sin_addr = *(addr); \
782 } while (/*CONSTCOND*/0)
783 ADDADDR(&ifra.ifra_addr, &ia->addr);
784 ADDADDR(&ifra.ifra_mask, &ia->mask);
785 if (cmd == RTM_NEWADDR && ia->brd.s_addr != INADDR_ANY)
786 ADDADDR(&ifra.ifra_broadaddr, &ia->brd);
787 #undef ADDADDR
789 r = ioctl(ia->iface->ctx->pf_inet_fd,
790 cmd == RTM_DELADDR ? SIOCDIFADDR : SIOCAIFADDR, &ifra);
791 return r;
796 #if !(defined(HAVE_IFADDRS_ADDRFLAGS) && defined(HAVE_IFAM_ADDRFLAGS))
798 if_addrflags(const struct interface *ifp, const struct in_addr *addr,
799 __unused const char *alias)
801 #ifdef SIOCGIFAFLAG_IN
802 struct ifreq ifr;
803 struct sockaddr_in *sin;
805 memset(&ifr, 0, sizeof(ifr));
806 strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
807 sin = (void *)&ifr.ifr_addr;
808 sin->sin_family = AF_INET;
809 sin->sin_addr = *addr;
810 if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFAFLAG_IN, &ifr) == -1)
811 return -1;
812 return ifr.ifr_addrflags;
813 #else
814 UNUSED(ifp);
815 UNUSED(addr);
816 return 0;
817 #endif
819 #endif
820 #endif /* INET */
822 #ifdef INET6
823 static void
824 ifa_setscope(struct sockaddr_in6 *sin, unsigned int ifindex)
827 #ifdef __KAME__
828 /* KAME based systems want to store the scope inside the sin6_addr
829 * for link local addresses */
830 if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
831 uint16_t scope = htons((uint16_t)ifindex);
832 memcpy(&sin->sin6_addr.s6_addr[2], &scope,
833 sizeof(scope));
835 sin->sin6_scope_id = 0;
836 #else
837 if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
838 sin->sin6_scope_id = ifindex;
839 else
840 sin->sin6_scope_id = 0;
841 #endif
844 static unsigned int
845 ifa_getscope(const struct sockaddr_in6 *sin)
847 #ifdef __KAME__
848 uint16_t scope;
849 #endif
851 if (!IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
852 return 0;
853 #ifdef __KAME__
854 memcpy(&scope, &sin->sin6_addr.s6_addr[2], sizeof(scope));
855 return (unsigned int)ntohs(scope);
856 #else
857 return (unsigned int)sin->sin6_scope_id;
858 #endif
862 if_address6(unsigned char cmd, const struct ipv6_addr *ia)
864 struct in6_aliasreq ifa;
865 struct in6_addr mask;
866 struct priv *priv;
868 priv = (struct priv *)ia->iface->ctx->priv;
870 memset(&ifa, 0, sizeof(ifa));
871 strlcpy(ifa.ifra_name, ia->iface->name, sizeof(ifa.ifra_name));
873 * We should not set IN6_IFF_TENTATIVE as the kernel should be
874 * able to work out if it's a new address or not.
876 * We should set IN6_IFF_AUTOCONF, but the kernel won't let us.
877 * This is probably a safety measure, but still it's not entirely right
878 * either.
880 #if 0
881 if (ia->autoconf)
882 ifa.ifra_flags |= IN6_IFF_AUTOCONF;
883 #endif
884 #if defined(__FreeBSD__) || defined(__DragonFly__)
885 if (ia->addr_flags & IN6_IFF_TENTATIVE)
886 ifa.ifra_flags |= IN6_IFF_TENTATIVE;
887 #endif
888 #ifdef IPV6_MANGETEMPADDR
889 if (ia->flags & IPV6_AF_TEMPORARY)
890 ifa.ifra_flags |= IN6_IFF_TEMPORARY;
891 #endif
893 #define ADDADDR(v, addr) { \
894 (v)->sin6_family = AF_INET6; \
895 (v)->sin6_len = sizeof(*v); \
896 (v)->sin6_addr = *(addr); \
899 ADDADDR(&ifa.ifra_addr, &ia->addr);
900 ifa_setscope(&ifa.ifra_addr, ia->iface->index);
901 ipv6_mask(&mask, ia->prefix_len);
902 ADDADDR(&ifa.ifra_prefixmask, &mask);
904 #undef ADDADDR
907 * Every BSD kernel wants to add the prefix of the address to it's
908 * list of RA received prefixes.
909 * THIS IS WRONG because there (as the comments in the kernel state)
910 * is no API for managing prefix lifetime and the kernel should not
911 * pretend it's from a RA either.
913 * The issue is that the very first assigned prefix will inherit the
914 * lifetime of the address, but any subsequent alteration of the
915 * address OR it's lifetime will not affect the prefix lifetime.
916 * As such, we cannot stop the prefix from timing out and then
917 * constantly removing the prefix route dhcpcd is capable of adding
918 * in it's absense.
920 * What we can do to mitigate the issue is to add the address with
921 * infinite lifetimes, so the prefix route will never time out.
922 * Once done, we can then set lifetimes on the address and all is good.
923 * The downside of this approach is that we need to manually remove
924 * the kernel route because it has no lifetime, but this is OK as
925 * dhcpcd will handle this too.
927 * This issue is discussed on the NetBSD mailing lists here:
928 * http://mail-index.netbsd.org/tech-net/2016/08/05/msg006044.html
930 * Fixed in NetBSD-7.99.36
931 * NOT fixed in FreeBSD - bug 195197
932 * Fixed in OpenBSD-5.9
935 #if !((defined(__NetBSD_Version__) && __NetBSD_Version__ >= 799003600) || \
936 (defined(__OpenBSD__) && OpenBSD >= 201605))
937 if (cmd == RTM_NEWADDR && !(ia->flags & IPV6_AF_ADDED)) {
938 ifa.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
939 ifa.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
940 (void)ioctl(priv->pf_inet6_fd, SIOCAIFADDR_IN6, &ifa);
942 #endif
944 #if defined(__OpenBSD__) && OpenBSD <= 201705
945 /* BUT OpenBSD older than 6.2 does not reset the address lifetime
946 * for subsequent calls...
947 * Luckily dhcpcd will remove the lease when it expires so
948 * just set an infinite lifetime, unless a temporary address. */
949 if (ifa.ifra_flags & IN6_IFF_PRIVACY) {
950 ifa.ifra_lifetime.ia6t_vltime = ia->prefix_vltime;
951 ifa.ifra_lifetime.ia6t_pltime = ia->prefix_pltime;
952 } else {
953 ifa.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
954 ifa.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
956 #else
957 ifa.ifra_lifetime.ia6t_vltime = ia->prefix_vltime;
958 ifa.ifra_lifetime.ia6t_pltime = ia->prefix_pltime;
959 #endif
961 return ioctl(priv->pf_inet6_fd,
962 cmd == RTM_DELADDR ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6, &ifa);
966 if_addrflags6(const struct interface *ifp, const struct in6_addr *addr,
967 __unused const char *alias)
969 int flags;
970 struct in6_ifreq ifr6;
971 struct priv *priv;
973 memset(&ifr6, 0, sizeof(ifr6));
974 strlcpy(ifr6.ifr_name, ifp->name, sizeof(ifr6.ifr_name));
975 ifr6.ifr_addr.sin6_family = AF_INET6;
976 ifr6.ifr_addr.sin6_addr = *addr;
977 ifa_setscope(&ifr6.ifr_addr, ifp->index);
978 priv = (struct priv *)ifp->ctx->priv;
979 if (ioctl(priv->pf_inet6_fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
980 flags = ifr6.ifr_ifru.ifru_flags6;
981 else
982 flags = -1;
983 return flags;
987 if_getlifetime6(struct ipv6_addr *ia)
989 struct in6_ifreq ifr6;
990 time_t t;
991 struct in6_addrlifetime *lifetime;
992 struct priv *priv;
994 memset(&ifr6, 0, sizeof(ifr6));
995 strlcpy(ifr6.ifr_name, ia->iface->name, sizeof(ifr6.ifr_name));
996 ifr6.ifr_addr.sin6_family = AF_INET6;
997 ifr6.ifr_addr.sin6_addr = ia->addr;
998 ifa_setscope(&ifr6.ifr_addr, ia->iface->index);
999 priv = (struct priv *)ia->iface->ctx->priv;
1000 if (ioctl(priv->pf_inet6_fd, SIOCGIFALIFETIME_IN6, &ifr6) == -1)
1001 return -1;
1002 clock_gettime(CLOCK_MONOTONIC, &ia->created);
1004 #if defined(__FreeBSD__) || defined(__DragonFly__)
1005 t = ia->created.tv_sec;
1006 #else
1007 t = time(NULL);
1008 #endif
1010 lifetime = &ifr6.ifr_ifru.ifru_lifetime;
1011 if (lifetime->ia6t_preferred)
1012 ia->prefix_pltime = (uint32_t)(lifetime->ia6t_preferred -
1013 MIN(t, lifetime->ia6t_preferred));
1014 else
1015 ia->prefix_pltime = ND6_INFINITE_LIFETIME;
1016 if (lifetime->ia6t_expire) {
1017 ia->prefix_vltime = (uint32_t)(lifetime->ia6t_expire -
1018 MIN(t, lifetime->ia6t_expire));
1019 /* Calculate the created time */
1020 ia->created.tv_sec -= lifetime->ia6t_vltime - ia->prefix_vltime;
1021 } else
1022 ia->prefix_vltime = ND6_INFINITE_LIFETIME;
1023 return 0;
1025 #endif
1027 static int
1028 if_announce(struct dhcpcd_ctx *ctx, const struct if_announcemsghdr *ifan)
1031 if (ifan->ifan_msglen < sizeof(*ifan)) {
1032 errno = EINVAL;
1033 return -1;
1036 switch(ifan->ifan_what) {
1037 case IFAN_ARRIVAL:
1038 return dhcpcd_handleinterface(ctx, 1, ifan->ifan_name);
1039 case IFAN_DEPARTURE:
1040 return dhcpcd_handleinterface(ctx, -1, ifan->ifan_name);
1043 return 0;
1046 static int
1047 if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)
1049 struct interface *ifp;
1050 int link_state;
1052 if (ifm->ifm_msglen < sizeof(*ifm)) {
1053 errno = EINVAL;
1054 return -1;
1057 if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL)
1058 return 0;
1060 switch (ifm->ifm_data.ifi_link_state) {
1061 case LINK_STATE_UNKNOWN:
1062 link_state = LINK_UNKNOWN;
1063 break;
1064 case LINK_STATE_UP:
1065 link_state = LINK_UP;
1066 break;
1067 default:
1068 link_state = LINK_DOWN;
1069 break;
1072 dhcpcd_handlecarrier(ctx, link_state,
1073 (unsigned int)ifm->ifm_flags, ifp->name);
1074 return 0;
1077 static int
1078 if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
1080 struct rt rt;
1082 if (rtm->rtm_msglen < sizeof(*rtm)) {
1083 errno = EINVAL;
1084 return -1;
1087 /* Ignore errors. */
1088 if (rtm->rtm_errno != 0)
1089 return 0;
1091 if (if_copyrt(ctx, &rt, rtm) == -1)
1092 return errno == ENOTSUP ? 0 : -1;
1094 #ifdef INET6
1096 * BSD announces host routes.
1097 * As such, we should be notified of reachability by its
1098 * existance with a hardware address.
1099 * Ensure we don't call this for a newly incomplete state.
1101 if (rt.rt_dest.sa_family == AF_INET6 &&
1102 (rt.rt_flags & RTF_HOST || rtm->rtm_type == RTM_MISS) &&
1103 !(rtm->rtm_type == RTM_ADD && !(rt.rt_dflags & RTDF_GATELINK)))
1105 bool reachable;
1107 reachable = (rtm->rtm_type == RTM_ADD ||
1108 rtm->rtm_type == RTM_CHANGE) &&
1109 rt.rt_dflags & RTDF_GATELINK;
1110 ipv6nd_neighbour(ctx, &rt.rt_ss_dest.sin6.sin6_addr, reachable);
1112 #endif
1114 if (rtm->rtm_type != RTM_MISS)
1115 rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
1116 return 0;
1119 static int
1120 if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
1122 struct interface *ifp;
1123 const struct sockaddr *rti_info[RTAX_MAX];
1124 int addrflags;
1125 pid_t pid;
1127 if (ifam->ifam_msglen < sizeof(*ifam)) {
1128 errno = EINVAL;
1129 return -1;
1131 if (~ifam->ifam_addrs & RTA_IFA)
1132 return 0;
1133 if ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL)
1134 return 0;
1136 if (get_addrs(ifam->ifam_addrs, (const char *)ifam + sizeof(*ifam),
1137 ifam->ifam_msglen - sizeof(*ifam), rti_info) == -1)
1138 return -1;
1140 #ifdef HAVE_IFAM_PID
1141 pid = ifam->ifam_pid;
1142 #else
1143 pid = 0;
1144 #endif
1146 #ifdef HAVE_IFAM_ADDRFLAGS
1147 addrflags = ifam->ifam_addrflags;
1148 #endif
1149 switch (rti_info[RTAX_IFA]->sa_family) {
1150 case AF_LINK:
1152 struct sockaddr_dl sdl;
1154 #ifdef RTM_CHGADDR
1155 if (ifam->ifam_type != RTM_CHGADDR)
1156 break;
1157 #else
1158 if (ifam->ifam_type != RTM_NEWADDR)
1159 break;
1160 #endif
1161 memcpy(&sdl, rti_info[RTAX_IFA], rti_info[RTAX_IFA]->sa_len);
1162 dhcpcd_handlehwaddr(ctx, ifp->name, CLLADDR(&sdl),sdl.sdl_alen);
1163 break;
1165 #ifdef INET
1166 case AF_INET:
1167 case 255: /* FIXME: Why 255? */
1169 const struct sockaddr_in *sin;
1170 struct in_addr addr, mask, bcast;
1172 sin = (const void *)rti_info[RTAX_IFA];
1173 addr.s_addr = sin != NULL && sin->sin_family == AF_INET ?
1174 sin->sin_addr.s_addr : INADDR_ANY;
1175 sin = (const void *)rti_info[RTAX_NETMASK];
1176 mask.s_addr = sin != NULL && sin->sin_family == AF_INET ?
1177 sin->sin_addr.s_addr : INADDR_ANY;
1178 sin = (const void *)rti_info[RTAX_BRD];
1179 bcast.s_addr = sin != NULL && sin->sin_family == AF_INET ?
1180 sin->sin_addr.s_addr : INADDR_ANY;
1182 #if defined(__NetBSD_Version__) && __NetBSD_Version__ < 800000000
1184 * NetBSD-7 and older send an invalid broadcast address.
1185 * So we need to query the actual address to get
1186 * the right one.
1189 #else
1191 * If the address was deleted, lets check if it's
1192 * a late message and it still exists (maybe modified).
1193 * If so, ignore it as deleting an address causes
1194 * dhcpcd to drop any lease to which it belongs.
1196 if (ifam->ifam_type == RTM_DELADDR) {
1197 #endif
1198 #ifdef SIOCGIFALIAS
1199 struct in_aliasreq ifra;
1201 memset(&ifra, 0, sizeof(ifra));
1202 strlcpy(ifra.ifra_name, ifp->name,
1203 sizeof(ifra.ifra_name));
1204 ifra.ifra_addr.sin_family = AF_INET;
1205 ifra.ifra_addr.sin_len = sizeof(ifra.ifra_addr);
1206 ifra.ifra_addr.sin_addr = addr;
1207 if (ioctl(ctx->pf_inet_fd, SIOCGIFALIAS, &ifra) == -1) {
1208 if (errno != ENXIO && errno != EADDRNOTAVAIL)
1209 logerr("%s: SIOCGIFALIAS", __func__);
1210 if (ifam->ifam_type != RTM_DELADDR)
1211 break;
1213 #if defined(__NetBSD_Version__) && __NetBSD_Version__ < 800000000
1214 else
1215 bcast = ifra.ifra_broadaddr.sin_addr;
1216 #endif
1217 #else
1218 #warning No SIOCGIFALIAS support
1220 * No SIOCGIFALIAS? That sucks!
1221 * This makes this call very heavy weight, but we
1222 * really need to know if the message is late or not.
1224 const struct sockaddr *sa;
1225 struct ifaddrs *ifaddrs = NULL, *ifa;
1227 sa = rti_info[RTAX_IFA];
1228 getifaddrs(&ifaddrs);
1229 for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
1230 if (ifa->ifa_addr == NULL)
1231 continue;
1232 if (sa_cmp(ifa->ifa_addr, sa) == 0 &&
1233 strcmp(ifa->ifa_name, ifp->name) == 0)
1234 break;
1236 freeifaddrs(ifaddrs);
1237 if (ifa != NULL)
1238 return 0;
1239 #endif
1242 #ifndef HAVE_IFAM_ADDRFLAGS
1243 if (ifam->ifam_type == RTM_DELADDR)
1244 addrflags = 0 ;
1245 else if ((addrflags = if_addrflags(ifp, &addr, NULL)) == -1) {
1246 if (errno != EADDRNOTAVAIL)
1247 logerr("%s: if_addrflags", __func__);
1248 break;
1250 #endif
1252 ipv4_handleifa(ctx, ifam->ifam_type, NULL, ifp->name,
1253 &addr, &mask, &bcast, addrflags, pid);
1254 break;
1256 #endif
1257 #ifdef INET6
1258 case AF_INET6:
1260 struct in6_addr addr6, mask6;
1261 const struct sockaddr_in6 *sin6;
1262 int flags;
1264 sin6 = (const void *)rti_info[RTAX_IFA];
1265 addr6 = sin6->sin6_addr;
1266 sin6 = (const void *)rti_info[RTAX_NETMASK];
1267 mask6 = sin6->sin6_addr;
1270 * If the address was deleted, lets check if it's
1271 * a late message and it still exists (maybe modified).
1272 * If so, ignore it as deleting an address causes
1273 * dhcpcd to drop any lease to which it belongs.
1275 if (ifam->ifam_type == RTM_DELADDR) {
1276 flags = if_addrflags6(ifp, &addr6, NULL);
1277 if (flags != -1)
1278 break;
1279 addrflags = 0;
1281 #ifndef HAVE_IFAM_ADDRFLAGS
1282 else if ((addrflags = if_addrflags6(ifp, &addr6, NULL)) == -1) {
1283 if (errno != EADDRNOTAVAIL)
1284 logerr("%s: if_addrflags6", __func__);
1285 break;
1287 #endif
1289 #ifdef __KAME__
1290 if (IN6_IS_ADDR_LINKLOCAL(&addr6))
1291 /* Remove the scope from the address */
1292 addr6.s6_addr[2] = addr6.s6_addr[3] = '\0';
1293 #endif
1295 ipv6_handleifa(ctx, ifam->ifam_type, NULL,
1296 ifp->name, &addr6, ipv6_prefixlen(&mask6), addrflags, pid);
1297 break;
1299 #endif
1302 return 0;
1305 static int
1306 if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
1309 if (rtm->rtm_version != RTM_VERSION)
1310 return 0;
1312 switch(rtm->rtm_type) {
1313 #ifdef RTM_IFANNOUNCE
1314 case RTM_IFANNOUNCE:
1315 return if_announce(ctx, (const void *)rtm);
1316 #endif
1317 case RTM_IFINFO:
1318 return if_ifinfo(ctx, (const void *)rtm);
1319 case RTM_ADD: /* FALLTHROUGH */
1320 case RTM_CHANGE: /* FALLTHROUGH */
1321 case RTM_DELETE: /* FALLTHROUGH */
1322 case RTM_MISS:
1323 return if_rtm(ctx, (const void *)rtm);
1324 #ifdef RTM_CHGADDR
1325 case RTM_CHGADDR: /* FALLTHROUGH */
1326 #endif
1327 case RTM_DELADDR: /* FALLTHROUGH */
1328 case RTM_NEWADDR:
1329 return if_ifa(ctx, (const void *)rtm);
1330 #ifdef RTM_DESYNC
1331 case RTM_DESYNC:
1332 dhcpcd_linkoverflow(ctx);
1333 #elif !defined(SO_RERROR)
1334 #warning cannot detect route socket overflow within kernel
1335 #endif
1338 return 0;
1341 __CTASSERT(offsetof(struct rt_msghdr, rtm_msglen) == 0);
1343 if_handlelink(struct dhcpcd_ctx *ctx)
1345 struct rtm rtm;
1346 ssize_t len;
1348 len = read(ctx->link_fd, &rtm, sizeof(rtm));
1349 if (len == -1)
1350 return -1;
1351 if (len == 0)
1352 return 0;
1353 if ((size_t)len < sizeof(rtm.hdr.rtm_msglen) ||
1354 len != rtm.hdr.rtm_msglen)
1356 errno = EINVAL;
1357 return -1;
1360 * Coverity thinks that the data could be tainted from here.
1361 * I have no idea how because the length of the data we read
1362 * is guarded by len and checked to match rtm_msglen.
1363 * The issue seems to be related to extracting the addresses
1364 * at the end of the header, but seems to have no issues with the
1365 * equivalent call in if_initrt.
1367 /* coverity[tainted_data] */
1368 return if_dispatch(ctx, &rtm.hdr);
1371 #ifndef SYS_NMLN /* OSX */
1372 # define SYS_NMLN 256
1373 #endif
1374 #ifndef HW_MACHINE_ARCH
1375 # ifdef HW_MODEL /* OpenBSD */
1376 # define HW_MACHINE_ARCH HW_MODEL
1377 # endif
1378 #endif
1380 if_machinearch(char *str, size_t len)
1382 int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
1383 char march[SYS_NMLN];
1384 size_t marchlen = sizeof(march);
1386 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
1387 march, &marchlen, NULL, 0) != 0)
1388 return -1;
1389 return snprintf(str, len, ":%s", march);
1392 #ifdef INET6
1393 #if (defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV)) || \
1394 defined(IPV6CTL_USETEMPADDR) || defined(IPV6CTL_TEMPVLTIME) || \
1395 defined(IPV6CTL_FORWARDING)
1396 #define get_inet6_sysctl(code) inet6_sysctl(code, 0, 0)
1397 #define set_inet6_sysctl(code, val) inet6_sysctl(code, val, 1)
1398 static int
1399 inet6_sysctl(int code, int val, int action)
1401 int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
1402 size_t size;
1404 mib[3] = code;
1405 size = sizeof(val);
1406 if (action) {
1407 if (sysctl(mib, sizeof(mib)/sizeof(mib[0]),
1408 NULL, 0, &val, size) == -1)
1409 return -1;
1410 return 0;
1412 if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &val, &size, NULL, 0) == -1)
1413 return -1;
1414 return val;
1416 #endif
1418 #ifdef IPV6_MANAGETEMPADDR
1419 #ifndef IPV6CTL_TEMPVLTIME
1420 #define get_inet6_sysctlbyname(code) inet6_sysctlbyname(code, 0, 0)
1421 #define set_inet6_sysctlbyname(code, val) inet6_sysctlbyname(code, val, 1)
1422 static int
1423 inet6_sysctlbyname(const char *name, int val, int action)
1425 size_t size;
1427 size = sizeof(val);
1428 if (action) {
1429 if (sysctlbyname(name, NULL, 0, &val, size) == -1)
1430 return -1;
1431 return 0;
1433 if (sysctlbyname(name, &val, &size, NULL, 0) == -1)
1434 return -1;
1435 return val;
1437 #endif
1440 ip6_use_tempaddr(__unused const char *ifname)
1442 int val;
1444 #ifdef IPV6CTL_USETEMPADDR
1445 val = get_inet6_sysctl(IPV6CTL_USETEMPADDR);
1446 #else
1447 val = get_inet6_sysctlbyname("net.inet6.ip6.use_tempaddr");
1448 #endif
1449 return val == -1 ? 0 : val;
1453 ip6_temp_preferred_lifetime(__unused const char *ifname)
1455 int val;
1457 #ifdef IPV6CTL_TEMPPLTIME
1458 val = get_inet6_sysctl(IPV6CTL_TEMPPLTIME);
1459 #else
1460 val = get_inet6_sysctlbyname("net.inet6.ip6.temppltime");
1461 #endif
1462 return val < 0 ? TEMP_PREFERRED_LIFETIME : val;
1466 ip6_temp_valid_lifetime(__unused const char *ifname)
1468 int val;
1470 #ifdef IPV6CTL_TEMPVLTIME
1471 val = get_inet6_sysctl(IPV6CTL_TEMPVLTIME);
1472 #else
1473 val = get_inet6_sysctlbyname("net.inet6.ip6.tempvltime");
1474 #endif
1475 return val < 0 ? TEMP_VALID_LIFETIME : val;
1477 #endif
1480 ip6_forwarding(__unused const char *ifname)
1482 int val;
1484 #ifdef IPV6CTL_FORWARDING
1485 val = get_inet6_sysctl(IPV6CTL_FORWARDING);
1486 #else
1487 val = get_inet6_sysctlbyname("net.inet6.ip6.forwarding");
1488 #endif
1489 return val < 0 ? 0 : val;
1492 #ifdef SIOCIFAFATTACH
1493 static int
1494 af_attach(int s, const struct interface *ifp, int af)
1496 struct if_afreq ifar;
1498 strlcpy(ifar.ifar_name, ifp->name, sizeof(ifar.ifar_name));
1499 ifar.ifar_af = af;
1500 return ioctl(s, SIOCIFAFATTACH, (void *)&ifar);
1502 #endif
1504 #ifdef SIOCGIFXFLAGS
1505 static int
1506 set_ifxflags(int s, const struct interface *ifp)
1508 struct ifreq ifr;
1509 int flags;
1511 strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
1512 if (ioctl(s, SIOCGIFXFLAGS, (void *)&ifr) == -1)
1513 return -1;
1514 flags = ifr.ifr_flags;
1515 #ifdef IFXF_NOINET6
1516 flags &= ~IFXF_NOINET6;
1517 #endif
1519 * If not doing autoconf, don't disable the kernel from doing it.
1520 * If we need to, we should have another option actively disable it.
1522 * OpenBSD moved from kernel based SLAAC to userland via slaacd(8).
1523 * It has a similar featureset to dhcpcd such as stable private
1524 * addresses, but lacks the ability to handle DNS inside the RA
1525 * which is a serious shortfall in this day and age.
1526 * Appease their user base by working alongside slaacd(8) if
1527 * dhcpcd is instructed not to do auto configuration of addresses.
1529 #if defined(ND6_IFF_ACCEPT_RTADV)
1530 #define BSD_AUTOCONF DHCPCD_IPV6RS
1531 #else
1532 #define BSD_AUTOCONF DHCPCD_IPV6RA_AUTOCONF
1533 #endif
1534 if (ifp->options->options & BSD_AUTOCONF)
1535 flags &= ~IFXF_AUTOCONF6;
1536 if (ifr.ifr_flags == flags)
1537 return 0;
1538 ifr.ifr_flags = flags;
1539 return ioctl(s, SIOCSIFXFLAGS, (void *)&ifr);
1541 #endif
1543 /* OpenBSD removed ND6 flags entirely, so we need to check for their
1544 * existance. */
1545 #if defined(ND6_IFF_AUTO_LINKLOCAL) || \
1546 defined(ND6_IFF_PERFORMNUD) || \
1547 defined(ND6_IFF_ACCEPT_RTADV) || \
1548 defined(ND6_IFF_OVERRIDE_RTADV) || \
1549 defined(ND6_IFF_IFDISABLED)
1550 #define ND6_NDI_FLAGS
1551 #endif
1553 void
1554 if_setup_inet6(const struct interface *ifp)
1556 struct priv *priv;
1557 int s;
1558 #ifdef ND6_NDI_FLAGS
1559 struct in6_ndireq nd;
1560 int flags;
1561 #endif
1563 priv = (struct priv *)ifp->ctx->priv;
1564 s = priv->pf_inet6_fd;
1566 #ifdef ND6_NDI_FLAGS
1567 memset(&nd, 0, sizeof(nd));
1568 strlcpy(nd.ifname, ifp->name, sizeof(nd.ifname));
1569 if (ioctl(s, SIOCGIFINFO_IN6, &nd) == -1)
1570 logerr("%s: SIOCGIFINFO_FLAGS", ifp->name);
1571 flags = (int)nd.ndi.flags;
1572 #endif
1574 #ifdef ND6_IFF_AUTO_LINKLOCAL
1575 /* Unlike the kernel,
1576 * dhcpcd make make a stable private address. */
1577 flags &= ~ND6_IFF_AUTO_LINKLOCAL;
1578 #endif
1580 #ifdef ND6_IFF_PERFORMNUD
1581 /* NUD is kind of essential. */
1582 flags |= ND6_IFF_PERFORMNUD;
1583 #endif
1585 #ifdef ND6_IFF_IFDISABLED
1586 /* Ensure the interface is not disabled. */
1587 flags &= ~ND6_IFF_IFDISABLED;
1588 #endif
1591 * If not doing autoconf, don't disable the kernel from doing it.
1592 * If we need to, we should have another option actively disable it.
1594 #ifdef ND6_IFF_ACCEPT_RTADV
1595 if (ifp->options->options & DHCPCD_IPV6RS)
1596 flags &= ~ND6_IFF_ACCEPT_RTADV;
1597 #ifdef ND6_IFF_OVERRIDE_RTADV
1598 if (ifp->options->options & DHCPCD_IPV6RS)
1599 flags |= ND6_IFF_OVERRIDE_RTADV;
1600 #endif
1601 #endif
1603 #ifdef ND6_NDI_FLAGS
1604 if (nd.ndi.flags != (uint32_t)flags) {
1605 nd.ndi.flags = (uint32_t)flags;
1606 if (ioctl(s, SIOCSIFINFO_FLAGS, &nd) == -1)
1607 logerr("%s: SIOCSIFINFO_FLAGS", ifp->name);
1609 #endif
1611 /* Enabling IPv6 by whatever means must be the
1612 * last action undertaken to ensure kernel RS and
1613 * LLADDR auto configuration are disabled where applicable. */
1614 #ifdef SIOCIFAFATTACH
1615 if (af_attach(s, ifp, AF_INET6) == -1)
1616 logerr("%s: af_attach", ifp->name);
1617 #endif
1619 #ifdef SIOCGIFXFLAGS
1620 if (set_ifxflags(s, ifp) == -1)
1621 logerr("%s: set_ifxflags", ifp->name);
1622 #endif
1624 #if defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV)
1625 /* If we cannot control ra per interface, disable it globally. */
1626 if (ifp->options->options & DHCPCD_IPV6RS) {
1627 int ra = get_inet6_sysctl(IPV6CTL_ACCEPT_RTADV);
1629 if (ra == -1) {
1630 if (errno != ENOENT)
1631 logerr("IPV6CTL_ACCEPT_RTADV");
1632 else if (ra != 0)
1633 if (set_inet6_sysctl(IPV6CTL_ACCEPT_RTADV, 0) == -1)
1634 logerr("IPV6CTL_ACCEPT_RTADV");
1637 #endif
1639 #if defined(IPV6CTL_ACCEPT_RTADV) || defined(ND6_IFF_ACCEPT_RTADV)
1640 /* Flush the kernel knowledge of advertised routers
1641 * and prefixes so the kernel does not expire prefixes
1642 * and default routes we are trying to own. */
1643 if (ifp->options->options & DHCPCD_IPV6RS) {
1644 struct in6_ifreq ifr;
1646 memset(&ifr, 0, sizeof(ifr));
1647 strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
1648 if (ioctl(s, SIOCSRTRFLUSH_IN6, &ifr) == -1 &&
1649 errno != ENOTSUP)
1650 logwarn("SIOCSRTRFLUSH_IN6");
1651 if (ioctl(s, SIOCSPFXFLUSH_IN6, &ifr) == -1 &&
1652 errno != ENOTSUP)
1653 logwarn("SIOCSPFXFLUSH_IN6");
1655 #endif
1657 #endif