xref: /dflybsd-src/contrib/dhcpcd/src/if-bsd.c (revision 6a6d63c5317abf314a78f8c8300ef73c2bc0c39e)
18d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI  * BSD interface driver for dhcpcd
480aa9461SRoy Marples  * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
57827cba2SAaron LI  * All rights reserved
67827cba2SAaron LI 
77827cba2SAaron LI  * Redistribution and use in source and binary forms, with or without
87827cba2SAaron LI  * modification, are permitted provided that the following conditions
97827cba2SAaron LI  * are met:
107827cba2SAaron LI  * 1. Redistributions of source code must retain the above copyright
117827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer.
127827cba2SAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
137827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer in the
147827cba2SAaron LI  *    documentation and/or other materials provided with the distribution.
157827cba2SAaron LI  *
167827cba2SAaron LI  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177827cba2SAaron LI  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187827cba2SAaron LI  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197827cba2SAaron LI  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207827cba2SAaron LI  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217827cba2SAaron LI  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227827cba2SAaron LI  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237827cba2SAaron LI  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247827cba2SAaron LI  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257827cba2SAaron LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267827cba2SAaron LI  * SUCH DAMAGE.
277827cba2SAaron LI  */
287827cba2SAaron LI 
297827cba2SAaron LI #include <sys/ioctl.h>
307827cba2SAaron LI #include <sys/param.h>
317827cba2SAaron LI #include <sys/socket.h>
327827cba2SAaron LI #include <sys/stat.h>
337827cba2SAaron LI #include <sys/sysctl.h>
347827cba2SAaron LI #include <sys/time.h>
357827cba2SAaron LI #include <sys/types.h>
367827cba2SAaron LI #include <sys/uio.h>
377827cba2SAaron LI #include <sys/utsname.h>
387827cba2SAaron LI 
397827cba2SAaron LI #include "config.h"
407827cba2SAaron LI 
417827cba2SAaron LI #include <arpa/inet.h>
427827cba2SAaron LI #include <net/bpf.h>
437827cba2SAaron LI #include <net/if.h>
447827cba2SAaron LI #include <net/if_dl.h>
457827cba2SAaron LI #include <net/if_media.h>
467827cba2SAaron LI #include <net/route.h>
477827cba2SAaron LI #include <netinet/if_ether.h>
487827cba2SAaron LI #include <netinet/in.h>
497827cba2SAaron LI #include <netinet/in_var.h>
507827cba2SAaron LI #include <netinet6/in6_var.h>
517827cba2SAaron LI #include <netinet6/nd6.h>
527827cba2SAaron LI #ifdef __NetBSD__
537827cba2SAaron LI #include <net/if_vlanvar.h> /* Needs netinet/if_ether.h */
548d36e1dfSRoy Marples #elif defined(__DragonFly__)
558d36e1dfSRoy Marples #include <net/vlan/if_vlan_var.h>
567827cba2SAaron LI #else
577827cba2SAaron LI #include <net/if_vlan_var.h>
587827cba2SAaron LI #endif
597827cba2SAaron LI #ifdef __DragonFly__
607827cba2SAaron LI #  include <netproto/802_11/ieee80211_ioctl.h>
617827cba2SAaron LI #else
627827cba2SAaron LI #  include <net80211/ieee80211.h>
637827cba2SAaron LI #  include <net80211/ieee80211_ioctl.h>
647827cba2SAaron LI #endif
657827cba2SAaron LI 
667827cba2SAaron LI #include <assert.h>
677827cba2SAaron LI #include <errno.h>
687827cba2SAaron LI #include <fcntl.h>
697827cba2SAaron LI #include <fnmatch.h>
707827cba2SAaron LI #include <paths.h>
717827cba2SAaron LI #include <stddef.h>
727827cba2SAaron LI #include <stdio.h>
737827cba2SAaron LI #include <stdlib.h>
747827cba2SAaron LI #include <string.h>
757827cba2SAaron LI #include <unistd.h>
767827cba2SAaron LI 
777827cba2SAaron LI #if defined(OpenBSD) && OpenBSD >= 201411
787827cba2SAaron LI /* OpenBSD dropped the global setting from sysctl but left the #define
797827cba2SAaron LI  * which causes a EPERM error when trying to use it.
807827cba2SAaron LI  * I think both the error and keeping the define are wrong, so we #undef it. */
817827cba2SAaron LI #undef IPV6CTL_ACCEPT_RTADV
827827cba2SAaron LI #endif
837827cba2SAaron LI 
847827cba2SAaron LI #include "common.h"
857827cba2SAaron LI #include "dhcp.h"
867827cba2SAaron LI #include "if.h"
877827cba2SAaron LI #include "if-options.h"
887827cba2SAaron LI #include "ipv4.h"
897827cba2SAaron LI #include "ipv4ll.h"
907827cba2SAaron LI #include "ipv6.h"
917827cba2SAaron LI #include "ipv6nd.h"
927827cba2SAaron LI #include "logerr.h"
936e63cc1fSRoy Marples #include "privsep.h"
947827cba2SAaron LI #include "route.h"
957827cba2SAaron LI #include "sa.h"
967827cba2SAaron LI 
977827cba2SAaron LI #ifndef RT_ROUNDUP
987827cba2SAaron LI #define RT_ROUNDUP(a)							      \
997827cba2SAaron LI 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
1007827cba2SAaron LI #define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
1017827cba2SAaron LI #endif
1027827cba2SAaron LI 
103acd7a309SRoy Marples /* Ignore these interface names which look like ethernet but are virtual or
104acd7a309SRoy Marples  * just won't work without explicit configuration. */
1051b3b16a2SRoy Marples static const char * const ifnames_ignore[] = {
1061b3b16a2SRoy Marples 	"bridge",
10780aa9461SRoy Marples 	"epair",	/* Virtual patch cable */
1081b3b16a2SRoy Marples 	"fwe",		/* Firewire */
109acd7a309SRoy Marples 	"fwip",		/* Firewire */
1101b3b16a2SRoy Marples 	"tap",
111a0d9933aSRoy Marples 	"vether",
1126e63cc1fSRoy Marples 	"xvif",		/* XEN DOM0 -> guest interface */
1131b3b16a2SRoy Marples 	NULL
1141b3b16a2SRoy Marples };
1151b3b16a2SRoy Marples 
1168d36e1dfSRoy Marples struct rtm
1178d36e1dfSRoy Marples {
1188d36e1dfSRoy Marples 	struct rt_msghdr hdr;
1198d36e1dfSRoy Marples 	char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
1208d36e1dfSRoy Marples };
1218d36e1dfSRoy Marples 
1227827cba2SAaron LI int
os_init(void)123a0d9933aSRoy Marples os_init(void)
124a0d9933aSRoy Marples {
125a0d9933aSRoy Marples 	return 0;
126a0d9933aSRoy Marples }
127a0d9933aSRoy Marples 
128a0d9933aSRoy Marples int
if_init(__unused struct interface * iface)1297827cba2SAaron LI if_init(__unused struct interface *iface)
1307827cba2SAaron LI {
1317827cba2SAaron LI 	/* BSD promotes secondary address by default */
1327827cba2SAaron LI 	return 0;
1337827cba2SAaron LI }
1347827cba2SAaron LI 
1357827cba2SAaron LI int
if_conf(__unused struct interface * iface)1367827cba2SAaron LI if_conf(__unused struct interface *iface)
1377827cba2SAaron LI {
1387827cba2SAaron LI 	/* No extra checks needed on BSD */
1397827cba2SAaron LI 	return 0;
1407827cba2SAaron LI }
1417827cba2SAaron LI 
1427827cba2SAaron LI int
if_opensockets_os(struct dhcpcd_ctx * ctx)1437827cba2SAaron LI if_opensockets_os(struct dhcpcd_ctx *ctx)
1447827cba2SAaron LI {
1457827cba2SAaron LI 	struct priv *priv;
1468d36e1dfSRoy Marples 	int n;
1477827cba2SAaron LI #if defined(RO_MSGFILTER) || defined(ROUTE_MSGFILTER)
1487827cba2SAaron LI 	unsigned char msgfilter[] = {
1497827cba2SAaron LI 	    RTM_IFINFO,
1507827cba2SAaron LI #ifdef RTM_IFANNOUNCE
1517827cba2SAaron LI 	    RTM_IFANNOUNCE,
1527827cba2SAaron LI #endif
1538d36e1dfSRoy Marples 	    RTM_ADD, RTM_CHANGE, RTM_DELETE, RTM_MISS,
1547827cba2SAaron LI #ifdef RTM_CHGADDR
1557827cba2SAaron LI 	    RTM_CHGADDR,
1567827cba2SAaron LI #endif
157f3744ac9SRoy Marples #ifdef RTM_DESYNC
158f3744ac9SRoy Marples 	    RTM_DESYNC,
159f3744ac9SRoy Marples #endif
1607827cba2SAaron LI 	    RTM_NEWADDR, RTM_DELADDR
1617827cba2SAaron LI 	};
1627827cba2SAaron LI #ifdef ROUTE_MSGFILTER
1637827cba2SAaron LI 	unsigned int i, msgfilter_mask;
1647827cba2SAaron LI #endif
1657827cba2SAaron LI #endif
1667827cba2SAaron LI 
1677827cba2SAaron LI 	if ((priv = malloc(sizeof(*priv))) == NULL)
1687827cba2SAaron LI 		return -1;
1697827cba2SAaron LI 	ctx->priv = priv;
1707827cba2SAaron LI 
1717827cba2SAaron LI #ifdef INET6
1727827cba2SAaron LI 	priv->pf_inet6_fd = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
1737827cba2SAaron LI 	/* Don't return an error so we at least work on kernels witout INET6
1747827cba2SAaron LI 	 * even though we expect INET6 support.
1757827cba2SAaron LI 	 * We will fail noisily elsewhere anyway. */
176f3744ac9SRoy Marples #ifdef PRIVSEP_RIGHTS
177f3744ac9SRoy Marples 	if (priv->pf_inet6_fd != -1 && IN_PRIVSEP(ctx))
178f3744ac9SRoy Marples 		ps_rights_limit_ioctl(priv->pf_inet6_fd);
179f3744ac9SRoy Marples #endif
1807827cba2SAaron LI #endif
1817827cba2SAaron LI 
182d4fb1e02SRoy Marples 	ctx->link_fd = xsocket(PF_ROUTE, SOCK_RAW | SOCK_CXNB, AF_UNSPEC);
1837827cba2SAaron LI 	if (ctx->link_fd == -1)
1847827cba2SAaron LI 		return -1;
1857827cba2SAaron LI 
186d4fb1e02SRoy Marples #ifdef SO_RERROR
187d4fb1e02SRoy Marples 	n = 1;
188d4fb1e02SRoy Marples 	if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RERROR, &n,sizeof(n)) == -1)
189d4fb1e02SRoy Marples 		logerr("%s: SO_RERROR", __func__);
190d4fb1e02SRoy Marples #endif
191d4fb1e02SRoy Marples 
1928d36e1dfSRoy Marples 	/* Ignore our own route(4) messages.
1938d36e1dfSRoy Marples 	 * Sadly there is no way of doing this for route(4) messages
1948d36e1dfSRoy Marples 	 * generated from addresses we add/delete. */
1958d36e1dfSRoy Marples 	n = 0;
1968d36e1dfSRoy Marples 	if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_USELOOPBACK,
1978d36e1dfSRoy Marples 	    &n, sizeof(n)) == -1)
1988d36e1dfSRoy Marples 		logerr("%s: SO_USELOOPBACK", __func__);
1998d36e1dfSRoy Marples 
200*54175cefSRoy Marples #ifdef PRIVSEP
201*54175cefSRoy Marples 	if (ctx->options & DHCPCD_PRIVSEPROOT) {
202*54175cefSRoy Marples 		/* We only want to write to this socket, so set
203*54175cefSRoy Marples 		 * a small as possible buffer size. */
204*54175cefSRoy Marples 		socklen_t smallbuf = 1;
205*54175cefSRoy Marples 
206*54175cefSRoy Marples 		if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RCVBUF,
207*54175cefSRoy Marples 		    &smallbuf, (socklen_t)sizeof(smallbuf)) == -1)
208*54175cefSRoy Marples 			logerr("%s: setsockopt(SO_RCVBUF)", __func__);
209*54175cefSRoy Marples 	}
210*54175cefSRoy Marples #endif
211*54175cefSRoy Marples 
2127827cba2SAaron LI #if defined(RO_MSGFILTER)
2137827cba2SAaron LI 	if (setsockopt(ctx->link_fd, PF_ROUTE, RO_MSGFILTER,
2147827cba2SAaron LI 	    &msgfilter, sizeof(msgfilter)) == -1)
2157827cba2SAaron LI 		logerr(__func__);
2167827cba2SAaron LI #elif defined(ROUTE_MSGFILTER)
2177827cba2SAaron LI 	/* Convert the array into a bitmask. */
2187827cba2SAaron LI 	msgfilter_mask = 0;
2197827cba2SAaron LI 	for (i = 0; i < __arraycount(msgfilter); i++)
2207827cba2SAaron LI 		msgfilter_mask |= ROUTE_FILTER(msgfilter[i]);
2217827cba2SAaron LI 	if (setsockopt(ctx->link_fd, PF_ROUTE, ROUTE_MSGFILTER,
2227827cba2SAaron LI 	    &msgfilter_mask, sizeof(msgfilter_mask)) == -1)
2237827cba2SAaron LI 		logerr(__func__);
2248d36e1dfSRoy Marples #else
2258d36e1dfSRoy Marples #warning kernel does not support route message filtering
2267827cba2SAaron LI #endif
2277827cba2SAaron LI 
228a0d9933aSRoy Marples #ifdef PRIVSEP_RIGHTS
229a0d9933aSRoy Marples 	/* We need to getsockopt for SO_RCVBUF and
230a0d9933aSRoy Marples 	 * setsockopt for RO_MISSFILTER. */
231a0d9933aSRoy Marples 	if (IN_PRIVSEP(ctx))
232a0d9933aSRoy Marples 		ps_rights_limit_fd_sockopt(ctx->link_fd);
233a0d9933aSRoy Marples #endif
234a0d9933aSRoy Marples 
23580aa9461SRoy Marples #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
236*54175cefSRoy Marples 	priv->pf_link_fd = xsocket(PF_LINK, SOCK_DGRAM, 0);
23780aa9461SRoy Marples 	if (priv->pf_link_fd == -1)
23880aa9461SRoy Marples 		logerr("%s: socket(PF_LINK)", __func__);
23980aa9461SRoy Marples #endif
2407827cba2SAaron LI 	return 0;
2417827cba2SAaron LI }
2427827cba2SAaron LI 
2437827cba2SAaron LI void
if_closesockets_os(struct dhcpcd_ctx * ctx)2447827cba2SAaron LI if_closesockets_os(struct dhcpcd_ctx *ctx)
2457827cba2SAaron LI {
2467827cba2SAaron LI 	struct priv *priv;
2477827cba2SAaron LI 
2487827cba2SAaron LI 	priv = (struct priv *)ctx->priv;
249*54175cefSRoy Marples 	if (priv == NULL)
250*54175cefSRoy Marples 		return;
251*54175cefSRoy Marples 
252f3744ac9SRoy Marples #ifdef INET6
253*54175cefSRoy Marples 	if (priv->pf_inet6_fd != -1) {
2547827cba2SAaron LI 		close(priv->pf_inet6_fd);
255*54175cefSRoy Marples 		priv->pf_inet6_fd = -1;
256*54175cefSRoy Marples 	}
257f3744ac9SRoy Marples #endif
25880aa9461SRoy Marples #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
259*54175cefSRoy Marples 	if (priv->pf_link_fd != -1) {
26080aa9461SRoy Marples 		close(priv->pf_link_fd);
261*54175cefSRoy Marples 		priv->pf_link_fd = -1;
262*54175cefSRoy Marples 	}
26380aa9461SRoy Marples #endif
2646e63cc1fSRoy Marples 	free(priv);
2656e63cc1fSRoy Marples 	ctx->priv = NULL;
2660a68f8d2SRoy Marples 	free(ctx->rt_missfilter);
2676e63cc1fSRoy Marples }
2686e63cc1fSRoy Marples 
2696e63cc1fSRoy Marples #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
2706e63cc1fSRoy Marples static int
if_ioctllink(struct dhcpcd_ctx * ctx,unsigned long req,void * data,size_t len)2716e63cc1fSRoy Marples if_ioctllink(struct dhcpcd_ctx *ctx, unsigned long req, void *data, size_t len)
2726e63cc1fSRoy Marples {
27380aa9461SRoy Marples 	struct priv *priv = (struct priv *)ctx->priv;
2746e63cc1fSRoy Marples 
2756e63cc1fSRoy Marples #ifdef PRIVSEP
2766e63cc1fSRoy Marples 	if (ctx->options & DHCPCD_PRIVSEP)
2776e63cc1fSRoy Marples 		return (int)ps_root_ioctllink(ctx, req, data, len);
2786e63cc1fSRoy Marples #endif
2796e63cc1fSRoy Marples 
28080aa9461SRoy Marples 	return ioctl(priv->pf_link_fd, req, data, len);
2816e63cc1fSRoy Marples }
2826e63cc1fSRoy Marples #endif
2836e63cc1fSRoy Marples 
2846e63cc1fSRoy Marples int
if_setmac(struct interface * ifp,void * mac,uint8_t maclen)2856e63cc1fSRoy Marples if_setmac(struct interface *ifp, void *mac, uint8_t maclen)
2866e63cc1fSRoy Marples {
2876e63cc1fSRoy Marples 
2886e63cc1fSRoy Marples 	if (ifp->hwlen != maclen) {
2896e63cc1fSRoy Marples 		errno = EINVAL;
2906e63cc1fSRoy Marples 		return -1;
2916e63cc1fSRoy Marples 	}
2926e63cc1fSRoy Marples 
2936e63cc1fSRoy Marples #if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
2946e63cc1fSRoy Marples 	struct if_laddrreq iflr = { .flags = IFLR_ACTIVE };
2956e63cc1fSRoy Marples 	struct sockaddr_dl *sdl = satosdl(&iflr.addr);
2966e63cc1fSRoy Marples 	int retval;
2976e63cc1fSRoy Marples 
2986e63cc1fSRoy Marples 	strlcpy(iflr.iflr_name, ifp->name, sizeof(iflr.iflr_name));
2996e63cc1fSRoy Marples 	sdl->sdl_family = AF_LINK;
3006e63cc1fSRoy Marples 	sdl->sdl_len = sizeof(*sdl);
3016e63cc1fSRoy Marples 	sdl->sdl_alen = maclen;
3026e63cc1fSRoy Marples 	memcpy(LLADDR(sdl), mac, maclen);
3036e63cc1fSRoy Marples 	retval = if_ioctllink(ifp->ctx, SIOCALIFADDR, &iflr, sizeof(iflr));
3046e63cc1fSRoy Marples 
3056e63cc1fSRoy Marples 	/* Try and remove the old address */
3066e63cc1fSRoy Marples 	memcpy(LLADDR(sdl), ifp->hwaddr, ifp->hwlen);
3076e63cc1fSRoy Marples 	if_ioctllink(ifp->ctx, SIOCDLIFADDR, &iflr, sizeof(iflr));
3086e63cc1fSRoy Marples 
3096e63cc1fSRoy Marples 	return retval;
3106e63cc1fSRoy Marples #else
3116e63cc1fSRoy Marples 	struct ifreq ifr = {
3126e63cc1fSRoy Marples 		.ifr_addr.sa_family = AF_LINK,
3136e63cc1fSRoy Marples 		.ifr_addr.sa_len = maclen,
3146e63cc1fSRoy Marples 	};
3156e63cc1fSRoy Marples 
3166e63cc1fSRoy Marples 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
3176e63cc1fSRoy Marples 	memcpy(ifr.ifr_addr.sa_data, mac, maclen);
3186e63cc1fSRoy Marples 	return if_ioctl(ifp->ctx, SIOCSIFLLADDR, &ifr, sizeof(ifr));
3196e63cc1fSRoy Marples #endif
3207827cba2SAaron LI }
3217827cba2SAaron LI 
3221b3b16a2SRoy Marples static bool
if_ignore1(const char * drvname)3231b3b16a2SRoy Marples if_ignore1(const char *drvname)
3241b3b16a2SRoy Marples {
3251b3b16a2SRoy Marples 	const char * const *p;
3261b3b16a2SRoy Marples 
3271b3b16a2SRoy Marples 	for (p = ifnames_ignore; *p; p++) {
3281b3b16a2SRoy Marples 		if (strcmp(*p, drvname) == 0)
3291b3b16a2SRoy Marples 			return true;
3301b3b16a2SRoy Marples 	}
3311b3b16a2SRoy Marples 	return false;
3321b3b16a2SRoy Marples }
3331b3b16a2SRoy Marples 
3347f8103cdSRoy Marples #ifdef SIOCGIFGROUP
3357f8103cdSRoy Marples int
if_ignoregroup(int s,const char * ifname)3367f8103cdSRoy Marples if_ignoregroup(int s, const char *ifname)
3377f8103cdSRoy Marples {
3387f8103cdSRoy Marples 	struct ifgroupreq ifgr = { .ifgr_len = 0 };
3397f8103cdSRoy Marples 	struct ifg_req *ifg;
3407f8103cdSRoy Marples 	size_t ifg_len;
3417f8103cdSRoy Marples 
3427f8103cdSRoy Marples 	/* Sadly it is possible to remove the device name
3437f8103cdSRoy Marples 	 * from the interface groups, but hopefully this
3447f8103cdSRoy Marples 	 * will be very unlikely.... */
3457f8103cdSRoy Marples 
3467f8103cdSRoy Marples 	strlcpy(ifgr.ifgr_name, ifname, sizeof(ifgr.ifgr_name));
3477f8103cdSRoy Marples 	if (ioctl(s, SIOCGIFGROUP, &ifgr) == -1 ||
3487f8103cdSRoy Marples 	    (ifgr.ifgr_groups = malloc(ifgr.ifgr_len)) == NULL ||
3497f8103cdSRoy Marples 	    ioctl(s, SIOCGIFGROUP, &ifgr) == -1)
3507f8103cdSRoy Marples 	{
3517f8103cdSRoy Marples 		logerr(__func__);
3527f8103cdSRoy Marples 		return -1;
3537f8103cdSRoy Marples 	}
3547f8103cdSRoy Marples 
3557f8103cdSRoy Marples 	for (ifg = ifgr.ifgr_groups, ifg_len = ifgr.ifgr_len;
3567f8103cdSRoy Marples 	     ifg && ifg_len >= sizeof(*ifg);
3577f8103cdSRoy Marples 	     ifg++, ifg_len -= sizeof(*ifg))
3587f8103cdSRoy Marples 	{
3597f8103cdSRoy Marples 		if (if_ignore1(ifg->ifgrq_group))
3607f8103cdSRoy Marples 			return 1;
3617f8103cdSRoy Marples 	}
3627f8103cdSRoy Marples 	return 0;
3637f8103cdSRoy Marples }
3647f8103cdSRoy Marples #endif
3657f8103cdSRoy Marples 
3661b3b16a2SRoy Marples bool
if_ignore(struct dhcpcd_ctx * ctx,const char * ifname)3671b3b16a2SRoy Marples if_ignore(struct dhcpcd_ctx *ctx, const char *ifname)
3681b3b16a2SRoy Marples {
3691b3b16a2SRoy Marples 	struct if_spec spec;
3701b3b16a2SRoy Marples 
3711b3b16a2SRoy Marples 	if (if_nametospec(ifname, &spec) != 0)
3721b3b16a2SRoy Marples 		return false;
3731b3b16a2SRoy Marples 
3741b3b16a2SRoy Marples 	if (if_ignore1(spec.drvname))
3751b3b16a2SRoy Marples 		return true;
3761b3b16a2SRoy Marples 
3771b3b16a2SRoy Marples #ifdef SIOCGIFGROUP
3787f8103cdSRoy Marples #if defined(PRIVSEP) && defined(HAVE_PLEDGE)
3797f8103cdSRoy Marples 	if (IN_PRIVSEP(ctx))
3807f8103cdSRoy Marples 		return ps_root_ifignoregroup(ctx, ifname) == 1 ? true : false;
381d4fb1e02SRoy Marples #endif
3827f8103cdSRoy Marples 	else
3837f8103cdSRoy Marples 		return if_ignoregroup(ctx->pf_inet_fd, ifname) == 1 ?
3847f8103cdSRoy Marples 		    true : false;
3851b3b16a2SRoy Marples #else
3861b3b16a2SRoy Marples 	UNUSED(ctx);
3871b3b16a2SRoy Marples 	return false;
3887f8103cdSRoy Marples #endif
3891b3b16a2SRoy Marples }
3901b3b16a2SRoy Marples 
if_indirect_ioctl(struct dhcpcd_ctx * ctx,const char * ifname,unsigned long cmd,void * data,size_t len)391a0d9933aSRoy Marples static int if_indirect_ioctl(struct dhcpcd_ctx *ctx,
392a0d9933aSRoy Marples     const char *ifname, unsigned long cmd, void *data, size_t len)
3938d36e1dfSRoy Marples {
394a0d9933aSRoy Marples 	struct ifreq ifr = { .ifr_flags = 0 };
3958d36e1dfSRoy Marples 
396a0d9933aSRoy Marples #if defined(PRIVSEP) && (defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE))
397a0d9933aSRoy Marples 	if (IN_PRIVSEP(ctx))
398a0d9933aSRoy Marples 		return (int)ps_root_indirectioctl(ctx, cmd, ifname, data, len);
399a0d9933aSRoy Marples #else
400a0d9933aSRoy Marples 	UNUSED(len);
401a0d9933aSRoy Marples #endif
402a0d9933aSRoy Marples 
403a0d9933aSRoy Marples 	strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
404a0d9933aSRoy Marples 	ifr.ifr_data = data;
405a0d9933aSRoy Marples 	return ioctl(ctx->pf_inet_fd, cmd, &ifr);
406a0d9933aSRoy Marples }
407a0d9933aSRoy Marples 
408a0d9933aSRoy Marples int
if_carrier(struct interface * ifp,const void * ifadata)40993ddca5eSRoy Marples if_carrier(struct interface *ifp, const void *ifadata)
410a0d9933aSRoy Marples {
411a0d9933aSRoy Marples 	const struct if_data *ifi = ifadata;
412a0d9933aSRoy Marples 
413a0d9933aSRoy Marples 	/*
414a0d9933aSRoy Marples 	 * Every BSD returns this and it is the sole source of truth.
415a0d9933aSRoy Marples 	 * Not all BSD's support SIOCGIFDATA and not all interfaces
416a0d9933aSRoy Marples 	 * support SIOCGIFMEDIA.
417a0d9933aSRoy Marples 	 */
418a0d9933aSRoy Marples 	assert(ifadata != NULL);
419a0d9933aSRoy Marples 
420a0d9933aSRoy Marples 	if (ifi->ifi_link_state >= LINK_STATE_UP)
421a0d9933aSRoy Marples 		return LINK_UP;
42293ddca5eSRoy Marples 	if (ifi->ifi_link_state == LINK_STATE_UNKNOWN) {
42393ddca5eSRoy Marples 		/*
42493ddca5eSRoy Marples 		 * Work around net80211 issues in some BSDs.
42593ddca5eSRoy Marples 		 * Wireless MUST support link state change.
42693ddca5eSRoy Marples 		 */
42793ddca5eSRoy Marples 		if (ifp->wireless)
42893ddca5eSRoy Marples 			return LINK_DOWN;
429cc34ba0cSRoy Marples 		return LINK_UNKNOWN;
43093ddca5eSRoy Marples 	}
431a0d9933aSRoy Marples 	return LINK_DOWN;
4328d36e1dfSRoy Marples }
4338d36e1dfSRoy Marples 
4340aaf6155SRoy Marples bool
if_roaming(struct interface * ifp)4350aaf6155SRoy Marples if_roaming(struct interface *ifp)
4360aaf6155SRoy Marples {
4370aaf6155SRoy Marples 
4380aaf6155SRoy Marples /* Check for NetBSD as a safety measure.
4390aaf6155SRoy Marples  * If other BSD's gain IN_IFF_TENTATIVE check they re-do DAD
4400aaf6155SRoy Marples  * when the carrier comes up again. */
4410aaf6155SRoy Marples #if defined(IN_IFF_TENTATIVE) && defined(__NetBSD__)
4420aaf6155SRoy Marples 	return ifp->flags & IFF_UP && ifp->carrier == LINK_DOWN;
4430aaf6155SRoy Marples #else
4440aaf6155SRoy Marples 	UNUSED(ifp);
4450aaf6155SRoy Marples 	return false;
4460aaf6155SRoy Marples #endif
4470aaf6155SRoy Marples }
4480aaf6155SRoy Marples 
4497827cba2SAaron LI static void
if_linkaddr(struct sockaddr_dl * sdl,const struct interface * ifp)4507827cba2SAaron LI if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp)
4517827cba2SAaron LI {
4527827cba2SAaron LI 
4537827cba2SAaron LI 	memset(sdl, 0, sizeof(*sdl));
4547827cba2SAaron LI 	sdl->sdl_family = AF_LINK;
4557827cba2SAaron LI 	sdl->sdl_len = sizeof(*sdl);
4567827cba2SAaron LI 	sdl->sdl_nlen = sdl->sdl_alen = sdl->sdl_slen = 0;
4577827cba2SAaron LI 	sdl->sdl_index = (unsigned short)ifp->index;
4587827cba2SAaron LI }
4597827cba2SAaron LI 
4607827cba2SAaron LI static int
if_getssid1(struct dhcpcd_ctx * ctx,const char * ifname,void * ssid)461d4fb1e02SRoy Marples if_getssid1(struct dhcpcd_ctx *ctx, const char *ifname, void *ssid)
4627827cba2SAaron LI {
4637827cba2SAaron LI 	int retval = -1;
4647827cba2SAaron LI #if defined(SIOCG80211NWID)
4657827cba2SAaron LI 	struct ieee80211_nwid nwid;
4667827cba2SAaron LI #elif defined(IEEE80211_IOC_SSID)
4677827cba2SAaron LI 	struct ieee80211req ireq;
4687827cba2SAaron LI 	char nwid[IEEE80211_NWID_LEN];
4697827cba2SAaron LI #endif
4707827cba2SAaron LI 
4717827cba2SAaron LI #if defined(SIOCG80211NWID) /* NetBSD */
4727827cba2SAaron LI 	memset(&nwid, 0, sizeof(nwid));
473d4fb1e02SRoy Marples 	if (if_indirect_ioctl(ctx, ifname, SIOCG80211NWID,
474d4fb1e02SRoy Marples 	    &nwid, sizeof(nwid)) == 0)
475d4fb1e02SRoy Marples 	{
4767827cba2SAaron LI 		if (ssid == NULL)
4777827cba2SAaron LI 			retval = nwid.i_len;
4787827cba2SAaron LI 		else if (nwid.i_len > IF_SSIDLEN)
4797827cba2SAaron LI 			errno = ENOBUFS;
4807827cba2SAaron LI 		else {
4817827cba2SAaron LI 			retval = nwid.i_len;
4827827cba2SAaron LI 			memcpy(ssid, nwid.i_nwid, nwid.i_len);
4837827cba2SAaron LI 		}
4847827cba2SAaron LI 	}
4857827cba2SAaron LI #elif defined(IEEE80211_IOC_SSID) /* FreeBSD */
4867827cba2SAaron LI 	memset(&ireq, 0, sizeof(ireq));
4877827cba2SAaron LI 	strlcpy(ireq.i_name, ifname, sizeof(ireq.i_name));
4887827cba2SAaron LI 	ireq.i_type = IEEE80211_IOC_SSID;
4897827cba2SAaron LI 	ireq.i_val = -1;
4907827cba2SAaron LI 	memset(nwid, 0, sizeof(nwid));
4917827cba2SAaron LI 	ireq.i_data = &nwid;
492d4fb1e02SRoy Marples 	if (ioctl(ctx->pf_inet_fd, SIOCG80211, &ireq) == 0) {
4937827cba2SAaron LI 		if (ssid == NULL)
4947827cba2SAaron LI 			retval = ireq.i_len;
4957827cba2SAaron LI 		else if (ireq.i_len > IF_SSIDLEN)
4967827cba2SAaron LI 			errno = ENOBUFS;
4977827cba2SAaron LI 		else  {
4987827cba2SAaron LI 			retval = ireq.i_len;
4997827cba2SAaron LI 			memcpy(ssid, nwid, ireq.i_len);
5007827cba2SAaron LI 		}
5017827cba2SAaron LI 	}
5027827cba2SAaron LI #else
5037827cba2SAaron LI 	errno = ENOSYS;
5047827cba2SAaron LI #endif
5057827cba2SAaron LI 
5067827cba2SAaron LI 	return retval;
5077827cba2SAaron LI }
5087827cba2SAaron LI 
5097827cba2SAaron LI int
if_getssid(struct interface * ifp)5107827cba2SAaron LI if_getssid(struct interface *ifp)
5117827cba2SAaron LI {
5127827cba2SAaron LI 	int r;
5137827cba2SAaron LI 
514d4fb1e02SRoy Marples 	r = if_getssid1(ifp->ctx, ifp->name, ifp->ssid);
5157827cba2SAaron LI 	if (r != -1)
5167827cba2SAaron LI 		ifp->ssid_len = (unsigned int)r;
5177827cba2SAaron LI 	else
5187827cba2SAaron LI 		ifp->ssid_len = 0;
5197827cba2SAaron LI 	ifp->ssid[ifp->ssid_len] = '\0';
5207827cba2SAaron LI 	return r;
5217827cba2SAaron LI }
5227827cba2SAaron LI 
5237827cba2SAaron LI /*
5247827cba2SAaron LI  * FreeBSD allows for Virtual Access Points
5257827cba2SAaron LI  * We need to check if the interface is a Virtual Interface Master
5267827cba2SAaron LI  * and if so, don't use it.
5277827cba2SAaron LI  * This check is made by virtue of being a IEEE80211 device but
5287827cba2SAaron LI  * returning the SSID gives an error.
5297827cba2SAaron LI  */
5307827cba2SAaron LI int
if_vimaster(struct dhcpcd_ctx * ctx,const char * ifname)531d4fb1e02SRoy Marples if_vimaster(struct dhcpcd_ctx *ctx, const char *ifname)
5327827cba2SAaron LI {
5337827cba2SAaron LI 	int r;
534d4fb1e02SRoy Marples 	struct ifmediareq ifmr = { .ifm_active = 0 };
5357827cba2SAaron LI 
5367827cba2SAaron LI 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
5377827cba2SAaron LI 	r = ioctl(ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr);
5387827cba2SAaron LI 	if (r == -1)
5397827cba2SAaron LI 		return -1;
5407827cba2SAaron LI 	if (ifmr.ifm_status & IFM_AVALID &&
5417827cba2SAaron LI 	    IFM_TYPE(ifmr.ifm_active) == IFM_IEEE80211)
5427827cba2SAaron LI 	{
543d4fb1e02SRoy Marples 		if (if_getssid1(ctx, ifname, NULL) == -1)
5447827cba2SAaron LI 			return 1;
5457827cba2SAaron LI 	}
5467827cba2SAaron LI 	return 0;
5477827cba2SAaron LI }
5487827cba2SAaron LI 
5497827cba2SAaron LI unsigned short
if_vlanid(const struct interface * ifp)5507827cba2SAaron LI if_vlanid(const struct interface *ifp)
5517827cba2SAaron LI {
5527827cba2SAaron LI #ifdef SIOCGETVLAN
553d4fb1e02SRoy Marples 	struct vlanreq vlr = { .vlr_tag = 0 };
5547827cba2SAaron LI 
555d4fb1e02SRoy Marples 	if (if_indirect_ioctl(ifp->ctx, ifp->name, SIOCGETVLAN,
556d4fb1e02SRoy Marples 	    &vlr, sizeof(vlr)) != 0)
5577827cba2SAaron LI 		return 0; /* 0 means no VLANID */
5587827cba2SAaron LI 	return vlr.vlr_tag;
5597827cba2SAaron LI #elif defined(SIOCGVNETID)
560d4fb1e02SRoy Marples 	struct ifreq ifr = { .ifr_vnetid = 0 };
5617827cba2SAaron LI 
5627827cba2SAaron LI 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
5637827cba2SAaron LI 	if (ioctl(ifp->ctx->pf_inet_fd, SIOCGVNETID, &ifr) != 0)
5647827cba2SAaron LI 		return 0; /* 0 means no VLANID */
5657827cba2SAaron LI 	return ifr.ifr_vnetid;
5667827cba2SAaron LI #else
5677827cba2SAaron LI 	UNUSED(ifp);
5687827cba2SAaron LI 	return 0; /* 0 means no VLANID */
5697827cba2SAaron LI #endif
5707827cba2SAaron LI }
5717827cba2SAaron LI 
5728d36e1dfSRoy Marples static int
get_addrs(int type,const void * data,size_t data_len,const struct sockaddr ** sa)5738d36e1dfSRoy Marples get_addrs(int type, const void *data, size_t data_len,
5748d36e1dfSRoy Marples     const struct sockaddr **sa)
5757827cba2SAaron LI {
5768d36e1dfSRoy Marples 	const char *cp, *ep;
5777827cba2SAaron LI 	int i;
5787827cba2SAaron LI 
5797827cba2SAaron LI 	cp = data;
5808d36e1dfSRoy Marples 	ep = cp + data_len;
5817827cba2SAaron LI 	for (i = 0; i < RTAX_MAX; i++) {
5827827cba2SAaron LI 		if (type & (1 << i)) {
5838d36e1dfSRoy Marples 			if (cp >= ep) {
5848d36e1dfSRoy Marples 				errno = EINVAL;
5858d36e1dfSRoy Marples 				return -1;
5868d36e1dfSRoy Marples 			}
5877827cba2SAaron LI 			sa[i] = (const struct sockaddr *)cp;
5887827cba2SAaron LI 			RT_ADVANCE(cp, sa[i]);
5897827cba2SAaron LI 		} else
5907827cba2SAaron LI 			sa[i] = NULL;
5917827cba2SAaron LI 	}
5928d36e1dfSRoy Marples 
5938d36e1dfSRoy Marples 	return 0;
5947827cba2SAaron LI }
5957827cba2SAaron LI 
5967827cba2SAaron LI static struct interface *
if_findsdl(struct dhcpcd_ctx * ctx,const struct sockaddr_dl * sdl)5977827cba2SAaron LI if_findsdl(struct dhcpcd_ctx *ctx, const struct sockaddr_dl *sdl)
5987827cba2SAaron LI {
5997827cba2SAaron LI 
6007827cba2SAaron LI 	if (sdl->sdl_index)
6017827cba2SAaron LI 		return if_findindex(ctx->ifaces, sdl->sdl_index);
6027827cba2SAaron LI 
6037827cba2SAaron LI 	if (sdl->sdl_nlen) {
6047827cba2SAaron LI 		char ifname[IF_NAMESIZE];
6057827cba2SAaron LI 
6067827cba2SAaron LI 		memcpy(ifname, sdl->sdl_data, sdl->sdl_nlen);
6077827cba2SAaron LI 		ifname[sdl->sdl_nlen] = '\0';
6087827cba2SAaron LI 		return if_find(ctx->ifaces, ifname);
6097827cba2SAaron LI 	}
6107827cba2SAaron LI 	if (sdl->sdl_alen) {
6117827cba2SAaron LI 		struct interface *ifp;
6127827cba2SAaron LI 
6137827cba2SAaron LI 		TAILQ_FOREACH(ifp, ctx->ifaces, next) {
6147827cba2SAaron LI 			if (ifp->hwlen == sdl->sdl_alen &&
6157827cba2SAaron LI 			    memcmp(ifp->hwaddr,
6167827cba2SAaron LI 			    sdl->sdl_data, sdl->sdl_alen) == 0)
6177827cba2SAaron LI 				return ifp;
6187827cba2SAaron LI 		}
6197827cba2SAaron LI 	}
6207827cba2SAaron LI 
6217827cba2SAaron LI 	errno = ENOENT;
6227827cba2SAaron LI 	return NULL;
6237827cba2SAaron LI }
6247827cba2SAaron LI 
6257827cba2SAaron LI static struct interface *
if_findsa(struct dhcpcd_ctx * ctx,const struct sockaddr * sa)6267827cba2SAaron LI if_findsa(struct dhcpcd_ctx *ctx, const struct sockaddr *sa)
6277827cba2SAaron LI {
6287827cba2SAaron LI 	if (sa == NULL) {
6297827cba2SAaron LI 		errno = EINVAL;
6307827cba2SAaron LI 		return NULL;
6317827cba2SAaron LI 	}
6327827cba2SAaron LI 
6337827cba2SAaron LI 	switch (sa->sa_family) {
6347827cba2SAaron LI 	case AF_LINK:
6357827cba2SAaron LI 	{
6367827cba2SAaron LI 		const struct sockaddr_dl *sdl;
6377827cba2SAaron LI 
6387827cba2SAaron LI 		sdl = (const void *)sa;
6397827cba2SAaron LI 		return if_findsdl(ctx, sdl);
6407827cba2SAaron LI 	}
6417827cba2SAaron LI #ifdef INET
6427827cba2SAaron LI 	case AF_INET:
6437827cba2SAaron LI 	{
6447827cba2SAaron LI 		const struct sockaddr_in *sin;
6457827cba2SAaron LI 		struct ipv4_addr *ia;
6467827cba2SAaron LI 
6477827cba2SAaron LI 		sin = (const void *)sa;
6487827cba2SAaron LI 		if ((ia = ipv4_findmaskaddr(ctx, &sin->sin_addr)))
6497827cba2SAaron LI 			return ia->iface;
6500a68f8d2SRoy Marples 		if ((ia = ipv4_findmaskbrd(ctx, &sin->sin_addr)))
6510a68f8d2SRoy Marples 			return ia->iface;
6527827cba2SAaron LI 		break;
6537827cba2SAaron LI 	}
6547827cba2SAaron LI #endif
6557827cba2SAaron LI #ifdef INET6
6567827cba2SAaron LI 	case AF_INET6:
6577827cba2SAaron LI 	{
6587827cba2SAaron LI 		const struct sockaddr_in6 *sin;
6598d36e1dfSRoy Marples 		unsigned int scope;
6607827cba2SAaron LI 		struct ipv6_addr *ia;
6617827cba2SAaron LI 
6627827cba2SAaron LI 		sin = (const void *)sa;
663d4fb1e02SRoy Marples 		scope = ipv6_getscope(sin);
6648d36e1dfSRoy Marples 		if (scope != 0)
6658d36e1dfSRoy Marples 			return if_findindex(ctx->ifaces, scope);
6667827cba2SAaron LI 		if ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr)))
6677827cba2SAaron LI 			return ia->iface;
6687827cba2SAaron LI 		break;
6697827cba2SAaron LI 	}
6707827cba2SAaron LI #endif
6717827cba2SAaron LI 	default:
6727827cba2SAaron LI 		errno = EAFNOSUPPORT;
6737827cba2SAaron LI 		return NULL;
6747827cba2SAaron LI 	}
6757827cba2SAaron LI 
6767827cba2SAaron LI 	errno = ENOENT;
6777827cba2SAaron LI 	return NULL;
6787827cba2SAaron LI }
6797827cba2SAaron LI 
6807827cba2SAaron LI static void
if_copysa(struct sockaddr * dst,const struct sockaddr * src)6817827cba2SAaron LI if_copysa(struct sockaddr *dst, const struct sockaddr *src)
6827827cba2SAaron LI {
6837827cba2SAaron LI 
6847827cba2SAaron LI 	assert(dst != NULL);
6857827cba2SAaron LI 	assert(src != NULL);
6867827cba2SAaron LI 
6877827cba2SAaron LI 	memcpy(dst, src, src->sa_len);
6887827cba2SAaron LI #if defined(INET6) && defined(__KAME__)
6897827cba2SAaron LI 	if (dst->sa_family == AF_INET6) {
6907827cba2SAaron LI 		struct in6_addr *in6;
6917827cba2SAaron LI 
6927827cba2SAaron LI 		in6 = &satosin6(dst)->sin6_addr;
6937827cba2SAaron LI 		if (IN6_IS_ADDR_LINKLOCAL(in6))
6947827cba2SAaron LI 			in6->s6_addr[2] = in6->s6_addr[3] = '\0';
6957827cba2SAaron LI 	}
6967827cba2SAaron LI #endif
6977827cba2SAaron LI }
6987827cba2SAaron LI 
6997827cba2SAaron LI int
if_route(unsigned char cmd,const struct rt * rt)7007827cba2SAaron LI if_route(unsigned char cmd, const struct rt *rt)
7017827cba2SAaron LI {
7027827cba2SAaron LI 	struct dhcpcd_ctx *ctx;
7038d36e1dfSRoy Marples 	struct rtm rtmsg;
7047827cba2SAaron LI 	struct rt_msghdr *rtm = &rtmsg.hdr;
7057827cba2SAaron LI 	char *bp = rtmsg.buffer;
7067827cba2SAaron LI 	struct sockaddr_dl sdl;
7077827cba2SAaron LI 	bool gateway_unspec;
7087827cba2SAaron LI 
7097827cba2SAaron LI 	assert(rt != NULL);
7108d36e1dfSRoy Marples 	assert(rt->rt_ifp != NULL);
7118d36e1dfSRoy Marples 	assert(rt->rt_ifp->ctx != NULL);
7127827cba2SAaron LI 	ctx = rt->rt_ifp->ctx;
7137827cba2SAaron LI 
7147827cba2SAaron LI #define ADDSA(sa) do {							      \
7157827cba2SAaron LI 		memcpy(bp, (sa), (sa)->sa_len);				      \
7167827cba2SAaron LI 		bp += RT_ROUNDUP((sa)->sa_len);				      \
7177827cba2SAaron LI 	}  while (0 /* CONSTCOND */)
7187827cba2SAaron LI 
7197827cba2SAaron LI 	memset(&rtmsg, 0, sizeof(rtmsg));
7207827cba2SAaron LI 	rtm->rtm_version = RTM_VERSION;
7217827cba2SAaron LI 	rtm->rtm_type = cmd;
7227827cba2SAaron LI #ifdef __OpenBSD__
7237827cba2SAaron LI 	rtm->rtm_pid = getpid();
7247827cba2SAaron LI #endif
7257827cba2SAaron LI 	rtm->rtm_seq = ++ctx->seq;
7267827cba2SAaron LI 	rtm->rtm_flags = (int)rt->rt_flags;
7277827cba2SAaron LI 	rtm->rtm_addrs = RTA_DST;
7287827cba2SAaron LI #ifdef RTF_PINNED
7297827cba2SAaron LI 	if (cmd != RTM_ADD)
7307827cba2SAaron LI 		rtm->rtm_flags |= RTF_PINNED;
7317827cba2SAaron LI #endif
7327827cba2SAaron LI 
7337827cba2SAaron LI 	gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
7347827cba2SAaron LI 
7357827cba2SAaron LI 	if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
7367827cba2SAaron LI 		bool netmask_bcast = sa_is_allones(&rt->rt_netmask);
7377827cba2SAaron LI 
7387827cba2SAaron LI 		rtm->rtm_flags |= RTF_UP;
7397827cba2SAaron LI 		rtm->rtm_addrs |= RTA_GATEWAY;
7407827cba2SAaron LI 		if (!(rtm->rtm_flags & RTF_REJECT) &&
7417827cba2SAaron LI 		    !sa_is_loopback(&rt->rt_gateway))
7427827cba2SAaron LI 		{
7437827cba2SAaron LI 			rtm->rtm_index = (unsigned short)rt->rt_ifp->index;
7448d36e1dfSRoy Marples /*
7458d36e1dfSRoy Marples  * OpenBSD rejects the message for on-link routes.
7468d36e1dfSRoy Marples  * FreeBSD-12 kernel apparently panics.
7478d36e1dfSRoy Marples  * I can't replicate the panic, but better safe than sorry!
7488d36e1dfSRoy Marples  * https://roy.marples.name/archives/dhcpcd-discuss/0002286.html
7498d36e1dfSRoy Marples  *
7508d36e1dfSRoy Marples  * Neither OS currently allows IPv6 address sharing anyway, so let's
7518d36e1dfSRoy Marples  * try to encourage someone to fix that by logging a waring during compile.
7528d36e1dfSRoy Marples  */
7538d36e1dfSRoy Marples #if defined(__FreeBSD__) || defined(__OpenBSD__)
7548d36e1dfSRoy Marples #warning kernel does not allow IPv6 address sharing
7558d36e1dfSRoy Marples 			if (!gateway_unspec || rt->rt_dest.sa_family!=AF_INET6)
7568d36e1dfSRoy Marples #endif
7577827cba2SAaron LI 			rtm->rtm_addrs |= RTA_IFP;
7587827cba2SAaron LI 			if (!sa_is_unspecified(&rt->rt_ifa))
7597827cba2SAaron LI 				rtm->rtm_addrs |= RTA_IFA;
7607827cba2SAaron LI 		}
7617827cba2SAaron LI 		if (netmask_bcast)
7627827cba2SAaron LI 			rtm->rtm_flags |= RTF_HOST;
7637827cba2SAaron LI 		/* Network routes are cloning or connected if supported.
7647827cba2SAaron LI 		 * All other routes are static. */
7657827cba2SAaron LI 		if (gateway_unspec) {
7667827cba2SAaron LI #ifdef RTF_CLONING
7677827cba2SAaron LI 			rtm->rtm_flags |= RTF_CLONING;
7687827cba2SAaron LI #endif
7697827cba2SAaron LI #ifdef RTF_CONNECTED
7707827cba2SAaron LI 			rtm->rtm_flags |= RTF_CONNECTED;
7717827cba2SAaron LI #endif
7727827cba2SAaron LI #ifdef RTP_CONNECTED
7737827cba2SAaron LI 			rtm->rtm_priority = RTP_CONNECTED;
7747827cba2SAaron LI #endif
7757827cba2SAaron LI #ifdef RTF_CLONING
7767827cba2SAaron LI 			if (netmask_bcast) {
7777827cba2SAaron LI 				/*
7787827cba2SAaron LI 				 * We add a cloning network route for a single
7797827cba2SAaron LI 				 * host. Traffic to the host will generate a
7807827cba2SAaron LI 				 * cloned route and the hardware address will
7817827cba2SAaron LI 				 * resolve correctly.
7827827cba2SAaron LI 				 * It might be more correct to use RTF_HOST
7837827cba2SAaron LI 				 * instead of RTF_CLONING, and that does work,
7847827cba2SAaron LI 				 * but some OS generate an arp warning
7857827cba2SAaron LI 				 * diagnostic which we don't want to do.
7867827cba2SAaron LI 				 */
7877827cba2SAaron LI 				rtm->rtm_flags &= ~RTF_HOST;
7887827cba2SAaron LI 			}
7897827cba2SAaron LI #endif
7907827cba2SAaron LI 		} else
7917827cba2SAaron LI 			rtm->rtm_flags |= RTF_GATEWAY;
7927827cba2SAaron LI 
7937f8103cdSRoy Marples 		if (rt->rt_dflags & RTDF_STATIC)
7947827cba2SAaron LI 			rtm->rtm_flags |= RTF_STATIC;
7957827cba2SAaron LI 
7967827cba2SAaron LI 		if (rt->rt_mtu != 0) {
7977827cba2SAaron LI 			rtm->rtm_inits |= RTV_MTU;
7987827cba2SAaron LI 			rtm->rtm_rmx.rmx_mtu = rt->rt_mtu;
7997827cba2SAaron LI 		}
8007827cba2SAaron LI 	}
8017827cba2SAaron LI 
8027827cba2SAaron LI 	if (!(rtm->rtm_flags & RTF_HOST))
8037827cba2SAaron LI 		rtm->rtm_addrs |= RTA_NETMASK;
8047827cba2SAaron LI 
8057827cba2SAaron LI 	if_linkaddr(&sdl, rt->rt_ifp);
8067827cba2SAaron LI 
8077827cba2SAaron LI 	ADDSA(&rt->rt_dest);
8087827cba2SAaron LI 
8097827cba2SAaron LI 	if (rtm->rtm_addrs & RTA_GATEWAY) {
8107827cba2SAaron LI 		if (gateway_unspec)
8117827cba2SAaron LI 			ADDSA((struct sockaddr *)&sdl);
8127827cba2SAaron LI 		else {
8137827cba2SAaron LI 			union sa_ss gateway;
8147827cba2SAaron LI 
8157827cba2SAaron LI 			if_copysa(&gateway.sa, &rt->rt_gateway);
8167827cba2SAaron LI #ifdef INET6
8177827cba2SAaron LI 			if (gateway.sa.sa_family == AF_INET6)
818d4fb1e02SRoy Marples 				ipv6_setscope(&gateway.sin6, rt->rt_ifp->index);
8197827cba2SAaron LI #endif
8207827cba2SAaron LI 			ADDSA(&gateway.sa);
8217827cba2SAaron LI 		}
8227827cba2SAaron LI 	}
8237827cba2SAaron LI 
8247827cba2SAaron LI 	if (rtm->rtm_addrs & RTA_NETMASK)
8257827cba2SAaron LI 		ADDSA(&rt->rt_netmask);
8267827cba2SAaron LI 
8277827cba2SAaron LI 	if (rtm->rtm_addrs & RTA_IFP)
8287827cba2SAaron LI 		ADDSA((struct sockaddr *)&sdl);
8297827cba2SAaron LI 
8307827cba2SAaron LI 	if (rtm->rtm_addrs & RTA_IFA)
8317827cba2SAaron LI 		ADDSA(&rt->rt_ifa);
8327827cba2SAaron LI 
8337827cba2SAaron LI #undef ADDSA
8347827cba2SAaron LI 
8357827cba2SAaron LI 	rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm);
8366e63cc1fSRoy Marples 
8376e63cc1fSRoy Marples #ifdef PRIVSEP
8386e63cc1fSRoy Marples 	if (ctx->options & DHCPCD_PRIVSEP) {
8396e63cc1fSRoy Marples 		if (ps_root_route(ctx, rtm, rtm->rtm_msglen) == -1)
8406e63cc1fSRoy Marples 			return -1;
8416e63cc1fSRoy Marples 		return 0;
8426e63cc1fSRoy Marples 	}
8436e63cc1fSRoy Marples #endif
8447827cba2SAaron LI 	if (write(ctx->link_fd, rtm, rtm->rtm_msglen) == -1)
8457827cba2SAaron LI 		return -1;
8467827cba2SAaron LI 	return 0;
8477827cba2SAaron LI }
8487827cba2SAaron LI 
8496e63cc1fSRoy Marples static bool
if_realroute(const struct rt_msghdr * rtm)8506e63cc1fSRoy Marples if_realroute(const struct rt_msghdr *rtm)
8516e63cc1fSRoy Marples {
8526e63cc1fSRoy Marples 
8536e63cc1fSRoy Marples #ifdef RTF_CLONED
8546e63cc1fSRoy Marples 	if (rtm->rtm_flags & RTF_CLONED)
8556e63cc1fSRoy Marples 		return false;
8566e63cc1fSRoy Marples #endif
8576e63cc1fSRoy Marples #ifdef RTF_WASCLONED
8586e63cc1fSRoy Marples 	if (rtm->rtm_flags & RTF_WASCLONED)
8596e63cc1fSRoy Marples 		return false;
8606e63cc1fSRoy Marples #endif
8616e63cc1fSRoy Marples #ifdef RTF_LOCAL
8626e63cc1fSRoy Marples 	if (rtm->rtm_flags & RTF_LOCAL)
8636e63cc1fSRoy Marples 		return false;
8646e63cc1fSRoy Marples #endif
8656e63cc1fSRoy Marples #ifdef RTF_BROADCAST
8666e63cc1fSRoy Marples 	if (rtm->rtm_flags & RTF_BROADCAST)
8676e63cc1fSRoy Marples 		return false;
8686e63cc1fSRoy Marples #endif
8696e63cc1fSRoy Marples 	return true;
8706e63cc1fSRoy Marples }
8716e63cc1fSRoy Marples 
8727827cba2SAaron LI static int
if_copyrt(struct dhcpcd_ctx * ctx,struct rt * rt,const struct rt_msghdr * rtm)8737827cba2SAaron LI if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
8747827cba2SAaron LI {
8757827cba2SAaron LI 	const struct sockaddr *rti_info[RTAX_MAX];
8767827cba2SAaron LI 
8778d36e1dfSRoy Marples 	if (!(rtm->rtm_addrs & RTA_DST)) {
8788d36e1dfSRoy Marples 		errno = EINVAL;
8797827cba2SAaron LI 		return -1;
8808d36e1dfSRoy Marples 	}
8818d36e1dfSRoy Marples 	if (rtm->rtm_type != RTM_MISS && !(rtm->rtm_addrs & RTA_GATEWAY)) {
8828d36e1dfSRoy Marples 		errno = EINVAL;
8838d36e1dfSRoy Marples 		return -1;
8848d36e1dfSRoy Marples 	}
8857827cba2SAaron LI 
8868d36e1dfSRoy Marples 	if (get_addrs(rtm->rtm_addrs, (const char *)rtm + sizeof(*rtm),
8878d36e1dfSRoy Marples 	              rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)
8888d36e1dfSRoy Marples 		return -1;
8897827cba2SAaron LI 	memset(rt, 0, sizeof(*rt));
8907827cba2SAaron LI 
8917827cba2SAaron LI 	rt->rt_flags = (unsigned int)rtm->rtm_flags;
8927827cba2SAaron LI 	if_copysa(&rt->rt_dest, rti_info[RTAX_DST]);
8937827cba2SAaron LI 	if (rtm->rtm_addrs & RTA_NETMASK) {
8947827cba2SAaron LI 		if_copysa(&rt->rt_netmask, rti_info[RTAX_NETMASK]);
8957827cba2SAaron LI 		if (rt->rt_netmask.sa_family == 255) /* Why? */
8967827cba2SAaron LI 			rt->rt_netmask.sa_family = rt->rt_dest.sa_family;
8977827cba2SAaron LI 	}
8988d36e1dfSRoy Marples 
8998d36e1dfSRoy Marples 	/* dhcpcd likes an unspecified gateway to indicate via the link.
9008d36e1dfSRoy Marples 	 * However we need to know if gateway was a link with an address. */
9018d36e1dfSRoy Marples 	if (rtm->rtm_addrs & RTA_GATEWAY) {
9028d36e1dfSRoy Marples 		if (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) {
9038d36e1dfSRoy Marples 			const struct sockaddr_dl *sdl;
9048d36e1dfSRoy Marples 
9058d36e1dfSRoy Marples 			sdl = (const struct sockaddr_dl*)
9068d36e1dfSRoy Marples 			    (const void *)rti_info[RTAX_GATEWAY];
9078d36e1dfSRoy Marples 			if (sdl->sdl_alen != 0)
9088d36e1dfSRoy Marples 				rt->rt_dflags |= RTDF_GATELINK;
9098d36e1dfSRoy Marples 		} else if (rtm->rtm_flags & RTF_GATEWAY)
9107827cba2SAaron LI 			if_copysa(&rt->rt_gateway, rti_info[RTAX_GATEWAY]);
9118d36e1dfSRoy Marples 	}
9128d36e1dfSRoy Marples 
9137827cba2SAaron LI 	if (rtm->rtm_addrs & RTA_IFA)
9147827cba2SAaron LI 		if_copysa(&rt->rt_ifa, rti_info[RTAX_IFA]);
9158d36e1dfSRoy Marples 
9167827cba2SAaron LI 	rt->rt_mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
9177827cba2SAaron LI 
9187827cba2SAaron LI 	if (rtm->rtm_index)
9197827cba2SAaron LI 		rt->rt_ifp = if_findindex(ctx->ifaces, rtm->rtm_index);
9207827cba2SAaron LI 	else if (rtm->rtm_addrs & RTA_IFP)
9217827cba2SAaron LI 		rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_IFP]);
9227827cba2SAaron LI 	else if (rtm->rtm_addrs & RTA_GATEWAY)
9237827cba2SAaron LI 		rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
9247827cba2SAaron LI 	else
9257827cba2SAaron LI 		rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_DST]);
9267827cba2SAaron LI 
9278d36e1dfSRoy Marples 	if (rt->rt_ifp == NULL && rtm->rtm_type == RTM_MISS)
9288d36e1dfSRoy Marples 		rt->rt_ifp = if_find(ctx->ifaces, "lo0");
9298d36e1dfSRoy Marples 
9307827cba2SAaron LI 	if (rt->rt_ifp == NULL) {
9317827cba2SAaron LI 		errno = ESRCH;
9327827cba2SAaron LI 		return -1;
9337827cba2SAaron LI 	}
9347827cba2SAaron LI 	return 0;
9357827cba2SAaron LI }
9367827cba2SAaron LI 
93780aa9461SRoy Marples static int
if_sysctl(struct dhcpcd_ctx * ctx,const int * name,u_int namelen,void * oldp,size_t * oldlenp,void * newp,size_t newlen)93880aa9461SRoy Marples if_sysctl(struct dhcpcd_ctx *ctx,
93980aa9461SRoy Marples     const int *name, u_int namelen,
94080aa9461SRoy Marples     void *oldp, size_t *oldlenp, void *newp, size_t newlen)
94180aa9461SRoy Marples {
94280aa9461SRoy Marples #if defined(PRIVSEP) && defined(HAVE_CAPSICUM)
94380aa9461SRoy Marples 	if (IN_PRIVSEP(ctx))
94480aa9461SRoy Marples 		return (int)ps_root_sysctl(ctx, name, namelen,
94580aa9461SRoy Marples 		    oldp, oldlenp, newp, newlen);
94680aa9461SRoy Marples #else
94780aa9461SRoy Marples 	UNUSED(ctx);
94880aa9461SRoy Marples #endif
94980aa9461SRoy Marples 
95080aa9461SRoy Marples 	return sysctl(name, namelen, oldp, oldlenp, newp, newlen);
95180aa9461SRoy Marples }
95280aa9461SRoy Marples 
9537827cba2SAaron LI int
if_initrt(struct dhcpcd_ctx * ctx,rb_tree_t * kroutes,int af)9548d36e1dfSRoy Marples if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)
9557827cba2SAaron LI {
9567827cba2SAaron LI 	struct rt_msghdr *rtm;
95780aa9461SRoy Marples 	int mib[6] = { CTL_NET, PF_ROUTE, 0, af, NET_RT_DUMP, 0 };
95880aa9461SRoy Marples 	size_t bufl;
9597827cba2SAaron LI 	char *buf, *p, *end;
9608d36e1dfSRoy Marples 	struct rt rt, *rtn;
9617827cba2SAaron LI 
96280aa9461SRoy Marples 	if (if_sysctl(ctx, mib, __arraycount(mib), NULL, &bufl, NULL, 0) == -1)
9637827cba2SAaron LI 		return -1;
96480aa9461SRoy Marples 	if (bufl == 0)
9657827cba2SAaron LI 		return 0;
96680aa9461SRoy Marples 	if ((buf = malloc(bufl)) == NULL)
9677827cba2SAaron LI 		return -1;
96880aa9461SRoy Marples 	if (if_sysctl(ctx, mib, __arraycount(mib), buf, &bufl, NULL, 0) == -1)
96980aa9461SRoy Marples 	{
9707827cba2SAaron LI 		free(buf);
9717827cba2SAaron LI 		return -1;
9727827cba2SAaron LI 	}
9737827cba2SAaron LI 
97480aa9461SRoy Marples 	end = buf + bufl;
9757827cba2SAaron LI 	for (p = buf; p < end; p += rtm->rtm_msglen) {
9767827cba2SAaron LI 		rtm = (void *)p;
97780aa9461SRoy Marples 		if (p + sizeof(*rtm) > end || p + rtm->rtm_msglen > end) {
9788d36e1dfSRoy Marples 			errno = EINVAL;
9798d36e1dfSRoy Marples 			break;
9807827cba2SAaron LI 		}
9816e63cc1fSRoy Marples 		if (!if_realroute(rtm))
9826e63cc1fSRoy Marples 			continue;
9838d36e1dfSRoy Marples 		if (if_copyrt(ctx, &rt, rtm) != 0)
9848d36e1dfSRoy Marples 			continue;
9858d36e1dfSRoy Marples 		if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
9868d36e1dfSRoy Marples 			logerr(__func__);
9878d36e1dfSRoy Marples 			break;
9888d36e1dfSRoy Marples 		}
9898d36e1dfSRoy Marples 		memcpy(rtn, &rt, sizeof(*rtn));
9908d36e1dfSRoy Marples 		if (rb_tree_insert_node(kroutes, rtn) != rtn)
9918d36e1dfSRoy Marples 			rt_free(rtn);
9927827cba2SAaron LI 	}
9937827cba2SAaron LI 	free(buf);
9948d36e1dfSRoy Marples 	return p == end ? 0 : -1;
9957827cba2SAaron LI }
9967827cba2SAaron LI 
9977827cba2SAaron LI #ifdef INET
9987827cba2SAaron LI int
if_address(unsigned char cmd,const struct ipv4_addr * ia)9997827cba2SAaron LI if_address(unsigned char cmd, const struct ipv4_addr *ia)
10007827cba2SAaron LI {
10017827cba2SAaron LI 	int r;
10027827cba2SAaron LI 	struct in_aliasreq ifra;
10036e63cc1fSRoy Marples 	struct dhcpcd_ctx *ctx = ia->iface->ctx;
10047827cba2SAaron LI 
10057827cba2SAaron LI 	memset(&ifra, 0, sizeof(ifra));
10067827cba2SAaron LI 	strlcpy(ifra.ifra_name, ia->iface->name, sizeof(ifra.ifra_name));
10077827cba2SAaron LI 
10087827cba2SAaron LI #define ADDADDR(var, addr) do {						      \
10097827cba2SAaron LI 		(var)->sin_family = AF_INET;				      \
10107827cba2SAaron LI 		(var)->sin_len = sizeof(*(var));			      \
10117827cba2SAaron LI 		(var)->sin_addr = *(addr);				      \
10127827cba2SAaron LI 	} while (/*CONSTCOND*/0)
10137827cba2SAaron LI 	ADDADDR(&ifra.ifra_addr, &ia->addr);
10147827cba2SAaron LI 	ADDADDR(&ifra.ifra_mask, &ia->mask);
10157827cba2SAaron LI 	if (cmd == RTM_NEWADDR && ia->brd.s_addr != INADDR_ANY)
10167827cba2SAaron LI 		ADDADDR(&ifra.ifra_broadaddr, &ia->brd);
10177827cba2SAaron LI #undef ADDADDR
10187827cba2SAaron LI 
10196e63cc1fSRoy Marples 	r = if_ioctl(ctx,
10206e63cc1fSRoy Marples 	    cmd == RTM_DELADDR ? SIOCDIFADDR : SIOCAIFADDR, &ifra,sizeof(ifra));
10217827cba2SAaron LI 	return r;
10227827cba2SAaron LI }
10237827cba2SAaron LI 
10247827cba2SAaron LI #if !(defined(HAVE_IFADDRS_ADDRFLAGS) && defined(HAVE_IFAM_ADDRFLAGS))
10257827cba2SAaron LI int
if_addrflags(const struct interface * ifp,const struct in_addr * addr,__unused const char * alias)10267827cba2SAaron LI if_addrflags(const struct interface *ifp, const struct in_addr *addr,
10277827cba2SAaron LI     __unused const char *alias)
10287827cba2SAaron LI {
10297827cba2SAaron LI #ifdef SIOCGIFAFLAG_IN
10307827cba2SAaron LI 	struct ifreq ifr;
10317827cba2SAaron LI 	struct sockaddr_in *sin;
10327827cba2SAaron LI 
10337827cba2SAaron LI 	memset(&ifr, 0, sizeof(ifr));
10347827cba2SAaron LI 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
10357827cba2SAaron LI 	sin = (void *)&ifr.ifr_addr;
10367827cba2SAaron LI 	sin->sin_family = AF_INET;
10377827cba2SAaron LI 	sin->sin_addr = *addr;
10387827cba2SAaron LI 	if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFAFLAG_IN, &ifr) == -1)
10397827cba2SAaron LI 		return -1;
10407827cba2SAaron LI 	return ifr.ifr_addrflags;
10417827cba2SAaron LI #else
10427827cba2SAaron LI 	UNUSED(ifp);
10437827cba2SAaron LI 	UNUSED(addr);
10447827cba2SAaron LI 	return 0;
10457827cba2SAaron LI #endif
10467827cba2SAaron LI }
10477827cba2SAaron LI #endif
10487827cba2SAaron LI #endif /* INET */
10497827cba2SAaron LI 
10507827cba2SAaron LI #ifdef INET6
10516e63cc1fSRoy Marples static int
if_ioctl6(struct dhcpcd_ctx * ctx,unsigned long req,void * data,size_t len)10526e63cc1fSRoy Marples if_ioctl6(struct dhcpcd_ctx *ctx, unsigned long req, void *data, size_t len)
10536e63cc1fSRoy Marples {
10546e63cc1fSRoy Marples 	struct priv *priv;
10556e63cc1fSRoy Marples 
10566e63cc1fSRoy Marples #ifdef PRIVSEP
10576e63cc1fSRoy Marples 	if (ctx->options & DHCPCD_PRIVSEP)
10586e63cc1fSRoy Marples 		return (int)ps_root_ioctl6(ctx, req, data, len);
10596e63cc1fSRoy Marples #endif
10606e63cc1fSRoy Marples 
10616e63cc1fSRoy Marples 	priv = ctx->priv;
10626e63cc1fSRoy Marples 	return ioctl(priv->pf_inet6_fd, req, data, len);
10636e63cc1fSRoy Marples }
10646e63cc1fSRoy Marples 
10657827cba2SAaron LI int
if_address6(unsigned char cmd,const struct ipv6_addr * ia)10667827cba2SAaron LI if_address6(unsigned char cmd, const struct ipv6_addr *ia)
10677827cba2SAaron LI {
10687a0236bfSRoy Marples 	struct in6_aliasreq ifa = { .ifra_flags = 0 };
10697827cba2SAaron LI 	struct in6_addr mask;
10706e63cc1fSRoy Marples 	struct dhcpcd_ctx *ctx = ia->iface->ctx;
10717827cba2SAaron LI 
10727827cba2SAaron LI 	strlcpy(ifa.ifra_name, ia->iface->name, sizeof(ifa.ifra_name));
10738d36e1dfSRoy Marples #if defined(__FreeBSD__) || defined(__DragonFly__)
10747a0236bfSRoy Marples 	/* This is a bug - the kernel should work this out. */
10758d36e1dfSRoy Marples 	if (ia->addr_flags & IN6_IFF_TENTATIVE)
10768d36e1dfSRoy Marples 		ifa.ifra_flags |= IN6_IFF_TENTATIVE;
10778d36e1dfSRoy Marples #endif
1078cc34ba0cSRoy Marples #if (defined(__NetBSD__) || defined(__OpenBSD__)) && \
1079cc34ba0cSRoy Marples     (defined(IPV6CTL_ACCEPT_RTADV) || defined(ND6_IFF_ACCEPT_RTADV))
1080cc34ba0cSRoy Marples 	/* These kernels don't accept userland setting IN6_IFF_AUTOCONF */
1081cc34ba0cSRoy Marples #else
10827a0236bfSRoy Marples 	if (ia->flags & IPV6_AF_AUTOCONF)
10837a0236bfSRoy Marples 		ifa.ifra_flags |= IN6_IFF_AUTOCONF;
10847a0236bfSRoy Marples #endif
1085280986e4SRoy Marples #ifdef IPV6_MANAGETEMPADDR
10867827cba2SAaron LI 	if (ia->flags & IPV6_AF_TEMPORARY)
10877827cba2SAaron LI 		ifa.ifra_flags |= IN6_IFF_TEMPORARY;
10887827cba2SAaron LI #endif
10897827cba2SAaron LI 
10907827cba2SAaron LI #define ADDADDR(v, addr) {						      \
10917827cba2SAaron LI 		(v)->sin6_family = AF_INET6;				      \
10927827cba2SAaron LI 		(v)->sin6_len = sizeof(*v);				      \
10937827cba2SAaron LI 		(v)->sin6_addr = *(addr);				      \
10947827cba2SAaron LI 	}
10957827cba2SAaron LI 
10967827cba2SAaron LI 	ADDADDR(&ifa.ifra_addr, &ia->addr);
1097d4fb1e02SRoy Marples 	ipv6_setscope(&ifa.ifra_addr, ia->iface->index);
10987827cba2SAaron LI 	ipv6_mask(&mask, ia->prefix_len);
10997827cba2SAaron LI 	ADDADDR(&ifa.ifra_prefixmask, &mask);
11007827cba2SAaron LI 
11017827cba2SAaron LI #undef ADDADDR
11027827cba2SAaron LI 
11037827cba2SAaron LI 	/*
11047827cba2SAaron LI 	 * Every BSD kernel wants to add the prefix of the address to it's
11057827cba2SAaron LI 	 * list of RA received prefixes.
11067827cba2SAaron LI 	 * THIS IS WRONG because there (as the comments in the kernel state)
11077827cba2SAaron LI 	 * is no API for managing prefix lifetime and the kernel should not
11087827cba2SAaron LI 	 * pretend it's from a RA either.
11097827cba2SAaron LI 	 *
11107827cba2SAaron LI 	 * The issue is that the very first assigned prefix will inherit the
11117827cba2SAaron LI 	 * lifetime of the address, but any subsequent alteration of the
11127827cba2SAaron LI 	 * address OR it's lifetime will not affect the prefix lifetime.
11137827cba2SAaron LI 	 * As such, we cannot stop the prefix from timing out and then
11147827cba2SAaron LI 	 * constantly removing the prefix route dhcpcd is capable of adding
11157827cba2SAaron LI 	 * in it's absense.
11167827cba2SAaron LI 	 *
11177827cba2SAaron LI 	 * What we can do to mitigate the issue is to add the address with
11187827cba2SAaron LI 	 * infinite lifetimes, so the prefix route will never time out.
11197827cba2SAaron LI 	 * Once done, we can then set lifetimes on the address and all is good.
11207827cba2SAaron LI 	 * The downside of this approach is that we need to manually remove
11217827cba2SAaron LI 	 * the kernel route because it has no lifetime, but this is OK as
11227827cba2SAaron LI 	 * dhcpcd will handle this too.
11237827cba2SAaron LI 	 *
11247827cba2SAaron LI 	 * This issue is discussed on the NetBSD mailing lists here:
11257827cba2SAaron LI 	 * http://mail-index.netbsd.org/tech-net/2016/08/05/msg006044.html
11267827cba2SAaron LI 	 *
11277827cba2SAaron LI 	 * Fixed in NetBSD-7.99.36
11287827cba2SAaron LI 	 * NOT fixed in FreeBSD - bug 195197
11297827cba2SAaron LI 	 * Fixed in OpenBSD-5.9
11307827cba2SAaron LI 	 */
11317827cba2SAaron LI 
11327827cba2SAaron LI #if !((defined(__NetBSD_Version__) && __NetBSD_Version__ >= 799003600) || \
11337827cba2SAaron LI       (defined(__OpenBSD__) && OpenBSD >= 201605))
11347827cba2SAaron LI 	if (cmd == RTM_NEWADDR && !(ia->flags & IPV6_AF_ADDED)) {
11357827cba2SAaron LI 		ifa.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
11367827cba2SAaron LI 		ifa.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
11376e63cc1fSRoy Marples 		(void)if_ioctl6(ctx, SIOCAIFADDR_IN6, &ifa, sizeof(ifa));
11387827cba2SAaron LI 	}
11397827cba2SAaron LI #endif
11407827cba2SAaron LI 
11417827cba2SAaron LI #if defined(__OpenBSD__) && OpenBSD <= 201705
11427827cba2SAaron LI 	/* BUT OpenBSD older than 6.2 does not reset the address lifetime
11437827cba2SAaron LI 	 * for subsequent calls...
11447827cba2SAaron LI 	 * Luckily dhcpcd will remove the lease when it expires so
11457827cba2SAaron LI 	 * just set an infinite lifetime, unless a temporary address. */
11467827cba2SAaron LI 	if (ifa.ifra_flags & IN6_IFF_PRIVACY) {
11477827cba2SAaron LI 		ifa.ifra_lifetime.ia6t_vltime = ia->prefix_vltime;
11487827cba2SAaron LI 		ifa.ifra_lifetime.ia6t_pltime = ia->prefix_pltime;
11497827cba2SAaron LI 	} else {
11507827cba2SAaron LI 		ifa.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
11517827cba2SAaron LI 		ifa.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
11527827cba2SAaron LI 	}
11537827cba2SAaron LI #else
11547827cba2SAaron LI 	ifa.ifra_lifetime.ia6t_vltime = ia->prefix_vltime;
11557827cba2SAaron LI 	ifa.ifra_lifetime.ia6t_pltime = ia->prefix_pltime;
11567827cba2SAaron LI #endif
11577827cba2SAaron LI 
11586e63cc1fSRoy Marples 	return if_ioctl6(ctx,
11596e63cc1fSRoy Marples 	    cmd == RTM_DELADDR ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6,
11606e63cc1fSRoy Marples 	    &ifa, sizeof(ifa));
11617827cba2SAaron LI }
11627827cba2SAaron LI 
11637827cba2SAaron LI int
if_addrflags6(const struct interface * ifp,const struct in6_addr * addr,__unused const char * alias)11647827cba2SAaron LI if_addrflags6(const struct interface *ifp, const struct in6_addr *addr,
11657827cba2SAaron LI     __unused const char *alias)
11667827cba2SAaron LI {
11677827cba2SAaron LI 	int flags;
11687827cba2SAaron LI 	struct in6_ifreq ifr6;
11697827cba2SAaron LI 	struct priv *priv;
11707827cba2SAaron LI 
11717827cba2SAaron LI 	memset(&ifr6, 0, sizeof(ifr6));
11727827cba2SAaron LI 	strlcpy(ifr6.ifr_name, ifp->name, sizeof(ifr6.ifr_name));
11737827cba2SAaron LI 	ifr6.ifr_addr.sin6_family = AF_INET6;
11747827cba2SAaron LI 	ifr6.ifr_addr.sin6_addr = *addr;
1175d4fb1e02SRoy Marples 	ipv6_setscope(&ifr6.ifr_addr, ifp->index);
11767827cba2SAaron LI 	priv = (struct priv *)ifp->ctx->priv;
11777827cba2SAaron LI 	if (ioctl(priv->pf_inet6_fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
11787827cba2SAaron LI 		flags = ifr6.ifr_ifru.ifru_flags6;
11797827cba2SAaron LI 	else
11807827cba2SAaron LI 		flags = -1;
11817827cba2SAaron LI 	return flags;
11827827cba2SAaron LI }
11837827cba2SAaron LI 
11847827cba2SAaron LI int
if_getlifetime6(struct ipv6_addr * ia)11857827cba2SAaron LI if_getlifetime6(struct ipv6_addr *ia)
11867827cba2SAaron LI {
11877827cba2SAaron LI 	struct in6_ifreq ifr6;
11887827cba2SAaron LI 	time_t t;
11897827cba2SAaron LI 	struct in6_addrlifetime *lifetime;
11907827cba2SAaron LI 	struct priv *priv;
11917827cba2SAaron LI 
11927827cba2SAaron LI 	memset(&ifr6, 0, sizeof(ifr6));
11937827cba2SAaron LI 	strlcpy(ifr6.ifr_name, ia->iface->name, sizeof(ifr6.ifr_name));
11947827cba2SAaron LI 	ifr6.ifr_addr.sin6_family = AF_INET6;
11957827cba2SAaron LI 	ifr6.ifr_addr.sin6_addr = ia->addr;
1196d4fb1e02SRoy Marples 	ipv6_setscope(&ifr6.ifr_addr, ia->iface->index);
11977827cba2SAaron LI 	priv = (struct priv *)ia->iface->ctx->priv;
11987827cba2SAaron LI 	if (ioctl(priv->pf_inet6_fd, SIOCGIFALIFETIME_IN6, &ifr6) == -1)
11997827cba2SAaron LI 		return -1;
12008d36e1dfSRoy Marples 	clock_gettime(CLOCK_MONOTONIC, &ia->created);
12017827cba2SAaron LI 
12028d36e1dfSRoy Marples #if defined(__FreeBSD__) || defined(__DragonFly__)
12038d36e1dfSRoy Marples 	t = ia->created.tv_sec;
12048d36e1dfSRoy Marples #else
12057827cba2SAaron LI 	t = time(NULL);
12068d36e1dfSRoy Marples #endif
12077827cba2SAaron LI 
12088d36e1dfSRoy Marples 	lifetime = &ifr6.ifr_ifru.ifru_lifetime;
12097827cba2SAaron LI 	if (lifetime->ia6t_preferred)
12107827cba2SAaron LI 		ia->prefix_pltime = (uint32_t)(lifetime->ia6t_preferred -
12117827cba2SAaron LI 		    MIN(t, lifetime->ia6t_preferred));
12127827cba2SAaron LI 	else
12137827cba2SAaron LI 		ia->prefix_pltime = ND6_INFINITE_LIFETIME;
12147827cba2SAaron LI 	if (lifetime->ia6t_expire) {
12157827cba2SAaron LI 		ia->prefix_vltime = (uint32_t)(lifetime->ia6t_expire -
12167827cba2SAaron LI 		    MIN(t, lifetime->ia6t_expire));
12177827cba2SAaron LI 		/* Calculate the created time */
12187827cba2SAaron LI 		ia->created.tv_sec -= lifetime->ia6t_vltime - ia->prefix_vltime;
12197827cba2SAaron LI 	} else
12207827cba2SAaron LI 		ia->prefix_vltime = ND6_INFINITE_LIFETIME;
12217827cba2SAaron LI 	return 0;
12227827cba2SAaron LI }
12237827cba2SAaron LI #endif
12247827cba2SAaron LI 
12258d36e1dfSRoy Marples static int
if_announce(struct dhcpcd_ctx * ctx,const struct if_announcemsghdr * ifan)12267827cba2SAaron LI if_announce(struct dhcpcd_ctx *ctx, const struct if_announcemsghdr *ifan)
12277827cba2SAaron LI {
12287827cba2SAaron LI 
12298d36e1dfSRoy Marples 	if (ifan->ifan_msglen < sizeof(*ifan)) {
12308d36e1dfSRoy Marples 		errno = EINVAL;
12318d36e1dfSRoy Marples 		return -1;
12327827cba2SAaron LI 	}
12337827cba2SAaron LI 
12348d36e1dfSRoy Marples 	switch(ifan->ifan_what) {
12358d36e1dfSRoy Marples 	case IFAN_ARRIVAL:
12368d36e1dfSRoy Marples 		return dhcpcd_handleinterface(ctx, 1, ifan->ifan_name);
12378d36e1dfSRoy Marples 	case IFAN_DEPARTURE:
12388d36e1dfSRoy Marples 		return dhcpcd_handleinterface(ctx, -1, ifan->ifan_name);
12398d36e1dfSRoy Marples 	}
12408d36e1dfSRoy Marples 
12418d36e1dfSRoy Marples 	return 0;
12428d36e1dfSRoy Marples }
12438d36e1dfSRoy Marples 
12448d36e1dfSRoy Marples static int
if_ifinfo(struct dhcpcd_ctx * ctx,const struct if_msghdr * ifm)12457827cba2SAaron LI if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)
12467827cba2SAaron LI {
12477827cba2SAaron LI 	struct interface *ifp;
12487827cba2SAaron LI 	int link_state;
12497827cba2SAaron LI 
12508d36e1dfSRoy Marples 	if (ifm->ifm_msglen < sizeof(*ifm)) {
12518d36e1dfSRoy Marples 		errno = EINVAL;
12528d36e1dfSRoy Marples 		return -1;
12538d36e1dfSRoy Marples 	}
12548d36e1dfSRoy Marples 
12557827cba2SAaron LI 	if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL)
12568d36e1dfSRoy Marples 		return 0;
12578d36e1dfSRoy Marples 
1258a0d9933aSRoy Marples 	link_state = if_carrier(ifp, &ifm->ifm_data);
1259a0d9933aSRoy Marples 	dhcpcd_handlecarrier(ifp, link_state, (unsigned int)ifm->ifm_flags);
12607827cba2SAaron LI 	return 0;
12617827cba2SAaron LI }
12627827cba2SAaron LI 
12638d36e1dfSRoy Marples static int
if_rtm(struct dhcpcd_ctx * ctx,const struct rt_msghdr * rtm)12647827cba2SAaron LI if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
12657827cba2SAaron LI {
12667827cba2SAaron LI 	struct rt rt;
12677827cba2SAaron LI 
12688d36e1dfSRoy Marples 	if (rtm->rtm_msglen < sizeof(*rtm)) {
12698d36e1dfSRoy Marples 		errno = EINVAL;
12708d36e1dfSRoy Marples 		return -1;
12718d36e1dfSRoy Marples 	}
12727827cba2SAaron LI 
12737827cba2SAaron LI 	/* Ignore errors. */
12747827cba2SAaron LI 	if (rtm->rtm_errno != 0)
12758d36e1dfSRoy Marples 		return 0;
12767827cba2SAaron LI 
12776e63cc1fSRoy Marples 	/* Ignore messages from ourself. */
12786e63cc1fSRoy Marples #ifdef PRIVSEP
127980aa9461SRoy Marples 	if (ctx->ps_root != NULL) {
128080aa9461SRoy Marples 		if (rtm->rtm_pid == ctx->ps_root->psp_pid)
12816e63cc1fSRoy Marples 			return 0;
12826e63cc1fSRoy Marples 	}
12836e63cc1fSRoy Marples #endif
12846e63cc1fSRoy Marples 
12857827cba2SAaron LI 	if (if_copyrt(ctx, &rt, rtm) == -1)
12868d36e1dfSRoy Marples 		return errno == ENOTSUP ? 0 : -1;
12877827cba2SAaron LI 
12887827cba2SAaron LI #ifdef INET6
12897827cba2SAaron LI 	/*
12907827cba2SAaron LI 	 * BSD announces host routes.
12917827cba2SAaron LI 	 * As such, we should be notified of reachability by its
12927827cba2SAaron LI 	 * existance with a hardware address.
12938d36e1dfSRoy Marples 	 * Ensure we don't call this for a newly incomplete state.
12947827cba2SAaron LI 	 */
12958d36e1dfSRoy Marples 	if (rt.rt_dest.sa_family == AF_INET6 &&
12968d36e1dfSRoy Marples 	    (rt.rt_flags & RTF_HOST || rtm->rtm_type == RTM_MISS) &&
12978d36e1dfSRoy Marples 	    !(rtm->rtm_type == RTM_ADD && !(rt.rt_dflags & RTDF_GATELINK)))
12988d36e1dfSRoy Marples 	{
12998d36e1dfSRoy Marples 		bool reachable;
13007827cba2SAaron LI 
13018d36e1dfSRoy Marples 		reachable = (rtm->rtm_type == RTM_ADD ||
13028d36e1dfSRoy Marples 		    rtm->rtm_type == RTM_CHANGE) &&
13038d36e1dfSRoy Marples 		    rt.rt_dflags & RTDF_GATELINK;
13048d36e1dfSRoy Marples 		ipv6nd_neighbour(ctx, &rt.rt_ss_dest.sin6.sin6_addr, reachable);
13057827cba2SAaron LI 	}
13067827cba2SAaron LI #endif
13077827cba2SAaron LI 
13086e63cc1fSRoy Marples 	if (rtm->rtm_type != RTM_MISS && if_realroute(rtm))
13098d36e1dfSRoy Marples 		rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
13108d36e1dfSRoy Marples 	return 0;
13117827cba2SAaron LI }
13127827cba2SAaron LI 
13138d36e1dfSRoy Marples static int
if_ifa(struct dhcpcd_ctx * ctx,const struct ifa_msghdr * ifam)13147827cba2SAaron LI if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
13157827cba2SAaron LI {
13167827cba2SAaron LI 	struct interface *ifp;
13177827cba2SAaron LI 	const struct sockaddr *rti_info[RTAX_MAX];
13186e63cc1fSRoy Marples 	int flags;
13197827cba2SAaron LI 	pid_t pid;
13207827cba2SAaron LI 
13218d36e1dfSRoy Marples 	if (ifam->ifam_msglen < sizeof(*ifam)) {
13228d36e1dfSRoy Marples 		errno = EINVAL;
13238d36e1dfSRoy Marples 		return -1;
13248d36e1dfSRoy Marples 	}
13256e63cc1fSRoy Marples 
13266e63cc1fSRoy Marples #ifdef HAVE_IFAM_PID
13276e63cc1fSRoy Marples 	/* Ignore address deletions from ourself.
13286e63cc1fSRoy Marples 	 * We need to process address flag changes though. */
13296e63cc1fSRoy Marples 	if (ifam->ifam_type == RTM_DELADDR) {
13306e63cc1fSRoy Marples #ifdef PRIVSEP
133180aa9461SRoy Marples 		if (ctx->ps_root != NULL) {
133280aa9461SRoy Marples 			if (ifam->ifam_pid == ctx->ps_root->psp_pid)
13336e63cc1fSRoy Marples 				return 0;
13346e63cc1fSRoy Marples 		} else
13356e63cc1fSRoy Marples #endif
13366e63cc1fSRoy Marples 			/* address management is done via ioctl,
13376e63cc1fSRoy Marples 			 * so SO_USELOOPBACK has no effect,
13386e63cc1fSRoy Marples 			 * so we do need to check the pid. */
13396e63cc1fSRoy Marples 			if (ifam->ifam_pid == getpid())
13406e63cc1fSRoy Marples 				return 0;
13416e63cc1fSRoy Marples 	}
13426e63cc1fSRoy Marples 	pid = ifam->ifam_pid;
13436e63cc1fSRoy Marples #else
13446e63cc1fSRoy Marples 	pid = 0;
13456e63cc1fSRoy Marples #endif
13466e63cc1fSRoy Marples 
13478d36e1dfSRoy Marples 	if (~ifam->ifam_addrs & RTA_IFA)
13488d36e1dfSRoy Marples 		return 0;
13497827cba2SAaron LI 	if ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL)
13508d36e1dfSRoy Marples 		return 0;
13518d36e1dfSRoy Marples 
13528d36e1dfSRoy Marples 	if (get_addrs(ifam->ifam_addrs, (const char *)ifam + sizeof(*ifam),
13538d36e1dfSRoy Marples 		      ifam->ifam_msglen - sizeof(*ifam), rti_info) == -1)
13548d36e1dfSRoy Marples 		return -1;
13557827cba2SAaron LI 
1356f3744ac9SRoy Marples 	/* All BSD's set IFF_UP on the interface when adding an address.
1357f3744ac9SRoy Marples 	 * But not all BSD's emit this via RTM_IFINFO when they do this ... */
1358f3744ac9SRoy Marples 	if (ifam->ifam_type == RTM_NEWADDR && !(ifp->flags & IFF_UP))
1359f3744ac9SRoy Marples 		dhcpcd_handlecarrier(ifp, ifp->carrier, ifp->flags | IFF_UP);
1360f3744ac9SRoy Marples 
13617827cba2SAaron LI 	switch (rti_info[RTAX_IFA]->sa_family) {
13627827cba2SAaron LI 	case AF_LINK:
13637827cba2SAaron LI 	{
13647827cba2SAaron LI 		struct sockaddr_dl sdl;
13657827cba2SAaron LI 
13667827cba2SAaron LI #ifdef RTM_CHGADDR
13677827cba2SAaron LI 		if (ifam->ifam_type != RTM_CHGADDR)
13687827cba2SAaron LI 			break;
13697827cba2SAaron LI #else
13707827cba2SAaron LI 		if (ifam->ifam_type != RTM_NEWADDR)
13717827cba2SAaron LI 			break;
13727827cba2SAaron LI #endif
13737827cba2SAaron LI 		memcpy(&sdl, rti_info[RTAX_IFA], rti_info[RTAX_IFA]->sa_len);
1374d4fb1e02SRoy Marples 		dhcpcd_handlehwaddr(ifp, ifp->hwtype,
1375d4fb1e02SRoy Marples 		    CLLADDR(&sdl), sdl.sdl_alen);
13767827cba2SAaron LI 		break;
13777827cba2SAaron LI 	}
13787827cba2SAaron LI #ifdef INET
13797827cba2SAaron LI 	case AF_INET:
13807827cba2SAaron LI 	case 255: /* FIXME: Why 255? */
13817827cba2SAaron LI 	{
13827827cba2SAaron LI 		const struct sockaddr_in *sin;
13837827cba2SAaron LI 		struct in_addr addr, mask, bcast;
13847827cba2SAaron LI 
13857827cba2SAaron LI 		sin = (const void *)rti_info[RTAX_IFA];
13867827cba2SAaron LI 		addr.s_addr = sin != NULL && sin->sin_family == AF_INET ?
13877827cba2SAaron LI 		    sin->sin_addr.s_addr : INADDR_ANY;
13887827cba2SAaron LI 		sin = (const void *)rti_info[RTAX_NETMASK];
13897827cba2SAaron LI 		mask.s_addr = sin != NULL && sin->sin_family == AF_INET ?
13907827cba2SAaron LI 		    sin->sin_addr.s_addr : INADDR_ANY;
13918d36e1dfSRoy Marples 		sin = (const void *)rti_info[RTAX_BRD];
13928d36e1dfSRoy Marples 		bcast.s_addr = sin != NULL && sin->sin_family == AF_INET ?
13938d36e1dfSRoy Marples 		    sin->sin_addr.s_addr : INADDR_ANY;
13947827cba2SAaron LI 
13958d36e1dfSRoy Marples 		/*
13968d36e1dfSRoy Marples 		 * NetBSD-7 and older send an invalid broadcast address.
13977827cba2SAaron LI 		 * So we need to query the actual address to get
13988d36e1dfSRoy Marples 		 * the right one.
13996e63cc1fSRoy Marples 		 * We can also use this to test if the address
14006e63cc1fSRoy Marples 		 * has really been added or deleted.
14018d36e1dfSRoy Marples 		 */
14028d36e1dfSRoy Marples #ifdef SIOCGIFALIAS
14037827cba2SAaron LI 		struct in_aliasreq ifra;
14047827cba2SAaron LI 
14057827cba2SAaron LI 		memset(&ifra, 0, sizeof(ifra));
14066e63cc1fSRoy Marples 		strlcpy(ifra.ifra_name, ifp->name, sizeof(ifra.ifra_name));
14077827cba2SAaron LI 		ifra.ifra_addr.sin_family = AF_INET;
14087827cba2SAaron LI 		ifra.ifra_addr.sin_len = sizeof(ifra.ifra_addr);
14097827cba2SAaron LI 		ifra.ifra_addr.sin_addr = addr;
14107827cba2SAaron LI 		if (ioctl(ctx->pf_inet_fd, SIOCGIFALIAS, &ifra) == -1) {
14118d36e1dfSRoy Marples 			if (errno != ENXIO && errno != EADDRNOTAVAIL)
14127827cba2SAaron LI 				logerr("%s: SIOCGIFALIAS", __func__);
14138d36e1dfSRoy Marples 			if (ifam->ifam_type != RTM_DELADDR)
14147827cba2SAaron LI 				break;
14156e63cc1fSRoy Marples 		} else {
14166e63cc1fSRoy Marples 			if (ifam->ifam_type == RTM_DELADDR)
14176e63cc1fSRoy Marples 				break;
14188d36e1dfSRoy Marples #if defined(__NetBSD_Version__) && __NetBSD_Version__ < 800000000
14197827cba2SAaron LI 			bcast = ifra.ifra_broadaddr.sin_addr;
14207827cba2SAaron LI #endif
14216e63cc1fSRoy Marples 		}
14228d36e1dfSRoy Marples #else
14238d36e1dfSRoy Marples #warning No SIOCGIFALIAS support
14248d36e1dfSRoy Marples 		/*
14258d36e1dfSRoy Marples 		 * No SIOCGIFALIAS? That sucks!
14268d36e1dfSRoy Marples 		 * This makes this call very heavy weight, but we
14278d36e1dfSRoy Marples 		 * really need to know if the message is late or not.
14288d36e1dfSRoy Marples 		 */
14298d36e1dfSRoy Marples 		const struct sockaddr *sa;
14308d36e1dfSRoy Marples 		struct ifaddrs *ifaddrs = NULL, *ifa;
14317827cba2SAaron LI 
14328d36e1dfSRoy Marples 		sa = rti_info[RTAX_IFA];
14337f8103cdSRoy Marples #ifdef PRIVSEP_GETIFADDRS
14347f8103cdSRoy Marples 		if (IN_PRIVSEP(ctx)) {
14357f8103cdSRoy Marples 			if (ps_root_getifaddrs(ctx, &ifaddrs) == -1) {
14367f8103cdSRoy Marples 				logerr("ps_root_getifaddrs");
14377f8103cdSRoy Marples 				break;
14387f8103cdSRoy Marples 			}
14397f8103cdSRoy Marples 		} else
14407f8103cdSRoy Marples #endif
14417f8103cdSRoy Marples 		if (getifaddrs(&ifaddrs) == -1) {
14427f8103cdSRoy Marples 			logerr("getifaddrs");
14437f8103cdSRoy Marples 			break;
14447f8103cdSRoy Marples 		}
14458d36e1dfSRoy Marples 		for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
14468d36e1dfSRoy Marples 			if (ifa->ifa_addr == NULL)
14478d36e1dfSRoy Marples 				continue;
14488d36e1dfSRoy Marples 			if (sa_cmp(ifa->ifa_addr, sa) == 0 &&
14498d36e1dfSRoy Marples 			    strcmp(ifa->ifa_name, ifp->name) == 0)
14507827cba2SAaron LI 				break;
14517827cba2SAaron LI 		}
14527f8103cdSRoy Marples #ifdef PRIVSEP_GETIFADDRS
14537f8103cdSRoy Marples 		if (IN_PRIVSEP(ctx))
14547f8103cdSRoy Marples 			free(ifaddrs);
14557f8103cdSRoy Marples 		else
14567f8103cdSRoy Marples #endif
14578d36e1dfSRoy Marples 		freeifaddrs(ifaddrs);
14586e63cc1fSRoy Marples 		if (ifam->ifam_type == RTM_DELADDR) {
14598d36e1dfSRoy Marples 			if (ifa != NULL)
14606e63cc1fSRoy Marples 				break;
14616e63cc1fSRoy Marples 		} else {
14626e63cc1fSRoy Marples 			if (ifa == NULL)
14637827cba2SAaron LI 				break;
14647827cba2SAaron LI 		}
14657827cba2SAaron LI #endif
14667827cba2SAaron LI 
14676e63cc1fSRoy Marples #ifdef HAVE_IFAM_ADDRFLAGS
14686e63cc1fSRoy Marples 		flags = ifam->ifam_addrflags;
14696e63cc1fSRoy Marples #else
14706e63cc1fSRoy Marples 		flags = 0;
14716e63cc1fSRoy Marples #endif
14726e63cc1fSRoy Marples 
14737827cba2SAaron LI 		ipv4_handleifa(ctx, ifam->ifam_type, NULL, ifp->name,
14746e63cc1fSRoy Marples 		    &addr, &mask, &bcast, flags, pid);
14757827cba2SAaron LI 		break;
14767827cba2SAaron LI 	}
14777827cba2SAaron LI #endif
14787827cba2SAaron LI #ifdef INET6
14797827cba2SAaron LI 	case AF_INET6:
14807827cba2SAaron LI 	{
14817827cba2SAaron LI 		struct in6_addr addr6, mask6;
14827827cba2SAaron LI 		const struct sockaddr_in6 *sin6;
14837827cba2SAaron LI 
14847827cba2SAaron LI 		sin6 = (const void *)rti_info[RTAX_IFA];
14857827cba2SAaron LI 		addr6 = sin6->sin6_addr;
14867827cba2SAaron LI 		sin6 = (const void *)rti_info[RTAX_NETMASK];
14877827cba2SAaron LI 		mask6 = sin6->sin6_addr;
14887827cba2SAaron LI 
14898d36e1dfSRoy Marples 		/*
14908d36e1dfSRoy Marples 		 * If the address was deleted, lets check if it's
14918d36e1dfSRoy Marples 		 * a late message and it still exists (maybe modified).
14928d36e1dfSRoy Marples 		 * If so, ignore it as deleting an address causes
14938d36e1dfSRoy Marples 		 * dhcpcd to drop any lease to which it belongs.
14946e63cc1fSRoy Marples 		 * Also check an added address was really added.
14958d36e1dfSRoy Marples 		 */
14968d36e1dfSRoy Marples 		flags = if_addrflags6(ifp, &addr6, NULL);
14976e63cc1fSRoy Marples 		if (flags == -1) {
14986e63cc1fSRoy Marples 			if (errno != ENXIO && errno != EADDRNOTAVAIL)
14997827cba2SAaron LI 				logerr("%s: if_addrflags6", __func__);
15006e63cc1fSRoy Marples 			if (ifam->ifam_type != RTM_DELADDR)
15017827cba2SAaron LI 				break;
15026e63cc1fSRoy Marples 			flags = 0;
15036e63cc1fSRoy Marples 		} else if (ifam->ifam_type == RTM_DELADDR)
15046e63cc1fSRoy Marples 			break;
15057827cba2SAaron LI 
15067827cba2SAaron LI #ifdef __KAME__
15077827cba2SAaron LI 		if (IN6_IS_ADDR_LINKLOCAL(&addr6))
15087827cba2SAaron LI 			/* Remove the scope from the address */
15097827cba2SAaron LI 			addr6.s6_addr[2] = addr6.s6_addr[3] = '\0';
15107827cba2SAaron LI #endif
15117827cba2SAaron LI 
15127827cba2SAaron LI 		ipv6_handleifa(ctx, ifam->ifam_type, NULL,
15136e63cc1fSRoy Marples 		    ifp->name, &addr6, ipv6_prefixlen(&mask6), flags, pid);
15147827cba2SAaron LI 		break;
15157827cba2SAaron LI 	}
15167827cba2SAaron LI #endif
15177827cba2SAaron LI 	}
15188d36e1dfSRoy Marples 
15198d36e1dfSRoy Marples 	return 0;
15207827cba2SAaron LI }
15217827cba2SAaron LI 
15228d36e1dfSRoy Marples static int
if_dispatch(struct dhcpcd_ctx * ctx,const struct rt_msghdr * rtm)15237827cba2SAaron LI if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
15247827cba2SAaron LI {
15257827cba2SAaron LI 
15267827cba2SAaron LI 	if (rtm->rtm_version != RTM_VERSION)
15278d36e1dfSRoy Marples 		return 0;
15287827cba2SAaron LI 
15297827cba2SAaron LI 	switch(rtm->rtm_type) {
15307827cba2SAaron LI #ifdef RTM_IFANNOUNCE
15317827cba2SAaron LI 	case RTM_IFANNOUNCE:
15328d36e1dfSRoy Marples 		return if_announce(ctx, (const void *)rtm);
15337827cba2SAaron LI #endif
15347827cba2SAaron LI 	case RTM_IFINFO:
15358d36e1dfSRoy Marples 		return if_ifinfo(ctx, (const void *)rtm);
15367827cba2SAaron LI 	case RTM_ADD:		/* FALLTHROUGH */
15377827cba2SAaron LI 	case RTM_CHANGE:	/* FALLTHROUGH */
15388d36e1dfSRoy Marples 	case RTM_DELETE:	/* FALLTHROUGH */
15398d36e1dfSRoy Marples 	case RTM_MISS:
15408d36e1dfSRoy Marples 		return if_rtm(ctx, (const void *)rtm);
15417827cba2SAaron LI #ifdef RTM_CHGADDR
15427827cba2SAaron LI 	case RTM_CHGADDR:	/* FALLTHROUGH */
15437827cba2SAaron LI #endif
15447827cba2SAaron LI 	case RTM_DELADDR:	/* FALLTHROUGH */
15457827cba2SAaron LI 	case RTM_NEWADDR:
15468d36e1dfSRoy Marples 		return if_ifa(ctx, (const void *)rtm);
15477827cba2SAaron LI #ifdef RTM_DESYNC
15487827cba2SAaron LI 	case RTM_DESYNC:
15497827cba2SAaron LI 		dhcpcd_linkoverflow(ctx);
15508d36e1dfSRoy Marples #elif !defined(SO_RERROR)
15518d36e1dfSRoy Marples #warning cannot detect route socket overflow within kernel
15527827cba2SAaron LI #endif
15537827cba2SAaron LI 	}
15548d36e1dfSRoy Marples 
15558d36e1dfSRoy Marples 	return 0;
15567827cba2SAaron LI }
15577827cba2SAaron LI 
15586e63cc1fSRoy Marples static int
if_missfilter0(struct dhcpcd_ctx * ctx,struct interface * ifp,struct sockaddr * sa)15596e63cc1fSRoy Marples if_missfilter0(struct dhcpcd_ctx *ctx, struct interface *ifp,
15606e63cc1fSRoy Marples     struct sockaddr *sa)
15616e63cc1fSRoy Marples {
15626e63cc1fSRoy Marples 	size_t salen = (size_t)RT_ROUNDUP(sa->sa_len);
15636e63cc1fSRoy Marples 	size_t newlen = ctx->rt_missfilterlen + salen;
15646e63cc1fSRoy Marples 	size_t diff = salen - (sa->sa_len);
15656e63cc1fSRoy Marples 	uint8_t *cp;
15666e63cc1fSRoy Marples 
15676e63cc1fSRoy Marples 	if (ctx->rt_missfiltersize < newlen) {
15686e63cc1fSRoy Marples 		void *n = realloc(ctx->rt_missfilter, newlen);
15696e63cc1fSRoy Marples 		if (n == NULL)
15706e63cc1fSRoy Marples 			return -1;
15716e63cc1fSRoy Marples 		ctx->rt_missfilter = n;
15726e63cc1fSRoy Marples 		ctx->rt_missfiltersize = newlen;
15736e63cc1fSRoy Marples 	}
15746e63cc1fSRoy Marples 
15756e63cc1fSRoy Marples #ifdef INET6
15766e63cc1fSRoy Marples 	if (sa->sa_family == AF_INET6)
1577d4fb1e02SRoy Marples 		ipv6_setscope(satosin6(sa), ifp->index);
1578280986e4SRoy Marples #else
1579280986e4SRoy Marples 	UNUSED(ifp);
15806e63cc1fSRoy Marples #endif
15816e63cc1fSRoy Marples 
15826e63cc1fSRoy Marples 	cp = ctx->rt_missfilter + ctx->rt_missfilterlen;
15836e63cc1fSRoy Marples 	memcpy(cp, sa, sa->sa_len);
15846e63cc1fSRoy Marples 	if (diff != 0)
15856e63cc1fSRoy Marples 		memset(cp + sa->sa_len, 0, diff);
15866e63cc1fSRoy Marples 	ctx->rt_missfilterlen += salen;
15876e63cc1fSRoy Marples 
15886e63cc1fSRoy Marples #ifdef INET6
15896e63cc1fSRoy Marples 	if (sa->sa_family == AF_INET6)
1590d4fb1e02SRoy Marples 		ipv6_setscope(satosin6(sa), 0);
15916e63cc1fSRoy Marples #endif
15926e63cc1fSRoy Marples 
15936e63cc1fSRoy Marples 	return 0;
15946e63cc1fSRoy Marples }
15956e63cc1fSRoy Marples 
15966e63cc1fSRoy Marples int
if_missfilter(struct interface * ifp,struct sockaddr * sa)15976e63cc1fSRoy Marples if_missfilter(struct interface *ifp, struct sockaddr *sa)
15986e63cc1fSRoy Marples {
15996e63cc1fSRoy Marples 
16006e63cc1fSRoy Marples 	return if_missfilter0(ifp->ctx, ifp, sa);
16016e63cc1fSRoy Marples }
16026e63cc1fSRoy Marples 
16036e63cc1fSRoy Marples int
if_missfilter_apply(struct dhcpcd_ctx * ctx)16046e63cc1fSRoy Marples if_missfilter_apply(struct dhcpcd_ctx *ctx)
16056e63cc1fSRoy Marples {
16066e63cc1fSRoy Marples #ifdef RO_MISSFILTER
16076e63cc1fSRoy Marples 	if (ctx->rt_missfilterlen == 0) {
16086e63cc1fSRoy Marples 		struct sockaddr sa = {
16096e63cc1fSRoy Marples 		    .sa_family = AF_UNSPEC,
16106e63cc1fSRoy Marples 		    .sa_len = sizeof(sa),
16116e63cc1fSRoy Marples 		};
16126e63cc1fSRoy Marples 
16136e63cc1fSRoy Marples 		if (if_missfilter0(ctx, NULL, &sa) == -1)
16146e63cc1fSRoy Marples 			return -1;
16156e63cc1fSRoy Marples 	}
16166e63cc1fSRoy Marples 
16176e63cc1fSRoy Marples 	return setsockopt(ctx->link_fd, PF_ROUTE, RO_MISSFILTER,
16186e63cc1fSRoy Marples 	    ctx->rt_missfilter, (socklen_t)ctx->rt_missfilterlen);
16196e63cc1fSRoy Marples #else
16206e63cc1fSRoy Marples #warning kernel does not support RTM_MISS DST filtering
16216e63cc1fSRoy Marples 	UNUSED(ctx);
16226e63cc1fSRoy Marples 	errno = ENOTSUP;
16236e63cc1fSRoy Marples 	return -1;
16246e63cc1fSRoy Marples #endif
16256e63cc1fSRoy Marples }
16266e63cc1fSRoy Marples 
16278d36e1dfSRoy Marples __CTASSERT(offsetof(struct rt_msghdr, rtm_msglen) == 0);
16287827cba2SAaron LI int
if_handlelink(struct dhcpcd_ctx * ctx)16297827cba2SAaron LI if_handlelink(struct dhcpcd_ctx *ctx)
16307827cba2SAaron LI {
16318d36e1dfSRoy Marples 	struct rtm rtm;
16327827cba2SAaron LI 	ssize_t len;
16337827cba2SAaron LI 
16348d36e1dfSRoy Marples 	len = read(ctx->link_fd, &rtm, sizeof(rtm));
16357827cba2SAaron LI 	if (len == -1)
16367827cba2SAaron LI 		return -1;
16378d36e1dfSRoy Marples 	if (len == 0)
16387827cba2SAaron LI 		return 0;
16398d36e1dfSRoy Marples 	if ((size_t)len < sizeof(rtm.hdr.rtm_msglen) ||
16408d36e1dfSRoy Marples 	    len != rtm.hdr.rtm_msglen)
16418d36e1dfSRoy Marples 	{
16428d36e1dfSRoy Marples 		errno = EINVAL;
16438d36e1dfSRoy Marples 		return -1;
16448d36e1dfSRoy Marples 	}
16458d36e1dfSRoy Marples 	/*
16468d36e1dfSRoy Marples 	 * Coverity thinks that the data could be tainted from here.
16478d36e1dfSRoy Marples 	 * I have no idea how because the length of the data we read
16488d36e1dfSRoy Marples 	 * is guarded by len and checked to match rtm_msglen.
16498d36e1dfSRoy Marples 	 * The issue seems to be related to extracting the addresses
16508d36e1dfSRoy Marples 	 * at the end of the header, but seems to have no issues with the
16518d36e1dfSRoy Marples 	 * equivalent call in if_initrt.
16528d36e1dfSRoy Marples 	 */
16538d36e1dfSRoy Marples 	/* coverity[tainted_data] */
16548d36e1dfSRoy Marples 	return if_dispatch(ctx, &rtm.hdr);
16557827cba2SAaron LI }
16567827cba2SAaron LI 
16577827cba2SAaron LI #ifndef SYS_NMLN	/* OSX */
1658d4fb1e02SRoy Marples #  define SYS_NMLN __SYS_NAMELEN
16597827cba2SAaron LI #endif
16607827cba2SAaron LI #ifndef HW_MACHINE_ARCH
16617827cba2SAaron LI #  ifdef HW_MODEL	/* OpenBSD */
16627827cba2SAaron LI #    define HW_MACHINE_ARCH HW_MODEL
16637827cba2SAaron LI #  endif
16647827cba2SAaron LI #endif
16657827cba2SAaron LI int
if_machinearch(char * str,size_t len)16667827cba2SAaron LI if_machinearch(char *str, size_t len)
16677827cba2SAaron LI {
16687827cba2SAaron LI 	int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
16697827cba2SAaron LI 
1670d4fb1e02SRoy Marples 	return sysctl(mib, sizeof(mib) / sizeof(mib[0]), str, &len, NULL, 0);
16717827cba2SAaron LI }
16727827cba2SAaron LI 
16737827cba2SAaron LI #ifdef INET6
16747827cba2SAaron LI #if (defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV)) || \
16758d36e1dfSRoy Marples     defined(IPV6CTL_FORWARDING)
16767827cba2SAaron LI #define get_inet6_sysctl(code) inet6_sysctl(code, 0, 0)
16777827cba2SAaron LI #define set_inet6_sysctl(code, val) inet6_sysctl(code, val, 1)
16787827cba2SAaron LI static int
inet6_sysctl(int code,int val,int action)16797827cba2SAaron LI inet6_sysctl(int code, int val, int action)
16807827cba2SAaron LI {
16817827cba2SAaron LI 	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
16827827cba2SAaron LI 	size_t size;
16837827cba2SAaron LI 
16847827cba2SAaron LI 	mib[3] = code;
16857827cba2SAaron LI 	size = sizeof(val);
16867827cba2SAaron LI 	if (action) {
168780aa9461SRoy Marples 		if (sysctl(mib, __arraycount(mib), NULL, 0, &val, size) == -1)
16887827cba2SAaron LI 			return -1;
16897827cba2SAaron LI 		return 0;
16907827cba2SAaron LI 	}
169180aa9461SRoy Marples 	if (sysctl(mib, __arraycount(mib), &val, &size, NULL, 0) == -1)
16927827cba2SAaron LI 		return -1;
16937827cba2SAaron LI 	return val;
16947827cba2SAaron LI }
16957827cba2SAaron LI #endif
16967827cba2SAaron LI 
1697b9ccd228SRoy Marples int
if_applyra(const struct ra * rap)1698b9ccd228SRoy Marples if_applyra(const struct ra *rap)
1699b9ccd228SRoy Marples {
1700b9ccd228SRoy Marples #ifdef SIOCSIFINFO_IN6
17016e63cc1fSRoy Marples 	struct in6_ndireq nd = { .ndi.chlim = 0 };
17026e63cc1fSRoy Marples 	struct dhcpcd_ctx *ctx = rap->iface->ctx;
1703b9ccd228SRoy Marples 	int error;
1704b9ccd228SRoy Marples 
17056e63cc1fSRoy Marples 	strlcpy(nd.ifname, rap->iface->name, sizeof(nd.ifname));
1706d4fb1e02SRoy Marples 
1707d4fb1e02SRoy Marples #ifdef IPV6CTL_ACCEPT_RTADV
1708d4fb1e02SRoy Marples 	struct priv *priv = ctx->priv;
1709d4fb1e02SRoy Marples 
1710d4fb1e02SRoy Marples 	/*
1711d4fb1e02SRoy Marples 	 * NetBSD changed SIOCSIFINFO_IN6 to NOT set flags when kernel
1712d4fb1e02SRoy Marples 	 * RA was removed, however both FreeBSD and DragonFlyBSD still do.
1713d4fb1e02SRoy Marples 	 * linkmtu was also removed.
1714d4fb1e02SRoy Marples 	 * Hopefully this guard will still work if either remove kernel RA.
1715d4fb1e02SRoy Marples 	 */
17166e63cc1fSRoy Marples 	if (ioctl(priv->pf_inet6_fd, SIOCGIFINFO_IN6, &nd, sizeof(nd)) == -1)
1717b9ccd228SRoy Marples 		return -1;
1718b9ccd228SRoy Marples 
17196e63cc1fSRoy Marples 	nd.ndi.linkmtu = rap->mtu;
1720d4fb1e02SRoy Marples #endif
1721d4fb1e02SRoy Marples 
17226e63cc1fSRoy Marples 	nd.ndi.chlim = rap->hoplimit;
17236e63cc1fSRoy Marples 	nd.ndi.retrans = rap->retrans;
17246e63cc1fSRoy Marples 	nd.ndi.basereachable = rap->reachable;
17256e63cc1fSRoy Marples 	error = if_ioctl6(ctx, SIOCSIFINFO_IN6, &nd, sizeof(nd));
1726d4fb1e02SRoy Marples #ifdef IPV6CTL_ACCEPT_RTADV
1727b9ccd228SRoy Marples 	if (error == -1 && errno == EINVAL) {
1728b9ccd228SRoy Marples 		/*
1729b9ccd228SRoy Marples 		 * Very likely that this is caused by a dodgy MTU
1730b9ccd228SRoy Marples 		 * setting specific to the interface.
1731b9ccd228SRoy Marples 		 * Let's set it to "unspecified" and try again.
1732b9ccd228SRoy Marples 		 * Doesn't really matter as we fix the MTU against the
1733b9ccd228SRoy Marples 		 * routes we add as not all OS support SIOCSIFINFO_IN6.
1734b9ccd228SRoy Marples 		 */
17356e63cc1fSRoy Marples 		nd.ndi.linkmtu = 0;
17366e63cc1fSRoy Marples 		error = if_ioctl6(ctx, SIOCSIFINFO_IN6, &nd, sizeof(nd));
1737b9ccd228SRoy Marples 	}
1738d4fb1e02SRoy Marples #endif
1739b9ccd228SRoy Marples 	return error;
1740b9ccd228SRoy Marples #else
1741b9ccd228SRoy Marples #warning OS does not allow setting of RA bits hoplimit, retrans or reachable
1742b9ccd228SRoy Marples 	UNUSED(rap);
1743b9ccd228SRoy Marples 	return 0;
1744b9ccd228SRoy Marples #endif
1745b9ccd228SRoy Marples }
1746b9ccd228SRoy Marples 
17477a0236bfSRoy Marples #ifndef IPV6CTL_FORWARDING
17487827cba2SAaron LI #define get_inet6_sysctlbyname(code) inet6_sysctlbyname(code, 0, 0)
17497827cba2SAaron LI #define set_inet6_sysctlbyname(code, val) inet6_sysctlbyname(code, val, 1)
17507827cba2SAaron LI static int
inet6_sysctlbyname(const char * name,int val,int action)17517827cba2SAaron LI inet6_sysctlbyname(const char *name, int val, int action)
17527827cba2SAaron LI {
17537827cba2SAaron LI 	size_t size;
17547827cba2SAaron LI 
17557827cba2SAaron LI 	size = sizeof(val);
17567827cba2SAaron LI 	if (action) {
17577827cba2SAaron LI 		if (sysctlbyname(name, NULL, 0, &val, size) == -1)
17587827cba2SAaron LI 			return -1;
17597827cba2SAaron LI 		return 0;
17607827cba2SAaron LI 	}
17617827cba2SAaron LI 	if (sysctlbyname(name, &val, &size, NULL, 0) == -1)
17627827cba2SAaron LI 		return -1;
17637827cba2SAaron LI 	return val;
17647827cba2SAaron LI }
17657827cba2SAaron LI #endif
17667827cba2SAaron LI 
17678d36e1dfSRoy Marples int
ip6_forwarding(__unused const char * ifname)17688d36e1dfSRoy Marples ip6_forwarding(__unused const char *ifname)
17698d36e1dfSRoy Marples {
17708d36e1dfSRoy Marples 	int val;
17718d36e1dfSRoy Marples 
17728d36e1dfSRoy Marples #ifdef IPV6CTL_FORWARDING
17738d36e1dfSRoy Marples 	val = get_inet6_sysctl(IPV6CTL_FORWARDING);
17748d36e1dfSRoy Marples #else
17758d36e1dfSRoy Marples 	val = get_inet6_sysctlbyname("net.inet6.ip6.forwarding");
17768d36e1dfSRoy Marples #endif
17778d36e1dfSRoy Marples 	return val < 0 ? 0 : val;
17788d36e1dfSRoy Marples }
17798d36e1dfSRoy Marples 
17807827cba2SAaron LI #ifdef SIOCIFAFATTACH
17817827cba2SAaron LI static int
if_af_attach(const struct interface * ifp,int af)17826e63cc1fSRoy Marples if_af_attach(const struct interface *ifp, int af)
17837827cba2SAaron LI {
1784f3744ac9SRoy Marples 	struct if_afreq ifar = { .ifar_af = af };
17857827cba2SAaron LI 
17867827cba2SAaron LI 	strlcpy(ifar.ifar_name, ifp->name, sizeof(ifar.ifar_name));
17876e63cc1fSRoy Marples 	return if_ioctl6(ifp->ctx, SIOCIFAFATTACH, &ifar, sizeof(ifar));
17887827cba2SAaron LI }
17897827cba2SAaron LI #endif
17907827cba2SAaron LI 
17917827cba2SAaron LI #ifdef SIOCGIFXFLAGS
17927827cba2SAaron LI static int
if_set_ifxflags(const struct interface * ifp)17936e63cc1fSRoy Marples if_set_ifxflags(const struct interface *ifp)
17947827cba2SAaron LI {
17957827cba2SAaron LI 	struct ifreq ifr;
17967827cba2SAaron LI 	int flags;
17976e63cc1fSRoy Marples 	struct priv *priv = ifp->ctx->priv;
17987827cba2SAaron LI 
17997827cba2SAaron LI 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
18006e63cc1fSRoy Marples 	if (ioctl(priv->pf_inet6_fd, SIOCGIFXFLAGS, &ifr) == -1)
18017827cba2SAaron LI 		return -1;
18027827cba2SAaron LI 	flags = ifr.ifr_flags;
18037827cba2SAaron LI #ifdef IFXF_NOINET6
18047827cba2SAaron LI 	flags &= ~IFXF_NOINET6;
18057827cba2SAaron LI #endif
18067827cba2SAaron LI 	/*
18077827cba2SAaron LI 	 * If not doing autoconf, don't disable the kernel from doing it.
18087827cba2SAaron LI 	 * If we need to, we should have another option actively disable it.
18098d36e1dfSRoy Marples 	 *
18108d36e1dfSRoy Marples 	 * OpenBSD moved from kernel based SLAAC to userland via slaacd(8).
18118d36e1dfSRoy Marples 	 * It has a similar featureset to dhcpcd such as stable private
18128d36e1dfSRoy Marples 	 * addresses, but lacks the ability to handle DNS inside the RA
18138d36e1dfSRoy Marples 	 * which is a serious shortfall in this day and age.
18148d36e1dfSRoy Marples 	 * Appease their user base by working alongside slaacd(8) if
18158d36e1dfSRoy Marples 	 * dhcpcd is instructed not to do auto configuration of addresses.
18167827cba2SAaron LI 	 */
18178d36e1dfSRoy Marples #if defined(ND6_IFF_ACCEPT_RTADV)
18188d36e1dfSRoy Marples #define	BSD_AUTOCONF	DHCPCD_IPV6RS
18198d36e1dfSRoy Marples #else
18208d36e1dfSRoy Marples #define	BSD_AUTOCONF	DHCPCD_IPV6RA_AUTOCONF
18218d36e1dfSRoy Marples #endif
18228d36e1dfSRoy Marples 	if (ifp->options->options & BSD_AUTOCONF)
18237827cba2SAaron LI 		flags &= ~IFXF_AUTOCONF6;
18247827cba2SAaron LI 	if (ifr.ifr_flags == flags)
18257827cba2SAaron LI 		return 0;
18267827cba2SAaron LI 	ifr.ifr_flags = flags;
18276e63cc1fSRoy Marples 	return if_ioctl6(ifp->ctx, SIOCSIFXFLAGS, &ifr, sizeof(ifr));
18287827cba2SAaron LI }
18297827cba2SAaron LI #endif
18307827cba2SAaron LI 
18317827cba2SAaron LI /* OpenBSD removed ND6 flags entirely, so we need to check for their
18327827cba2SAaron LI  * existance. */
18337827cba2SAaron LI #if defined(ND6_IFF_AUTO_LINKLOCAL) || \
18347827cba2SAaron LI     defined(ND6_IFF_PERFORMNUD) || \
18357827cba2SAaron LI     defined(ND6_IFF_ACCEPT_RTADV) || \
18367827cba2SAaron LI     defined(ND6_IFF_OVERRIDE_RTADV) || \
18377827cba2SAaron LI     defined(ND6_IFF_IFDISABLED)
18387827cba2SAaron LI #define	ND6_NDI_FLAGS
18397827cba2SAaron LI #endif
18407827cba2SAaron LI 
18417827cba2SAaron LI void
if_disable_rtadv(void)1842b9ccd228SRoy Marples if_disable_rtadv(void)
1843b9ccd228SRoy Marples {
1844b9ccd228SRoy Marples #if defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV)
1845b9ccd228SRoy Marples 	int ra = get_inet6_sysctl(IPV6CTL_ACCEPT_RTADV);
1846b9ccd228SRoy Marples 
1847b9ccd228SRoy Marples 	if (ra == -1) {
1848b9ccd228SRoy Marples 		if (errno != ENOENT)
1849b9ccd228SRoy Marples 			logerr("IPV6CTL_ACCEPT_RTADV");
1850b9ccd228SRoy Marples 	else if (ra != 0)
1851b9ccd228SRoy Marples 		if (set_inet6_sysctl(IPV6CTL_ACCEPT_RTADV, 0) == -1)
1852b9ccd228SRoy Marples 			logerr("IPV6CTL_ACCEPT_RTADV");
1853b9ccd228SRoy Marples 	}
1854b9ccd228SRoy Marples #endif
1855b9ccd228SRoy Marples }
1856b9ccd228SRoy Marples 
1857b9ccd228SRoy Marples void
if_setup_inet6(const struct interface * ifp)18587827cba2SAaron LI if_setup_inet6(const struct interface *ifp)
18597827cba2SAaron LI {
1860f3744ac9SRoy Marples #ifdef ND6_NDI_FLAGS
18617827cba2SAaron LI 	struct priv *priv;
18627827cba2SAaron LI 	int s;
18637827cba2SAaron LI 	struct in6_ndireq nd;
18647827cba2SAaron LI 	int flags;
18657827cba2SAaron LI 
18667827cba2SAaron LI 	priv = (struct priv *)ifp->ctx->priv;
18677827cba2SAaron LI 	s = priv->pf_inet6_fd;
18687827cba2SAaron LI 
18697827cba2SAaron LI 	memset(&nd, 0, sizeof(nd));
18707827cba2SAaron LI 	strlcpy(nd.ifname, ifp->name, sizeof(nd.ifname));
18717827cba2SAaron LI 	if (ioctl(s, SIOCGIFINFO_IN6, &nd) == -1)
18727827cba2SAaron LI 		logerr("%s: SIOCGIFINFO_FLAGS", ifp->name);
18737827cba2SAaron LI 	flags = (int)nd.ndi.flags;
18747827cba2SAaron LI 
18757827cba2SAaron LI #ifdef ND6_IFF_AUTO_LINKLOCAL
1876d4fb1e02SRoy Marples 	/* Unlike the kernel, dhcpcd make make a stable private address. */
18777827cba2SAaron LI 	flags &= ~ND6_IFF_AUTO_LINKLOCAL;
18787827cba2SAaron LI #endif
18797827cba2SAaron LI 
18807827cba2SAaron LI #ifdef ND6_IFF_PERFORMNUD
18817827cba2SAaron LI 	/* NUD is kind of essential. */
18827827cba2SAaron LI 	flags |= ND6_IFF_PERFORMNUD;
18837827cba2SAaron LI #endif
18847827cba2SAaron LI 
18857827cba2SAaron LI #ifdef ND6_IFF_IFDISABLED
18867827cba2SAaron LI 	/* Ensure the interface is not disabled. */
18877827cba2SAaron LI 	flags &= ~ND6_IFF_IFDISABLED;
18887827cba2SAaron LI #endif
18897827cba2SAaron LI 
18907827cba2SAaron LI 	/*
18917827cba2SAaron LI 	 * If not doing autoconf, don't disable the kernel from doing it.
18927827cba2SAaron LI 	 * If we need to, we should have another option actively disable it.
18937827cba2SAaron LI 	 */
18947827cba2SAaron LI #ifdef ND6_IFF_ACCEPT_RTADV
18957827cba2SAaron LI 	if (ifp->options->options & DHCPCD_IPV6RS)
18967827cba2SAaron LI 		flags &= ~ND6_IFF_ACCEPT_RTADV;
18977827cba2SAaron LI #ifdef ND6_IFF_OVERRIDE_RTADV
18987827cba2SAaron LI 	if (ifp->options->options & DHCPCD_IPV6RS)
18997827cba2SAaron LI 		flags |= ND6_IFF_OVERRIDE_RTADV;
19007827cba2SAaron LI #endif
19017827cba2SAaron LI #endif
19027827cba2SAaron LI 
19037827cba2SAaron LI 	if (nd.ndi.flags != (uint32_t)flags) {
19047827cba2SAaron LI 		nd.ndi.flags = (uint32_t)flags;
19056e63cc1fSRoy Marples 		if (if_ioctl6(ifp->ctx, SIOCSIFINFO_FLAGS,
19066e63cc1fSRoy Marples 		    &nd, sizeof(nd)) == -1)
19077827cba2SAaron LI 			logerr("%s: SIOCSIFINFO_FLAGS", ifp->name);
19087827cba2SAaron LI 	}
1909f3744ac9SRoy Marples #endif /* ND6_NDI_FLAGS */
19107827cba2SAaron LI 
19117827cba2SAaron LI 	/* Enabling IPv6 by whatever means must be the
19127827cba2SAaron LI 	 * last action undertaken to ensure kernel RS and
19137827cba2SAaron LI 	 * LLADDR auto configuration are disabled where applicable. */
19147827cba2SAaron LI #ifdef SIOCIFAFATTACH
19156e63cc1fSRoy Marples 	if (if_af_attach(ifp, AF_INET6) == -1)
19166e63cc1fSRoy Marples 		logerr("%s: if_af_attach", ifp->name);
19177827cba2SAaron LI #endif
19187827cba2SAaron LI 
19197827cba2SAaron LI #ifdef SIOCGIFXFLAGS
19206e63cc1fSRoy Marples 	if (if_set_ifxflags(ifp) == -1)
19217827cba2SAaron LI 		logerr("%s: set_ifxflags", ifp->name);
19227827cba2SAaron LI #endif
19237827cba2SAaron LI 
19247a0236bfSRoy Marples #ifdef SIOCSRTRFLUSH_IN6
19257827cba2SAaron LI 	/* Flush the kernel knowledge of advertised routers
19267827cba2SAaron LI 	 * and prefixes so the kernel does not expire prefixes
19277827cba2SAaron LI 	 * and default routes we are trying to own. */
19287827cba2SAaron LI 	if (ifp->options->options & DHCPCD_IPV6RS) {
19298d36e1dfSRoy Marples 		struct in6_ifreq ifr;
19307827cba2SAaron LI 
19318d36e1dfSRoy Marples 		memset(&ifr, 0, sizeof(ifr));
19328d36e1dfSRoy Marples 		strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
19336e63cc1fSRoy Marples 		if (if_ioctl6(ifp->ctx, SIOCSRTRFLUSH_IN6,
19346e63cc1fSRoy Marples 		    &ifr, sizeof(ifr)) == -1 &&
19357a0236bfSRoy Marples 		    errno != ENOTSUP && errno != ENOTTY)
19367a0236bfSRoy Marples 			logwarn("SIOCSRTRFLUSH_IN6 %d", errno);
19377a0236bfSRoy Marples #ifdef SIOCSPFXFLUSH_IN6
19386e63cc1fSRoy Marples 		if (if_ioctl6(ifp->ctx, SIOCSPFXFLUSH_IN6,
19396e63cc1fSRoy Marples 		    &ifr, sizeof(ifr)) == -1 &&
19407a0236bfSRoy Marples 		    errno != ENOTSUP && errno != ENOTTY)
19417827cba2SAaron LI 			logwarn("SIOCSPFXFLUSH_IN6");
19427a0236bfSRoy Marples #endif
19437827cba2SAaron LI 	}
19447827cba2SAaron LI #endif
19457827cba2SAaron LI }
19467827cba2SAaron LI #endif
1947