xref: /dflybsd-src/contrib/dhcpcd/src/ipv6.c (revision b2927f2b6ddfb64c8f3a45f76e21fb8e0fcbe11a)
18d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI  * dhcpcd - DHCP client daemon
46e63cc1fSRoy Marples  * Copyright (c) 2006-2020 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/param.h>
307827cba2SAaron LI #include <sys/types.h>
317827cba2SAaron LI #include <sys/socket.h>
327827cba2SAaron LI #include <sys/stat.h>
337827cba2SAaron LI 
348d36e1dfSRoy Marples #include <arpa/inet.h>
357827cba2SAaron LI #include <net/if.h>
367827cba2SAaron LI #include <net/route.h>
377827cba2SAaron LI #include <netinet/in.h>
387827cba2SAaron LI #include <netinet/if_ether.h>
397827cba2SAaron LI 
407827cba2SAaron LI #include "config.h"
417827cba2SAaron LI 
427827cba2SAaron LI #ifdef HAVE_SYS_BITOPS_H
437827cba2SAaron LI #include <sys/bitops.h>
447827cba2SAaron LI #else
457827cba2SAaron LI #include "compat/bitops.h"
467827cba2SAaron LI #endif
477827cba2SAaron LI 
487827cba2SAaron LI #ifdef BSD
497827cba2SAaron LI /* Purely for the ND6_IFF_AUTO_LINKLOCAL #define which is solely used
507827cba2SAaron LI  * to generate our CAN_ADD_LLADDR #define. */
517827cba2SAaron LI #  include <netinet6/in6_var.h>
527827cba2SAaron LI #  include <netinet6/nd6.h>
537827cba2SAaron LI #endif
547827cba2SAaron LI 
557827cba2SAaron LI #include <errno.h>
567827cba2SAaron LI #include <ifaddrs.h>
577827cba2SAaron LI #include <inttypes.h>
587827cba2SAaron LI #include <stdlib.h>
597827cba2SAaron LI #include <string.h>
606e63cc1fSRoy Marples #include <syslog.h>
617827cba2SAaron LI #include <unistd.h>
627827cba2SAaron LI 
636e63cc1fSRoy Marples #define ELOOP_QUEUE	ELOOP_IPV6
647827cba2SAaron LI #include "common.h"
657827cba2SAaron LI #include "if.h"
667827cba2SAaron LI #include "dhcpcd.h"
677827cba2SAaron LI #include "dhcp6.h"
687827cba2SAaron LI #include "eloop.h"
697827cba2SAaron LI #include "ipv6.h"
707827cba2SAaron LI #include "ipv6nd.h"
717827cba2SAaron LI #include "logerr.h"
72d4fb1e02SRoy Marples #include "privsep.h"
737827cba2SAaron LI #include "sa.h"
747827cba2SAaron LI #include "script.h"
757827cba2SAaron LI 
767827cba2SAaron LI #ifdef HAVE_MD5_H
777827cba2SAaron LI #  ifndef DEPGEN
787827cba2SAaron LI #    include <md5.h>
797827cba2SAaron LI #  endif
807827cba2SAaron LI #endif
817827cba2SAaron LI 
827827cba2SAaron LI #ifdef SHA2_H
837827cba2SAaron LI #  include SHA2_H
847827cba2SAaron LI #endif
857827cba2SAaron LI 
867827cba2SAaron LI #ifndef SHA256_DIGEST_LENGTH
877827cba2SAaron LI #  define SHA256_DIGEST_LENGTH		32
887827cba2SAaron LI #endif
897827cba2SAaron LI 
907827cba2SAaron LI #ifdef IPV6_POLLADDRFLAG
917827cba2SAaron LI #  warning kernel does not report IPv6 address flag changes
927827cba2SAaron LI #  warning polling tentative address flags periodically
937827cba2SAaron LI #endif
947827cba2SAaron LI 
957827cba2SAaron LI /* Hackery at it's finest. */
967827cba2SAaron LI #ifndef s6_addr32
977827cba2SAaron LI #  ifdef __sun
987827cba2SAaron LI #    define s6_addr32	_S6_un._S6_u32
997827cba2SAaron LI #  else
1007827cba2SAaron LI #    define s6_addr32	__u6_addr.__u6_addr32
1017827cba2SAaron LI #  endif
1027827cba2SAaron LI #endif
1037827cba2SAaron LI 
1047827cba2SAaron LI #if defined(HAVE_IN6_ADDR_GEN_MODE_NONE) || defined(ND6_IFF_AUTO_LINKLOCAL) || \
1057827cba2SAaron LI     defined(IFF_NOLINKLOCAL)
1067827cba2SAaron LI /* Only add the LL address if we have a carrier, so DaD works. */
1077827cba2SAaron LI #define	CAN_ADD_LLADDR(ifp) \
10893ddca5eSRoy Marples     (!((ifp)->options->options & DHCPCD_LINK) || if_is_link_up((ifp)))
1097827cba2SAaron LI #ifdef __sun
1107827cba2SAaron LI /* Although we can add our own LL address, we cannot drop it
1117827cba2SAaron LI  * without unplumbing the if which is a lot of code.
1127827cba2SAaron LI  * So just keep it for the time being. */
1137827cba2SAaron LI #define	CAN_DROP_LLADDR(ifp)	(0)
1147827cba2SAaron LI #else
1157827cba2SAaron LI #define	CAN_DROP_LLADDR(ifp)	(1)
1167827cba2SAaron LI #endif
1177827cba2SAaron LI #else
1187827cba2SAaron LI /* We have no control over the OS adding the LLADDR, so just let it do it
1197827cba2SAaron LI  * as we cannot force our own view on it. */
1207827cba2SAaron LI #define	CAN_ADD_LLADDR(ifp)	(0)
1217827cba2SAaron LI #define	CAN_DROP_LLADDR(ifp)	(0)
1227827cba2SAaron LI #endif
1237827cba2SAaron LI 
1247827cba2SAaron LI #ifdef IPV6_MANAGETEMPADDR
1257827cba2SAaron LI static void ipv6_regentempaddr(void *);
1267827cba2SAaron LI #endif
1277827cba2SAaron LI 
1287827cba2SAaron LI int
1297827cba2SAaron LI ipv6_init(struct dhcpcd_ctx *ctx)
1307827cba2SAaron LI {
1317827cba2SAaron LI 
1328d36e1dfSRoy Marples 	if (ctx->ra_routers != NULL)
1337827cba2SAaron LI 		return 0;
1347827cba2SAaron LI 
1357827cba2SAaron LI 	ctx->ra_routers = malloc(sizeof(*ctx->ra_routers));
1367827cba2SAaron LI 	if (ctx->ra_routers == NULL)
1377827cba2SAaron LI 		return -1;
1387827cba2SAaron LI 	TAILQ_INIT(ctx->ra_routers);
1397827cba2SAaron LI 
1408d36e1dfSRoy Marples #ifndef __sun
1417827cba2SAaron LI 	ctx->nd_fd = -1;
1428d36e1dfSRoy Marples #endif
143d4fb1e02SRoy Marples #ifdef DHCP6
144d4fb1e02SRoy Marples 	ctx->dhcp6_rfd = -1;
145d4fb1e02SRoy Marples 	ctx->dhcp6_wfd = -1;
146d4fb1e02SRoy Marples #endif
1477827cba2SAaron LI 	return 0;
1487827cba2SAaron LI }
1497827cba2SAaron LI 
1507827cba2SAaron LI static ssize_t
1517827cba2SAaron LI ipv6_readsecret(struct dhcpcd_ctx *ctx)
1527827cba2SAaron LI {
1537827cba2SAaron LI 	char line[1024];
1547827cba2SAaron LI 	unsigned char *p;
1557827cba2SAaron LI 	size_t len;
1567827cba2SAaron LI 	uint32_t r;
1577827cba2SAaron LI 
158d4fb1e02SRoy Marples 	ctx->secret_len = dhcp_read_hwaddr_aton(ctx, &ctx->secret, SECRET);
159d4fb1e02SRoy Marples 	if (ctx->secret_len != 0)
1607827cba2SAaron LI 		return (ssize_t)ctx->secret_len;
1617827cba2SAaron LI 
1627827cba2SAaron LI 	if (errno != ENOENT)
163d4fb1e02SRoy Marples 		logerr("%s: cannot read secret", __func__);
1647827cba2SAaron LI 
1657827cba2SAaron LI 	/* Chaining arc4random should be good enough.
1667827cba2SAaron LI 	 * RFC7217 section 5.1 states the key SHOULD be at least 128 bits.
1677827cba2SAaron LI 	 * To attempt and future proof ourselves, we'll generate a key of
1687827cba2SAaron LI 	 * 512 bits (64 bytes). */
1697827cba2SAaron LI 	if (ctx->secret_len < 64) {
1707827cba2SAaron LI 		if ((ctx->secret = malloc(64)) == NULL) {
1717827cba2SAaron LI 			logerr(__func__);
1727827cba2SAaron LI 			return -1;
1737827cba2SAaron LI 		}
1747827cba2SAaron LI 		ctx->secret_len = 64;
1757827cba2SAaron LI 	}
1767827cba2SAaron LI 	p = ctx->secret;
1777827cba2SAaron LI 	for (len = 0; len < 512 / NBBY; len += sizeof(r)) {
1787827cba2SAaron LI 		r = arc4random();
1797827cba2SAaron LI 		memcpy(p, &r, sizeof(r));
1807827cba2SAaron LI 		p += sizeof(r);
1817827cba2SAaron LI 	}
1827827cba2SAaron LI 
183d4fb1e02SRoy Marples 	hwaddr_ntoa(ctx->secret, ctx->secret_len, line, sizeof(line));
184d4fb1e02SRoy Marples 	len = strlen(line);
185d4fb1e02SRoy Marples 	if (len < sizeof(line) - 2) {
186d4fb1e02SRoy Marples 		line[len++] = '\n';
187d4fb1e02SRoy Marples 		line[len] = '\0';
188d4fb1e02SRoy Marples 	}
189d4fb1e02SRoy Marples 	if (dhcp_writefile(ctx, SECRET, S_IRUSR, line, len) == -1) {
190d4fb1e02SRoy Marples 		logerr("%s: cannot write secret", __func__);
1917827cba2SAaron LI 		ctx->secret_len = 0;
1927827cba2SAaron LI 		return -1;
1937827cba2SAaron LI 	}
194d4fb1e02SRoy Marples 	return (ssize_t)ctx->secret_len;
195d4fb1e02SRoy Marples }
1967827cba2SAaron LI 
1977827cba2SAaron LI /* http://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xhtml
1987827cba2SAaron LI  * RFC5453 */
1997827cba2SAaron LI static const struct reslowhigh {
2007827cba2SAaron LI 	const uint8_t high[8];
2017827cba2SAaron LI 	const uint8_t low[8];
2027827cba2SAaron LI } reslowhigh[] = {
2037827cba2SAaron LI 	/* RFC4291 + RFC6543 */
2047827cba2SAaron LI 	{ { 0x02, 0x00, 0x5e, 0xff, 0xfe, 0x00, 0x00, 0x00 },
2057827cba2SAaron LI 	  { 0x02, 0x00, 0x5e, 0xff, 0xfe, 0xff, 0xff, 0xff } },
2067827cba2SAaron LI 	/* RFC2526 */
2077827cba2SAaron LI 	{ { 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 },
2087827cba2SAaron LI 	  { 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }
2097827cba2SAaron LI };
2107827cba2SAaron LI 
211280986e4SRoy Marples static bool
2127827cba2SAaron LI ipv6_reserved(const struct in6_addr *addr)
2137827cba2SAaron LI {
2147827cba2SAaron LI 	uint64_t id, low, high;
2157827cba2SAaron LI 	size_t i;
2167827cba2SAaron LI 	const struct reslowhigh *r;
2177827cba2SAaron LI 
2187827cba2SAaron LI 	id = be64dec(addr->s6_addr + sizeof(id));
2197827cba2SAaron LI 	if (id == 0) /* RFC4291 */
2207827cba2SAaron LI 		return 1;
221280986e4SRoy Marples 	for (i = 0; i < __arraycount(reslowhigh); i++) {
2227827cba2SAaron LI 		r = &reslowhigh[i];
2237827cba2SAaron LI 		low = be64dec(r->low);
2247827cba2SAaron LI 		high = be64dec(r->high);
2257827cba2SAaron LI 		if (id >= low && id <= high)
226280986e4SRoy Marples 			return true;
2277827cba2SAaron LI 	}
228280986e4SRoy Marples 	return false;
2297827cba2SAaron LI }
2307827cba2SAaron LI 
2317827cba2SAaron LI /* RFC7217 */
2327827cba2SAaron LI static int
233280986e4SRoy Marples ipv6_makestableprivate1(struct dhcpcd_ctx *ctx,
234280986e4SRoy Marples     struct in6_addr *addr, const struct in6_addr *prefix, int prefix_len,
2357827cba2SAaron LI     const unsigned char *netiface, size_t netiface_len,
2367827cba2SAaron LI     const unsigned char *netid, size_t netid_len,
2377827cba2SAaron LI     unsigned short vlanid,
238280986e4SRoy Marples     uint32_t *dad_counter)
2397827cba2SAaron LI {
2407827cba2SAaron LI 	unsigned char buf[2048], *p, digest[SHA256_DIGEST_LENGTH];
2417827cba2SAaron LI 	size_t len, l;
242280986e4SRoy Marples 	SHA256_CTX sha_ctx;
2437827cba2SAaron LI 
2447827cba2SAaron LI 	if (prefix_len < 0 || prefix_len > 120) {
2457827cba2SAaron LI 		errno = EINVAL;
2467827cba2SAaron LI 		return -1;
2477827cba2SAaron LI 	}
2487827cba2SAaron LI 
249280986e4SRoy Marples 	if (ctx->secret_len == 0) {
250280986e4SRoy Marples 		if (ipv6_readsecret(ctx) == -1)
251280986e4SRoy Marples 			return -1;
252280986e4SRoy Marples 	}
253280986e4SRoy Marples 
2547827cba2SAaron LI 	l = (size_t)(ROUNDUP8(prefix_len) / NBBY);
255280986e4SRoy Marples 	len = l + netiface_len + netid_len + sizeof(*dad_counter) +
256280986e4SRoy Marples 	    ctx->secret_len;
2577827cba2SAaron LI 	if (vlanid != 0)
2587827cba2SAaron LI 		len += sizeof(vlanid);
2597827cba2SAaron LI 	if (len > sizeof(buf)) {
2607827cba2SAaron LI 		errno = ENOBUFS;
2617827cba2SAaron LI 		return -1;
2627827cba2SAaron LI 	}
2637827cba2SAaron LI 
2647827cba2SAaron LI 	for (;; (*dad_counter)++) {
2657827cba2SAaron LI 		/* Combine all parameters into one buffer */
2667827cba2SAaron LI 		p = buf;
2677827cba2SAaron LI 		memcpy(p, prefix, l);
2687827cba2SAaron LI 		p += l;
2697827cba2SAaron LI 		memcpy(p, netiface, netiface_len);
2707827cba2SAaron LI 		p += netiface_len;
2717827cba2SAaron LI 		memcpy(p, netid, netid_len);
2727827cba2SAaron LI 		p += netid_len;
2737827cba2SAaron LI 		/* Don't use a vlanid if not set.
2747827cba2SAaron LI 		 * This ensures prior versions have the same unique address. */
2757827cba2SAaron LI 		if (vlanid != 0) {
2767827cba2SAaron LI 			memcpy(p, &vlanid, sizeof(vlanid));
2777827cba2SAaron LI 			p += sizeof(vlanid);
2787827cba2SAaron LI 		}
2797827cba2SAaron LI 		memcpy(p, dad_counter, sizeof(*dad_counter));
2807827cba2SAaron LI 		p += sizeof(*dad_counter);
281280986e4SRoy Marples 		memcpy(p, ctx->secret, ctx->secret_len);
2827827cba2SAaron LI 
2837827cba2SAaron LI 		/* Make an address using the digest of the above.
2847827cba2SAaron LI 		 * RFC7217 Section 5.1 states that we shouldn't use MD5.
2857827cba2SAaron LI 		 * Pity as we use that for HMAC-MD5 which is still deemed OK.
2867827cba2SAaron LI 		 * SHA-256 is recommended */
287280986e4SRoy Marples 		SHA256_Init(&sha_ctx);
288280986e4SRoy Marples 		SHA256_Update(&sha_ctx, buf, len);
289280986e4SRoy Marples 		SHA256_Final(digest, &sha_ctx);
2907827cba2SAaron LI 
2917827cba2SAaron LI 		p = addr->s6_addr;
2927827cba2SAaron LI 		memcpy(p, prefix, l);
2937827cba2SAaron LI 		/* RFC7217 section 5.2 says we need to start taking the id from
2947827cba2SAaron LI 		 * the least significant bit */
2957827cba2SAaron LI 		len = sizeof(addr->s6_addr) - l;
2967827cba2SAaron LI 		memcpy(p + l, digest + (sizeof(digest) - len), len);
2977827cba2SAaron LI 
2987827cba2SAaron LI 		/* Ensure that the Interface ID does not match a reserved one,
2997827cba2SAaron LI 		 * if it does then treat it as a DAD failure.
3007827cba2SAaron LI 		 * RFC7217 section 5.2 */
3017827cba2SAaron LI 		if (prefix_len != 64)
3027827cba2SAaron LI 			break;
3037827cba2SAaron LI 		if (!ipv6_reserved(addr))
3047827cba2SAaron LI 			break;
3057827cba2SAaron LI 	}
3067827cba2SAaron LI 
3077827cba2SAaron LI 	return 0;
3087827cba2SAaron LI }
3097827cba2SAaron LI 
3107827cba2SAaron LI int
3117827cba2SAaron LI ipv6_makestableprivate(struct in6_addr *addr,
3127827cba2SAaron LI     const struct in6_addr *prefix, int prefix_len,
3137827cba2SAaron LI     const struct interface *ifp,
3147827cba2SAaron LI     int *dad_counter)
3157827cba2SAaron LI {
3167827cba2SAaron LI 	uint32_t dad;
3177827cba2SAaron LI 	int r;
3187827cba2SAaron LI 
3197827cba2SAaron LI 	dad = (uint32_t)*dad_counter;
3207827cba2SAaron LI 
3217827cba2SAaron LI 	/* For our implementation, we shall set the hardware address
3227827cba2SAaron LI 	 * as the interface identifier */
323280986e4SRoy Marples 	r = ipv6_makestableprivate1(ifp->ctx, addr, prefix, prefix_len,
3247827cba2SAaron LI 	    ifp->hwaddr, ifp->hwlen,
3257827cba2SAaron LI 	    ifp->ssid, ifp->ssid_len,
326280986e4SRoy Marples 	    ifp->vlanid, &dad);
3277827cba2SAaron LI 
3287827cba2SAaron LI 	if (r == 0)
3297827cba2SAaron LI 		*dad_counter = (int)dad;
3307827cba2SAaron LI 	return r;
3317827cba2SAaron LI }
3327827cba2SAaron LI 
333280986e4SRoy Marples #ifdef IPV6_AF_TEMPORARY
334280986e4SRoy Marples static int
335280986e4SRoy Marples ipv6_maketemporaryaddress(struct in6_addr *addr,
336280986e4SRoy Marples     const struct in6_addr *prefix, int prefix_len,
337280986e4SRoy Marples     const struct interface *ifp)
338280986e4SRoy Marples {
339280986e4SRoy Marples 	struct in6_addr mask;
340280986e4SRoy Marples 	struct interface *ifpn;
341280986e4SRoy Marples 
342280986e4SRoy Marples 	if (ipv6_mask(&mask, prefix_len) == -1)
343280986e4SRoy Marples 		return -1;
344280986e4SRoy Marples 	*addr = *prefix;
345280986e4SRoy Marples 
346280986e4SRoy Marples again:
347280986e4SRoy Marples 	addr->s6_addr32[2] |= (arc4random() & ~mask.s6_addr32[2]);
348280986e4SRoy Marples 	addr->s6_addr32[3] |= (arc4random() & ~mask.s6_addr32[3]);
349280986e4SRoy Marples 
350280986e4SRoy Marples 	TAILQ_FOREACH(ifpn, ifp->ctx->ifaces, next) {
351280986e4SRoy Marples 		if (ipv6_iffindaddr(ifpn, addr, 0) != NULL)
352280986e4SRoy Marples 			break;
353280986e4SRoy Marples 	}
354280986e4SRoy Marples 	if (ifpn != NULL)
355280986e4SRoy Marples 		goto again;
356280986e4SRoy Marples 	if (ipv6_reserved(addr))
357280986e4SRoy Marples 		goto again;
358280986e4SRoy Marples 	return 0;
359280986e4SRoy Marples }
360280986e4SRoy Marples #endif
361280986e4SRoy Marples 
3627827cba2SAaron LI int
3637827cba2SAaron LI ipv6_makeaddr(struct in6_addr *addr, struct interface *ifp,
364280986e4SRoy Marples     const struct in6_addr *prefix, int prefix_len, unsigned int flags)
3657827cba2SAaron LI {
3667827cba2SAaron LI 	const struct ipv6_addr *ap;
3677827cba2SAaron LI 	int dad;
3687827cba2SAaron LI 
3697827cba2SAaron LI 	if (prefix_len < 0 || prefix_len > 120) {
3707827cba2SAaron LI 		errno = EINVAL;
3717827cba2SAaron LI 		return -1;
3727827cba2SAaron LI 	}
3737827cba2SAaron LI 
374280986e4SRoy Marples #ifdef IPV6_AF_TEMPORARY
375280986e4SRoy Marples 	if (flags & IPV6_AF_TEMPORARY)
376280986e4SRoy Marples 		return ipv6_maketemporaryaddress(addr, prefix, prefix_len, ifp);
377280986e4SRoy Marples #else
378280986e4SRoy Marples 	UNUSED(flags);
379280986e4SRoy Marples #endif
380280986e4SRoy Marples 
3817827cba2SAaron LI 	if (ifp->options->options & DHCPCD_SLAACPRIVATE) {
3827827cba2SAaron LI 		dad = 0;
3837827cba2SAaron LI 		if (ipv6_makestableprivate(addr,
3847827cba2SAaron LI 		    prefix, prefix_len, ifp, &dad) == -1)
3857827cba2SAaron LI 			return -1;
3867827cba2SAaron LI 		return dad;
3877827cba2SAaron LI 	}
3887827cba2SAaron LI 
3897827cba2SAaron LI 	if (prefix_len > 64) {
3907827cba2SAaron LI 		errno = EINVAL;
3917827cba2SAaron LI 		return -1;
3927827cba2SAaron LI 	}
3937827cba2SAaron LI 	if ((ap = ipv6_linklocal(ifp)) == NULL) {
3947827cba2SAaron LI 		/* We delay a few functions until we get a local-link address
3957827cba2SAaron LI 		 * so this should never be hit. */
3967827cba2SAaron LI 		errno = ENOENT;
3977827cba2SAaron LI 		return -1;
3987827cba2SAaron LI 	}
3997827cba2SAaron LI 
4007827cba2SAaron LI 	/* Make the address from the first local-link address */
4017827cba2SAaron LI 	memcpy(addr, prefix, sizeof(*prefix));
4027827cba2SAaron LI 	addr->s6_addr32[2] = ap->addr.s6_addr32[2];
4037827cba2SAaron LI 	addr->s6_addr32[3] = ap->addr.s6_addr32[3];
4047827cba2SAaron LI 	return 0;
4057827cba2SAaron LI }
4067827cba2SAaron LI 
4077827cba2SAaron LI static int
4087827cba2SAaron LI ipv6_makeprefix(struct in6_addr *prefix, const struct in6_addr *addr, int len)
4097827cba2SAaron LI {
4106e63cc1fSRoy Marples 	struct in6_addr mask;
4116e63cc1fSRoy Marples 	size_t i;
4127827cba2SAaron LI 
4136e63cc1fSRoy Marples 	if (ipv6_mask(&mask, len) == -1)
4147827cba2SAaron LI 		return -1;
4156e63cc1fSRoy Marples 	*prefix = *addr;
4166e63cc1fSRoy Marples 	for (i = 0; i < sizeof(prefix->s6_addr); i++)
4176e63cc1fSRoy Marples 		prefix->s6_addr[i] &= mask.s6_addr[i];
4187827cba2SAaron LI 	return 0;
4197827cba2SAaron LI }
4207827cba2SAaron LI 
4217827cba2SAaron LI int
4227827cba2SAaron LI ipv6_mask(struct in6_addr *mask, int len)
4237827cba2SAaron LI {
4247827cba2SAaron LI 	static const unsigned char masks[NBBY] =
4257827cba2SAaron LI 	    { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
4267827cba2SAaron LI 	int bytes, bits, i;
4277827cba2SAaron LI 
4287827cba2SAaron LI 	if (len < 0 || len > 128) {
4297827cba2SAaron LI 		errno = EINVAL;
4307827cba2SAaron LI 		return -1;
4317827cba2SAaron LI 	}
4327827cba2SAaron LI 
4337827cba2SAaron LI 	memset(mask, 0, sizeof(*mask));
4347827cba2SAaron LI 	bytes = len / NBBY;
4357827cba2SAaron LI 	bits = len % NBBY;
4367827cba2SAaron LI 	for (i = 0; i < bytes; i++)
4377827cba2SAaron LI 		mask->s6_addr[i] = 0xff;
4387827cba2SAaron LI 	if (bits != 0) {
4397827cba2SAaron LI 		/* Coverify false positive.
4407827cba2SAaron LI 		 * bytelen cannot be 16 if bitlen is non zero */
4417827cba2SAaron LI 		/* coverity[overrun-local] */
4427827cba2SAaron LI 		mask->s6_addr[bytes] = masks[bits - 1];
4437827cba2SAaron LI 	}
4447827cba2SAaron LI 	return 0;
4457827cba2SAaron LI }
4467827cba2SAaron LI 
4477827cba2SAaron LI uint8_t
4487827cba2SAaron LI ipv6_prefixlen(const struct in6_addr *mask)
4497827cba2SAaron LI {
4507827cba2SAaron LI 	int x = 0, y;
4517827cba2SAaron LI 	const unsigned char *lim, *p;
4527827cba2SAaron LI 
4537827cba2SAaron LI 	lim = (const unsigned char *)mask + sizeof(*mask);
4547827cba2SAaron LI 	for (p = (const unsigned char *)mask; p < lim; x++, p++) {
4557827cba2SAaron LI 		if (*p != 0xff)
4567827cba2SAaron LI 			break;
4577827cba2SAaron LI 	}
4587827cba2SAaron LI 	y = 0;
4597827cba2SAaron LI 	if (p < lim) {
4607827cba2SAaron LI 		for (y = 0; y < NBBY; y++) {
4617827cba2SAaron LI 			if ((*p & (0x80 >> y)) == 0)
4627827cba2SAaron LI 				break;
4637827cba2SAaron LI 		}
4647827cba2SAaron LI 	}
4657827cba2SAaron LI 
4667827cba2SAaron LI 	/*
4677827cba2SAaron LI 	 * when the limit pointer is given, do a stricter check on the
4687827cba2SAaron LI 	 * remaining bits.
4697827cba2SAaron LI 	 */
4707827cba2SAaron LI 	if (p < lim) {
4717827cba2SAaron LI 		if (y != 0 && (*p & (0x00ff >> y)) != 0)
4727827cba2SAaron LI 			return 0;
4737827cba2SAaron LI 		for (p = p + 1; p < lim; p++)
4747827cba2SAaron LI 			if (*p != 0)
4757827cba2SAaron LI 				return 0;
4767827cba2SAaron LI 	}
4777827cba2SAaron LI 
4787827cba2SAaron LI 	return (uint8_t)(x * NBBY + y);
4797827cba2SAaron LI }
4807827cba2SAaron LI 
4817827cba2SAaron LI static void
4827827cba2SAaron LI in6_to_h64(uint64_t *vhigh, uint64_t *vlow, const struct in6_addr *addr)
4837827cba2SAaron LI {
4847827cba2SAaron LI 
4857827cba2SAaron LI 	*vhigh = be64dec(addr->s6_addr);
4867827cba2SAaron LI 	*vlow = be64dec(addr->s6_addr + 8);
4877827cba2SAaron LI }
4887827cba2SAaron LI 
4897827cba2SAaron LI static void
4907827cba2SAaron LI h64_to_in6(struct in6_addr *addr, uint64_t vhigh, uint64_t vlow)
4917827cba2SAaron LI {
4927827cba2SAaron LI 
4937827cba2SAaron LI 	be64enc(addr->s6_addr, vhigh);
4947827cba2SAaron LI 	be64enc(addr->s6_addr + 8, vlow);
4957827cba2SAaron LI }
4967827cba2SAaron LI 
4977827cba2SAaron LI int
4987827cba2SAaron LI ipv6_userprefix(
4997827cba2SAaron LI 	const struct in6_addr *prefix,	// prefix from router
5007827cba2SAaron LI 	short prefix_len,		// length of prefix received
5017827cba2SAaron LI 	uint64_t user_number,		// "random" number from user
5027827cba2SAaron LI 	struct in6_addr *result,	// resultant prefix
5037827cba2SAaron LI 	short result_len)		// desired prefix length
5047827cba2SAaron LI {
5057827cba2SAaron LI 	uint64_t vh, vl, user_low, user_high;
5067827cba2SAaron LI 
5077827cba2SAaron LI 	if (prefix_len < 1 || prefix_len > 128 ||
5087827cba2SAaron LI 	    result_len < 1 || result_len > 128)
5097827cba2SAaron LI 	{
5107827cba2SAaron LI 		errno = EINVAL;
5117827cba2SAaron LI 		return -1;
5127827cba2SAaron LI 	}
5137827cba2SAaron LI 
5147827cba2SAaron LI 	/* Check that the user_number fits inside result_len less prefix_len */
5157827cba2SAaron LI 	if (result_len < prefix_len ||
5167827cba2SAaron LI 	    fls64(user_number) > result_len - prefix_len)
5177827cba2SAaron LI 	{
5187827cba2SAaron LI 	       errno = ERANGE;
5197827cba2SAaron LI 	       return -1;
5207827cba2SAaron LI 	}
5217827cba2SAaron LI 
5227827cba2SAaron LI 	/* If user_number is zero, just copy the prefix into the result. */
5237827cba2SAaron LI 	if (user_number == 0) {
5247827cba2SAaron LI 		*result = *prefix;
5257827cba2SAaron LI 		return 0;
5267827cba2SAaron LI 	}
5277827cba2SAaron LI 
5287827cba2SAaron LI 	/* Shift user_number so it fit's just inside result_len.
5297827cba2SAaron LI 	 * Shifting by 0 or sizeof(user_number) is undefined,
5307827cba2SAaron LI 	 * so we cater for that. */
5317827cba2SAaron LI 	if (result_len == 128) {
5327827cba2SAaron LI 		user_high = 0;
5337827cba2SAaron LI 		user_low = user_number;
5347827cba2SAaron LI 	} else if (result_len > 64) {
5357827cba2SAaron LI 		if (prefix_len >= 64)
5367827cba2SAaron LI 			user_high = 0;
5377827cba2SAaron LI 		else
5387827cba2SAaron LI 			user_high = user_number >> (result_len - prefix_len);
5397827cba2SAaron LI 		user_low = user_number << (128 - result_len);
5407827cba2SAaron LI 	} else if (result_len == 64) {
5417827cba2SAaron LI 		user_high = user_number;
5427827cba2SAaron LI 		user_low = 0;
5437827cba2SAaron LI 	} else {
5447827cba2SAaron LI 		user_high = user_number << (64 - result_len);
5457827cba2SAaron LI 		user_low = 0;
5467827cba2SAaron LI 	}
5477827cba2SAaron LI 
5487827cba2SAaron LI 	/* convert to two 64bit host order values */
5497827cba2SAaron LI 	in6_to_h64(&vh, &vl, prefix);
5507827cba2SAaron LI 
5517827cba2SAaron LI 	vh |= user_high;
5527827cba2SAaron LI 	vl |= user_low;
5537827cba2SAaron LI 
5547827cba2SAaron LI 	/* copy back result */
5557827cba2SAaron LI 	h64_to_in6(result, vh, vl);
5567827cba2SAaron LI 
5577827cba2SAaron LI 	return 0;
5587827cba2SAaron LI }
5597827cba2SAaron LI 
5607827cba2SAaron LI #ifdef IPV6_POLLADDRFLAG
5617827cba2SAaron LI void
5627827cba2SAaron LI ipv6_checkaddrflags(void *arg)
5637827cba2SAaron LI {
5647827cba2SAaron LI 	struct ipv6_addr *ia;
5657827cba2SAaron LI 	int flags;
5667827cba2SAaron LI 	const char *alias;
5677827cba2SAaron LI 
5687827cba2SAaron LI 	ia = arg;
5697827cba2SAaron LI #ifdef ALIAS_ADDR
5707827cba2SAaron LI 	alias = ia->alias;
5717827cba2SAaron LI #else
5727827cba2SAaron LI 	alias = NULL;
5737827cba2SAaron LI #endif
5747827cba2SAaron LI 	if ((flags = if_addrflags6(ia->iface, &ia->addr, alias)) == -1) {
5757827cba2SAaron LI 		if (errno != EEXIST && errno != EADDRNOTAVAIL)
5767827cba2SAaron LI 			logerr("%s: if_addrflags6", __func__);
5777827cba2SAaron LI 		return;
5787827cba2SAaron LI 	}
5797827cba2SAaron LI 
5807827cba2SAaron LI 	if (!(flags & IN6_IFF_TENTATIVE)) {
5817827cba2SAaron LI 		/* Simulate the kernel announcing the new address. */
5827827cba2SAaron LI 		ipv6_handleifa(ia->iface->ctx, RTM_NEWADDR,
5837827cba2SAaron LI 		    ia->iface->ctx->ifaces, ia->iface->name,
5847827cba2SAaron LI 		    &ia->addr, ia->prefix_len, flags, 0);
5857827cba2SAaron LI 	} else {
5867827cba2SAaron LI 		/* Still tentative? Check again in a bit. */
5876e63cc1fSRoy Marples 		eloop_timeout_add_msec(ia->iface->ctx->eloop,
5886e63cc1fSRoy Marples 		    RETRANS_TIMER / 2, ipv6_checkaddrflags, ia);
5897827cba2SAaron LI 	}
5907827cba2SAaron LI }
5917827cba2SAaron LI #endif
5927827cba2SAaron LI 
5937827cba2SAaron LI static void
5947827cba2SAaron LI ipv6_deletedaddr(struct ipv6_addr *ia)
5957827cba2SAaron LI {
5967827cba2SAaron LI 
597d4fb1e02SRoy Marples #ifdef DHCP6
5986e63cc1fSRoy Marples #ifdef PRIVSEP
5996e63cc1fSRoy Marples 	if (!(ia->iface->ctx->options & DHCPCD_MASTER))
6006e63cc1fSRoy Marples 		ps_inet_closedhcp6(ia);
6016e63cc1fSRoy Marples #elif defined(SMALL)
6027827cba2SAaron LI 	UNUSED(ia);
6037827cba2SAaron LI #else
6047827cba2SAaron LI 	/* NOREJECT is set if we delegated exactly the prefix to another
6057827cba2SAaron LI 	 * address.
6067827cba2SAaron LI 	 * This can only be one address, so just clear the flag.
6077827cba2SAaron LI 	 * This should ensure the reject route will be restored. */
6087827cba2SAaron LI 	if (ia->delegating_prefix != NULL)
6097827cba2SAaron LI 		ia->delegating_prefix->flags &= ~IPV6_AF_NOREJECT;
6107827cba2SAaron LI #endif
611d4fb1e02SRoy Marples #else
612d4fb1e02SRoy Marples 	UNUSED(ia);
613d4fb1e02SRoy Marples #endif
6147827cba2SAaron LI }
6157827cba2SAaron LI 
6167827cba2SAaron LI void
6177827cba2SAaron LI ipv6_deleteaddr(struct ipv6_addr *ia)
6187827cba2SAaron LI {
6197827cba2SAaron LI 	struct ipv6_state *state;
6207827cba2SAaron LI 	struct ipv6_addr *ap;
6217827cba2SAaron LI 
6227827cba2SAaron LI 	loginfox("%s: deleting address %s", ia->iface->name, ia->saddr);
6237827cba2SAaron LI 	if (if_address6(RTM_DELADDR, ia) == -1 &&
6247827cba2SAaron LI 	    errno != EADDRNOTAVAIL && errno != ESRCH &&
6257827cba2SAaron LI 	    errno != ENXIO && errno != ENODEV)
6267827cba2SAaron LI 		logerr(__func__);
6277827cba2SAaron LI 
6287827cba2SAaron LI 	ipv6_deletedaddr(ia);
6297827cba2SAaron LI 
6307827cba2SAaron LI 	state = IPV6_STATE(ia->iface);
6317827cba2SAaron LI 	TAILQ_FOREACH(ap, &state->addrs, next) {
6327827cba2SAaron LI 		if (IN6_ARE_ADDR_EQUAL(&ap->addr, &ia->addr)) {
6337827cba2SAaron LI 			TAILQ_REMOVE(&state->addrs, ap, next);
6347827cba2SAaron LI 			ipv6_freeaddr(ap);
6357827cba2SAaron LI 			break;
6367827cba2SAaron LI 		}
6377827cba2SAaron LI 	}
6388d36e1dfSRoy Marples 
6398d36e1dfSRoy Marples #ifdef ND6_ADVERTISE
6408d36e1dfSRoy Marples 	/* Advertise the address if it exists on another interface. */
6418d36e1dfSRoy Marples 	ipv6nd_advertise(ia);
6428d36e1dfSRoy Marples #endif
6437827cba2SAaron LI }
6447827cba2SAaron LI 
6457827cba2SAaron LI static int
6467827cba2SAaron LI ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
6477827cba2SAaron LI {
6487827cba2SAaron LI 	struct interface *ifp;
6497827cba2SAaron LI 	uint32_t pltime, vltime;
6506e63cc1fSRoy Marples 	int loglevel;
6518d36e1dfSRoy Marples #ifdef ND6_ADVERTISE
6528d36e1dfSRoy Marples 	bool vltime_was_zero = ia->prefix_vltime == 0;
6538d36e1dfSRoy Marples #endif
6548d36e1dfSRoy Marples #ifdef __sun
6558d36e1dfSRoy Marples 	struct ipv6_state *state;
6568d36e1dfSRoy Marples 	struct ipv6_addr *ia2;
6577827cba2SAaron LI 
6588d36e1dfSRoy Marples 	/* If we re-add then address on Solaris then the prefix
6598d36e1dfSRoy Marples 	 * route will be scrubbed and re-added. Something might
6608d36e1dfSRoy Marples 	 * be using it, so let's avoid it. */
6618d36e1dfSRoy Marples 	if (ia->flags & IPV6_AF_DADCOMPLETED) {
6628d36e1dfSRoy Marples 		logdebugx("%s: IP address %s already exists",
6638d36e1dfSRoy Marples 		    ia->iface->name, ia->saddr);
6648d36e1dfSRoy Marples #ifdef ND6_ADVERTISE
6658d36e1dfSRoy Marples 		goto advertise;
6668d36e1dfSRoy Marples #else
6678d36e1dfSRoy Marples 		return 0;
6688d36e1dfSRoy Marples #endif
6697827cba2SAaron LI 	}
6708d36e1dfSRoy Marples #endif
6717827cba2SAaron LI 
6727827cba2SAaron LI 	/* Remember the interface of the address. */
6737827cba2SAaron LI 	ifp = ia->iface;
6747827cba2SAaron LI 
6757827cba2SAaron LI 	if (!(ia->flags & IPV6_AF_DADCOMPLETED) &&
6767827cba2SAaron LI 	    ipv6_iffindaddr(ifp, &ia->addr, IN6_IFF_NOTUSEABLE))
6777827cba2SAaron LI 		ia->flags |= IPV6_AF_DADCOMPLETED;
6787827cba2SAaron LI 
6797827cba2SAaron LI 	/* Adjust plftime and vltime based on acquired time */
6807827cba2SAaron LI 	pltime = ia->prefix_pltime;
6817827cba2SAaron LI 	vltime = ia->prefix_vltime;
682b8b69544SRoy Marples 
683b8b69544SRoy Marples 	if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {
684b8b69544SRoy Marples 		/* We don't want the kernel to expire the address.
685b8b69544SRoy Marples 		 * The saved times will be re-applied to the ia
686b8b69544SRoy Marples 		 * before exiting this function. */
687b8b69544SRoy Marples 		ia->prefix_vltime = ia->prefix_pltime = ND6_INFINITE_LIFETIME;
688b8b69544SRoy Marples 	}
689b8b69544SRoy Marples 
6907827cba2SAaron LI 	if (timespecisset(&ia->acquired) &&
6917827cba2SAaron LI 	    (ia->prefix_pltime != ND6_INFINITE_LIFETIME ||
6927827cba2SAaron LI 	    ia->prefix_vltime != ND6_INFINITE_LIFETIME))
6937827cba2SAaron LI 	{
6946e63cc1fSRoy Marples 		uint32_t elapsed;
6957827cba2SAaron LI 		struct timespec n;
6967827cba2SAaron LI 
6977827cba2SAaron LI 		if (now == NULL) {
6987827cba2SAaron LI 			clock_gettime(CLOCK_MONOTONIC, &n);
6997827cba2SAaron LI 			now = &n;
7007827cba2SAaron LI 		}
7016e63cc1fSRoy Marples 		elapsed = (uint32_t)eloop_timespec_diff(now, &ia->acquired,
7026e63cc1fSRoy Marples 		    NULL);
7037827cba2SAaron LI 		if (ia->prefix_pltime != ND6_INFINITE_LIFETIME) {
7046e63cc1fSRoy Marples 			if (elapsed > ia->prefix_pltime)
7057827cba2SAaron LI 				ia->prefix_pltime = 0;
7066e63cc1fSRoy Marples 			else
7076e63cc1fSRoy Marples 				ia->prefix_pltime -= elapsed;
7087827cba2SAaron LI 		}
7097827cba2SAaron LI 		if (ia->prefix_vltime != ND6_INFINITE_LIFETIME) {
7106e63cc1fSRoy Marples 			if (elapsed > ia->prefix_vltime)
7116e63cc1fSRoy Marples 				ia->prefix_vltime = 0;
7126e63cc1fSRoy Marples 			else
7136e63cc1fSRoy Marples 				ia->prefix_vltime -= elapsed;
7147827cba2SAaron LI 		}
7157827cba2SAaron LI 	}
7167827cba2SAaron LI 
7176e63cc1fSRoy Marples 	loglevel = ia->flags & IPV6_AF_NEW ? LOG_INFO : LOG_DEBUG;
7186e63cc1fSRoy Marples 	logmessage(loglevel, "%s: adding %saddress %s", ifp->name,
7197827cba2SAaron LI #ifdef IPV6_AF_TEMPORARY
7207827cba2SAaron LI 	    ia->flags & IPV6_AF_TEMPORARY ? "temporary " : "",
7217827cba2SAaron LI #else
7227827cba2SAaron LI 	    "",
7237827cba2SAaron LI #endif
7247827cba2SAaron LI 	    ia->saddr);
7257827cba2SAaron LI 	if (ia->prefix_pltime == ND6_INFINITE_LIFETIME &&
7267827cba2SAaron LI 	    ia->prefix_vltime == ND6_INFINITE_LIFETIME)
7277827cba2SAaron LI 		logdebugx("%s: pltime infinity, vltime infinity",
7287827cba2SAaron LI 		    ifp->name);
7297827cba2SAaron LI 	else if (ia->prefix_pltime == ND6_INFINITE_LIFETIME)
7307827cba2SAaron LI 		logdebugx("%s: pltime infinity, vltime %"PRIu32" seconds",
7317827cba2SAaron LI 		    ifp->name, ia->prefix_vltime);
7327827cba2SAaron LI 	else if (ia->prefix_vltime == ND6_INFINITE_LIFETIME)
7337827cba2SAaron LI 		logdebugx("%s: pltime %"PRIu32"seconds, vltime infinity",
7347827cba2SAaron LI 		    ifp->name, ia->prefix_pltime);
7357827cba2SAaron LI 	else
7367827cba2SAaron LI 		logdebugx("%s: pltime %"PRIu32" seconds, vltime %"PRIu32
7377827cba2SAaron LI 		    " seconds",
7387827cba2SAaron LI 		    ifp->name, ia->prefix_pltime, ia->prefix_vltime);
7397827cba2SAaron LI 
7407827cba2SAaron LI 	if (if_address6(RTM_NEWADDR, ia) == -1) {
7417827cba2SAaron LI 		logerr(__func__);
7427827cba2SAaron LI 		/* Restore real pltime and vltime */
7437827cba2SAaron LI 		ia->prefix_pltime = pltime;
7447827cba2SAaron LI 		ia->prefix_vltime = vltime;
7457827cba2SAaron LI 		return -1;
7467827cba2SAaron LI 	}
7477827cba2SAaron LI 
7487827cba2SAaron LI #ifdef IPV6_MANAGETEMPADDR
7497827cba2SAaron LI 	/* RFC4941 Section 3.4 */
7507827cba2SAaron LI 	if (ia->flags & IPV6_AF_TEMPORARY &&
7517827cba2SAaron LI 	    ia->prefix_pltime &&
7527827cba2SAaron LI 	    ia->prefix_vltime &&
7537a0236bfSRoy Marples 	    ifp->options->options & DHCPCD_SLAACTEMP)
7547827cba2SAaron LI 		eloop_timeout_add_sec(ifp->ctx->eloop,
7556e63cc1fSRoy Marples 		    ia->prefix_pltime - REGEN_ADVANCE,
7567827cba2SAaron LI 		    ipv6_regentempaddr, ia);
7577827cba2SAaron LI #endif
7587827cba2SAaron LI 
7597827cba2SAaron LI 	/* Restore real pltime and vltime */
7607827cba2SAaron LI 	ia->prefix_pltime = pltime;
7617827cba2SAaron LI 	ia->prefix_vltime = vltime;
7627827cba2SAaron LI 
7637827cba2SAaron LI 	ia->flags &= ~IPV6_AF_NEW;
7647827cba2SAaron LI 	ia->flags |= IPV6_AF_ADDED;
7657827cba2SAaron LI #ifndef SMALL
7667827cba2SAaron LI 	if (ia->delegating_prefix != NULL)
7677827cba2SAaron LI 		ia->flags |= IPV6_AF_DELEGATED;
7687827cba2SAaron LI #endif
7697827cba2SAaron LI 
7707827cba2SAaron LI #ifdef IPV6_POLLADDRFLAG
7717827cba2SAaron LI 	eloop_timeout_delete(ifp->ctx->eloop,
7727827cba2SAaron LI 		ipv6_checkaddrflags, ia);
7737827cba2SAaron LI 	if (!(ia->flags & IPV6_AF_DADCOMPLETED)) {
7746e63cc1fSRoy Marples 		eloop_timeout_add_msec(ifp->ctx->eloop,
7756e63cc1fSRoy Marples 		    RETRANS_TIMER / 2, ipv6_checkaddrflags, ia);
7767827cba2SAaron LI 	}
7777827cba2SAaron LI #endif
7787827cba2SAaron LI 
7797827cba2SAaron LI #ifdef __sun
7807827cba2SAaron LI 	/* Solaris does not announce new addresses which need DaD
7817827cba2SAaron LI 	 * so we need to take a copy and add it to our list.
7827827cba2SAaron LI 	 * Otherwise aliasing gets confused if we add another
7837827cba2SAaron LI 	 * address during DaD. */
7847827cba2SAaron LI 
7857827cba2SAaron LI 	state = IPV6_STATE(ifp);
7867827cba2SAaron LI 	TAILQ_FOREACH(ia2, &state->addrs, next) {
7877827cba2SAaron LI 		if (IN6_ARE_ADDR_EQUAL(&ia2->addr, &ia->addr))
7887827cba2SAaron LI 			break;
7897827cba2SAaron LI 	}
7907827cba2SAaron LI 	if (ia2 == NULL) {
7917827cba2SAaron LI 		if ((ia2 = malloc(sizeof(*ia2))) == NULL) {
7927827cba2SAaron LI 			logerr(__func__);
7937827cba2SAaron LI 			return 0; /* Well, we did add the address */
7947827cba2SAaron LI 		}
7957827cba2SAaron LI 		memcpy(ia2, ia, sizeof(*ia2));
7967827cba2SAaron LI 		TAILQ_INSERT_TAIL(&state->addrs, ia2, next);
7977827cba2SAaron LI 	}
7987827cba2SAaron LI #endif
7997827cba2SAaron LI 
8008d36e1dfSRoy Marples #ifdef ND6_ADVERTISE
8018d36e1dfSRoy Marples #ifdef __sun
8028d36e1dfSRoy Marples advertise:
8038d36e1dfSRoy Marples #endif
8048d36e1dfSRoy Marples 	/* Re-advertise the preferred address to be safe. */
8058d36e1dfSRoy Marples 	if (!vltime_was_zero)
8068d36e1dfSRoy Marples 		ipv6nd_advertise(ia);
8078d36e1dfSRoy Marples #endif
8088d36e1dfSRoy Marples 
8097827cba2SAaron LI 	return 0;
8107827cba2SAaron LI }
8117827cba2SAaron LI 
8127827cba2SAaron LI #ifdef ALIAS_ADDR
8138d36e1dfSRoy Marples /* Find the next logical alias address we can use. */
8147827cba2SAaron LI static int
8157827cba2SAaron LI ipv6_aliasaddr(struct ipv6_addr *ia, struct ipv6_addr **repl)
8167827cba2SAaron LI {
8177827cba2SAaron LI 	struct ipv6_state *state;
8187827cba2SAaron LI 	struct ipv6_addr *iap;
8198d36e1dfSRoy Marples 	unsigned int lun;
8207827cba2SAaron LI 	char alias[IF_NAMESIZE];
8217827cba2SAaron LI 
8227827cba2SAaron LI 	if (ia->alias[0] != '\0')
8237827cba2SAaron LI 		return 0;
8247827cba2SAaron LI 	state = IPV6_STATE(ia->iface);
8257827cba2SAaron LI 
8267827cba2SAaron LI 	/* First find an existng address.
8277827cba2SAaron LI 	 * This can happen when dhcpcd restarts as ND and DHCPv6
8287827cba2SAaron LI 	 * maintain their own lists of addresses. */
8297827cba2SAaron LI 	TAILQ_FOREACH(iap, &state->addrs, next) {
8307827cba2SAaron LI 		if (iap->alias[0] != '\0' &&
8317827cba2SAaron LI 		    IN6_ARE_ADDR_EQUAL(&iap->addr, &ia->addr))
8327827cba2SAaron LI 		{
8337827cba2SAaron LI 			strlcpy(ia->alias, iap->alias, sizeof(ia->alias));
8347827cba2SAaron LI 			return 0;
8357827cba2SAaron LI 		}
8367827cba2SAaron LI 	}
8377827cba2SAaron LI 
8388d36e1dfSRoy Marples 	lun = 0;
8397827cba2SAaron LI find_unit:
8408d36e1dfSRoy Marples 	if (if_makealias(alias, IF_NAMESIZE, ia->iface->name, lun) >=
8418d36e1dfSRoy Marples 	    IF_NAMESIZE)
8428d36e1dfSRoy Marples 	{
8438d36e1dfSRoy Marples 		errno = ENOMEM;
8448d36e1dfSRoy Marples 		return -1;
8458d36e1dfSRoy Marples 	}
8467827cba2SAaron LI 	TAILQ_FOREACH(iap, &state->addrs, next) {
8477827cba2SAaron LI 		if (iap->alias[0] == '\0')
8487827cba2SAaron LI 			continue;
8497827cba2SAaron LI 		if (IN6_IS_ADDR_UNSPECIFIED(&iap->addr)) {
8507827cba2SAaron LI 			/* No address assigned? Lets use it. */
8517827cba2SAaron LI 			strlcpy(ia->alias, iap->alias, sizeof(ia->alias));
8527827cba2SAaron LI 			if (repl)
8537827cba2SAaron LI 				*repl = iap;
8547827cba2SAaron LI 			return 1;
8557827cba2SAaron LI 		}
8567827cba2SAaron LI 		if (strcmp(iap->alias, alias) == 0)
8577827cba2SAaron LI 			break;
8587827cba2SAaron LI 	}
8597827cba2SAaron LI 
8607827cba2SAaron LI 	if (iap != NULL) {
8618d36e1dfSRoy Marples 		if (lun == UINT_MAX) {
8627827cba2SAaron LI 			errno = ERANGE;
8637827cba2SAaron LI 			return -1;
8647827cba2SAaron LI 		}
8658d36e1dfSRoy Marples 		lun++;
8667827cba2SAaron LI 		goto find_unit;
8677827cba2SAaron LI 	}
8687827cba2SAaron LI 
8697827cba2SAaron LI 	strlcpy(ia->alias, alias, sizeof(ia->alias));
8707827cba2SAaron LI 	return 0;
8717827cba2SAaron LI }
8727827cba2SAaron LI #endif
8737827cba2SAaron LI 
8747827cba2SAaron LI int
8757827cba2SAaron LI ipv6_addaddr(struct ipv6_addr *ia, const struct timespec *now)
8767827cba2SAaron LI {
8777827cba2SAaron LI 	int r;
8787827cba2SAaron LI #ifdef ALIAS_ADDR
8797827cba2SAaron LI 	int replaced, blank;
8807827cba2SAaron LI 	struct ipv6_addr *replaced_ia;
8817827cba2SAaron LI 
8827827cba2SAaron LI 	blank = (ia->alias[0] == '\0');
8837827cba2SAaron LI 	if ((replaced = ipv6_aliasaddr(ia, &replaced_ia)) == -1)
8847827cba2SAaron LI 		return -1;
8857827cba2SAaron LI 	if (blank)
8867827cba2SAaron LI 		logdebugx("%s: aliased %s", ia->alias, ia->saddr);
8877827cba2SAaron LI #endif
8887827cba2SAaron LI 
8897827cba2SAaron LI 	if ((r = ipv6_addaddr1(ia, now)) == 0) {
8907827cba2SAaron LI #ifdef ALIAS_ADDR
8917827cba2SAaron LI 		if (replaced) {
8927827cba2SAaron LI 			struct ipv6_state *state;
8937827cba2SAaron LI 
8947827cba2SAaron LI 			state = IPV6_STATE(ia->iface);
8957827cba2SAaron LI 			TAILQ_REMOVE(&state->addrs, replaced_ia, next);
8967827cba2SAaron LI 			ipv6_freeaddr(replaced_ia);
8977827cba2SAaron LI 		}
8987827cba2SAaron LI #endif
8997827cba2SAaron LI 	}
9007827cba2SAaron LI 	return r;
9017827cba2SAaron LI }
9027827cba2SAaron LI 
9037827cba2SAaron LI int
9047827cba2SAaron LI ipv6_findaddrmatch(const struct ipv6_addr *addr, const struct in6_addr *match,
9057827cba2SAaron LI     unsigned int flags)
9067827cba2SAaron LI {
9077827cba2SAaron LI 
9087827cba2SAaron LI 	if (match == NULL) {
9097827cba2SAaron LI 		if ((addr->flags &
9107827cba2SAaron LI 		    (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)) ==
9117827cba2SAaron LI 		    (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED))
9127827cba2SAaron LI 			return 1;
9137827cba2SAaron LI 	} else if (addr->prefix_vltime &&
9147827cba2SAaron LI 	    IN6_ARE_ADDR_EQUAL(&addr->addr, match) &&
9157827cba2SAaron LI 	    (!flags || addr->flags & flags))
9167827cba2SAaron LI 		return 1;
9177827cba2SAaron LI 
9187827cba2SAaron LI 	return 0;
9197827cba2SAaron LI }
9207827cba2SAaron LI 
9217827cba2SAaron LI struct ipv6_addr *
9227827cba2SAaron LI ipv6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr, unsigned int flags)
9237827cba2SAaron LI {
9248d36e1dfSRoy Marples 	struct ipv6_addr *nap;
9258d36e1dfSRoy Marples #ifdef DHCP6
9268d36e1dfSRoy Marples 	struct ipv6_addr *dap;
9278d36e1dfSRoy Marples #endif
9287827cba2SAaron LI 
9297827cba2SAaron LI 	nap = ipv6nd_findaddr(ctx, addr, flags);
9308d36e1dfSRoy Marples #ifdef DHCP6
9318d36e1dfSRoy Marples 	dap = dhcp6_findaddr(ctx, addr, flags);
9327827cba2SAaron LI 	if (!dap && !nap)
9337827cba2SAaron LI 		return NULL;
9347827cba2SAaron LI 	if (dap && !nap)
9357827cba2SAaron LI 		return dap;
9367827cba2SAaron LI 	if (nap && !dap)
9377827cba2SAaron LI 		return nap;
9387827cba2SAaron LI 	if (nap->iface->metric < dap->iface->metric)
9397827cba2SAaron LI 		return nap;
9407827cba2SAaron LI 	return dap;
9418d36e1dfSRoy Marples #else
9428d36e1dfSRoy Marples 	return nap;
9438d36e1dfSRoy Marples #endif
9447827cba2SAaron LI }
9457827cba2SAaron LI 
946ce6cc02eSRoy Marples int
947ce6cc02eSRoy Marples ipv6_doaddr(struct ipv6_addr *ia, struct timespec *now)
9487827cba2SAaron LI {
949ce6cc02eSRoy Marples 
950ce6cc02eSRoy Marples 	/* A delegated prefix is not an address. */
951ce6cc02eSRoy Marples 	if (ia->flags & IPV6_AF_DELEGATEDPFX)
952ce6cc02eSRoy Marples 		return 0;
953ce6cc02eSRoy Marples 
954ce6cc02eSRoy Marples 	if (ia->prefix_vltime == 0) {
955ce6cc02eSRoy Marples 		if (ia->flags & IPV6_AF_ADDED)
956ce6cc02eSRoy Marples 			ipv6_deleteaddr(ia);
957ce6cc02eSRoy Marples 		eloop_q_timeout_delete(ia->iface->ctx->eloop,
9586e63cc1fSRoy Marples 		    ELOOP_QUEUE_ALL, NULL, ia);
959ce6cc02eSRoy Marples 		if (ia->flags & IPV6_AF_REQUEST) {
960ce6cc02eSRoy Marples 			ia->flags &= ~IPV6_AF_ADDED;
961ce6cc02eSRoy Marples 			return 0;
962ce6cc02eSRoy Marples 		}
963ce6cc02eSRoy Marples 		return -1;
964ce6cc02eSRoy Marples 	}
965ce6cc02eSRoy Marples 
966ce6cc02eSRoy Marples 	if (ia->flags & IPV6_AF_STALE ||
967ce6cc02eSRoy Marples 	    IN6_IS_ADDR_UNSPECIFIED(&ia->addr))
968ce6cc02eSRoy Marples 		return 0;
969ce6cc02eSRoy Marples 
970ce6cc02eSRoy Marples 	if (!timespecisset(now))
971ce6cc02eSRoy Marples 		clock_gettime(CLOCK_MONOTONIC, now);
972ce6cc02eSRoy Marples 	ipv6_addaddr(ia, now);
973ce6cc02eSRoy Marples 	return ia->flags & IPV6_AF_NEW ? 1 : 0;
974ce6cc02eSRoy Marples }
975ce6cc02eSRoy Marples 
976ce6cc02eSRoy Marples ssize_t
977ce6cc02eSRoy Marples ipv6_addaddrs(struct ipv6_addrhead *iaddrs)
978ce6cc02eSRoy Marples {
9797827cba2SAaron LI 	struct timespec now;
980ce6cc02eSRoy Marples 	struct ipv6_addr *ia, *ian;
981ce6cc02eSRoy Marples 	ssize_t i, r;
9827827cba2SAaron LI 
9837827cba2SAaron LI 	i = 0;
9847827cba2SAaron LI 	timespecclear(&now);
985ce6cc02eSRoy Marples 	TAILQ_FOREACH_SAFE(ia, iaddrs, next, ian) {
986ce6cc02eSRoy Marples 		r = ipv6_doaddr(ia, &now);
987ce6cc02eSRoy Marples 		if (r != 0)
9887827cba2SAaron LI 			i++;
989ce6cc02eSRoy Marples 		if (r == -1) {
990ce6cc02eSRoy Marples 			TAILQ_REMOVE(iaddrs, ia, next);
991ce6cc02eSRoy Marples 			ipv6_freeaddr(ia);
9927827cba2SAaron LI 		}
9937827cba2SAaron LI 	}
9947827cba2SAaron LI 	return i;
9957827cba2SAaron LI }
9967827cba2SAaron LI 
9977827cba2SAaron LI void
9987827cba2SAaron LI ipv6_freeaddr(struct ipv6_addr *ia)
9997827cba2SAaron LI {
10006e63cc1fSRoy Marples 	struct eloop *eloop = ia->iface->ctx->eloop;
10017827cba2SAaron LI #ifndef SMALL
10027827cba2SAaron LI 	struct ipv6_addr *iad;
10037827cba2SAaron LI 
10047827cba2SAaron LI 	/* Forget the reference */
10057827cba2SAaron LI 	if (ia->flags & IPV6_AF_DELEGATEDPFX) {
10067827cba2SAaron LI 		TAILQ_FOREACH(iad, &ia->pd_pfxs, pd_next) {
10077827cba2SAaron LI 			iad->delegating_prefix = NULL;
10087827cba2SAaron LI 		}
10097827cba2SAaron LI 	} else if (ia->delegating_prefix != NULL) {
10107827cba2SAaron LI 		TAILQ_REMOVE(&ia->delegating_prefix->pd_pfxs, ia, pd_next);
10117827cba2SAaron LI 	}
10127827cba2SAaron LI #endif
10137827cba2SAaron LI 
10147827cba2SAaron LI 	if (ia->dhcp6_fd != -1) {
10157827cba2SAaron LI 		close(ia->dhcp6_fd);
10166e63cc1fSRoy Marples 		eloop_event_delete(eloop, ia->dhcp6_fd);
10177827cba2SAaron LI 	}
10187827cba2SAaron LI 
10196e63cc1fSRoy Marples 	eloop_q_timeout_delete(eloop, ELOOP_QUEUE_ALL, NULL, ia);
10208d36e1dfSRoy Marples 	free(ia->na);
10217827cba2SAaron LI 	free(ia);
10227827cba2SAaron LI }
10237827cba2SAaron LI 
10247827cba2SAaron LI void
10257827cba2SAaron LI ipv6_freedrop_addrs(struct ipv6_addrhead *addrs, int drop,
10267827cba2SAaron LI     const struct interface *ifd)
10277827cba2SAaron LI {
10287827cba2SAaron LI 	struct ipv6_addr *ap, *apn, *apf;
10297827cba2SAaron LI 	struct timespec now;
10307827cba2SAaron LI 
10317827cba2SAaron LI #ifdef SMALL
10327827cba2SAaron LI 	UNUSED(ifd);
10337827cba2SAaron LI #endif
10347827cba2SAaron LI 	timespecclear(&now);
10357827cba2SAaron LI 	TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
10367827cba2SAaron LI #ifndef SMALL
10377827cba2SAaron LI 		if (ifd != NULL &&
10387827cba2SAaron LI 		    (ap->delegating_prefix == NULL ||
10397827cba2SAaron LI 		    ap->delegating_prefix->iface != ifd))
10407827cba2SAaron LI 			continue;
10417827cba2SAaron LI #endif
10427827cba2SAaron LI 		if (drop != 2)
10437827cba2SAaron LI 			TAILQ_REMOVE(addrs, ap, next);
10447827cba2SAaron LI 		if (drop && ap->flags & IPV6_AF_ADDED &&
10457827cba2SAaron LI 		    (ap->iface->options->options &
10467827cba2SAaron LI 		    (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
10477827cba2SAaron LI 		    (DHCPCD_EXITING | DHCPCD_PERSISTENT))
10487827cba2SAaron LI 		{
10497827cba2SAaron LI 			/* Don't drop link-local addresses. */
10507827cba2SAaron LI 			if (!IN6_IS_ADDR_LINKLOCAL(&ap->addr) ||
10517827cba2SAaron LI 			    CAN_DROP_LLADDR(ap->iface))
10527827cba2SAaron LI 			{
10537827cba2SAaron LI 				if (drop == 2)
10547827cba2SAaron LI 					TAILQ_REMOVE(addrs, ap, next);
10557827cba2SAaron LI 				/* Find the same address somewhere else */
10567827cba2SAaron LI 				apf = ipv6_findaddr(ap->iface->ctx, &ap->addr,
10577827cba2SAaron LI 				    0);
10587827cba2SAaron LI 				if ((apf == NULL ||
10597827cba2SAaron LI 				    (apf->iface != ap->iface)))
10607827cba2SAaron LI 					ipv6_deleteaddr(ap);
10617827cba2SAaron LI 				if (!(ap->iface->options->options &
10627827cba2SAaron LI 				    DHCPCD_EXITING) && apf)
10637827cba2SAaron LI 				{
10647827cba2SAaron LI 					if (!timespecisset(&now))
10657827cba2SAaron LI 						clock_gettime(CLOCK_MONOTONIC,
10667827cba2SAaron LI 						    &now);
10677827cba2SAaron LI 					ipv6_addaddr(apf, &now);
10687827cba2SAaron LI 				}
10697827cba2SAaron LI 				if (drop == 2)
10707827cba2SAaron LI 					ipv6_freeaddr(ap);
10717827cba2SAaron LI 			}
10727827cba2SAaron LI 		}
10737827cba2SAaron LI 		if (drop != 2)
10747827cba2SAaron LI 			ipv6_freeaddr(ap);
10757827cba2SAaron LI 	}
10767827cba2SAaron LI }
10777827cba2SAaron LI 
10787827cba2SAaron LI static struct ipv6_state *
10797827cba2SAaron LI ipv6_getstate(struct interface *ifp)
10807827cba2SAaron LI {
10817827cba2SAaron LI 	struct ipv6_state *state;
10827827cba2SAaron LI 
10837827cba2SAaron LI 	state = IPV6_STATE(ifp);
10847827cba2SAaron LI 	if (state == NULL) {
10857827cba2SAaron LI 	        ifp->if_data[IF_DATA_IPV6] = calloc(1, sizeof(*state));
10867827cba2SAaron LI 		state = IPV6_STATE(ifp);
10877827cba2SAaron LI 		if (state == NULL) {
10887827cba2SAaron LI 			logerr(__func__);
10897827cba2SAaron LI 			return NULL;
10907827cba2SAaron LI 		}
10917827cba2SAaron LI 		TAILQ_INIT(&state->addrs);
10927827cba2SAaron LI 		TAILQ_INIT(&state->ll_callbacks);
10937827cba2SAaron LI 	}
10947827cba2SAaron LI 	return state;
10957827cba2SAaron LI }
10967827cba2SAaron LI 
10978d36e1dfSRoy Marples struct ipv6_addr *
109812af092aSRoy Marples ipv6_anyglobal(struct interface *sifp)
10998d36e1dfSRoy Marples {
110012af092aSRoy Marples 	struct interface *ifp;
11018d36e1dfSRoy Marples 	struct ipv6_state *state;
11028d36e1dfSRoy Marples 	struct ipv6_addr *ia;
1103d4fb1e02SRoy Marples 	bool forwarding;
1104d4fb1e02SRoy Marples 
1105b8b69544SRoy Marples 	/* BSD forwarding is either on or off.
1106b8b69544SRoy Marples 	 * Linux forwarding is technically the same as it's
1107b8b69544SRoy Marples 	 * configured by the "all" interface.
1108b8b69544SRoy Marples 	 * Per interface only affects IsRouter of NA messages. */
1109b8b69544SRoy Marples #if defined(PRIVSEP) && (defined(HAVE_PLEDGE) || defined(__linux__))
1110d4fb1e02SRoy Marples 	if (IN_PRIVSEP(sifp->ctx))
1111b8b69544SRoy Marples 		forwarding = ps_root_ip6forwarding(sifp->ctx, NULL) != 0;
1112d4fb1e02SRoy Marples 	else
1113d4fb1e02SRoy Marples #endif
1114b8b69544SRoy Marples 		forwarding = ip6_forwarding(NULL) != 0;
11158d36e1dfSRoy Marples 
111612af092aSRoy Marples 	TAILQ_FOREACH(ifp, sifp->ctx->ifaces, next) {
1117d4fb1e02SRoy Marples 		if (ifp != sifp && !forwarding)
1118d4fb1e02SRoy Marples 			continue;
11198d36e1dfSRoy Marples 
11208d36e1dfSRoy Marples 		state = IPV6_STATE(ifp);
11218d36e1dfSRoy Marples 		if (state == NULL)
112212af092aSRoy Marples 			continue;
11238d36e1dfSRoy Marples 
11248d36e1dfSRoy Marples 		TAILQ_FOREACH(ia, &state->addrs, next) {
11258d36e1dfSRoy Marples 			if (IN6_IS_ADDR_LINKLOCAL(&ia->addr))
11268d36e1dfSRoy Marples 				continue;
11278d36e1dfSRoy Marples 			/* Let's be optimistic.
11288d36e1dfSRoy Marples 			 * Any decent OS won't forward or accept traffic
11298d36e1dfSRoy Marples 			 * from/to tentative or detached addresses. */
11308d36e1dfSRoy Marples 			if (!(ia->addr_flags & IN6_IFF_DUPLICATED))
11318d36e1dfSRoy Marples 				return ia;
11328d36e1dfSRoy Marples 		}
113312af092aSRoy Marples 	}
113412af092aSRoy Marples 	return NULL;
113512af092aSRoy Marples }
11368d36e1dfSRoy Marples 
11377827cba2SAaron LI void
11387827cba2SAaron LI ipv6_handleifa(struct dhcpcd_ctx *ctx,
11397827cba2SAaron LI     int cmd, struct if_head *ifs, const char *ifname,
11407827cba2SAaron LI     const struct in6_addr *addr, uint8_t prefix_len, int addrflags, pid_t pid)
11417827cba2SAaron LI {
11427827cba2SAaron LI 	struct interface *ifp;
11437827cba2SAaron LI 	struct ipv6_state *state;
11447827cba2SAaron LI 	struct ipv6_addr *ia;
11457827cba2SAaron LI 	struct ll_callback *cb;
11468d36e1dfSRoy Marples 	bool anyglobal;
11478d36e1dfSRoy Marples 
11488d36e1dfSRoy Marples #ifdef __sun
11498d36e1dfSRoy Marples 	struct sockaddr_in6 subnet;
11508d36e1dfSRoy Marples 
11518d36e1dfSRoy Marples 	/* Solaris on-link route is an unspecified address! */
11528d36e1dfSRoy Marples 	if (IN6_IS_ADDR_UNSPECIFIED(addr)) {
11538d36e1dfSRoy Marples 		if (if_getsubnet(ctx, ifname, AF_INET6,
11548d36e1dfSRoy Marples 		    &subnet, sizeof(subnet)) == -1)
11558d36e1dfSRoy Marples 		{
11568d36e1dfSRoy Marples 			logerr(__func__);
11578d36e1dfSRoy Marples 			return;
11588d36e1dfSRoy Marples 		}
11598d36e1dfSRoy Marples 		addr = &subnet.sin6_addr;
11608d36e1dfSRoy Marples 	}
11618d36e1dfSRoy Marples #endif
11627827cba2SAaron LI 
11637827cba2SAaron LI #if 0
11647827cba2SAaron LI 	char dbuf[INET6_ADDRSTRLEN];
11657827cba2SAaron LI 	const char *dbp;
11667827cba2SAaron LI 
11677827cba2SAaron LI 	dbp = inet_ntop(AF_INET6, &addr->s6_addr,
11687827cba2SAaron LI 	    dbuf, INET6_ADDRSTRLEN);
11698d36e1dfSRoy Marples 	loginfox("%s: cmd %d addr %s addrflags %d",
11708d36e1dfSRoy Marples 	    ifname, cmd, dbp, addrflags);
11717827cba2SAaron LI #endif
11727827cba2SAaron LI 
11737827cba2SAaron LI 	if (ifs == NULL)
11747827cba2SAaron LI 		ifs = ctx->ifaces;
11757827cba2SAaron LI 	if (ifs == NULL)
11767827cba2SAaron LI 		return;
11777827cba2SAaron LI 	if ((ifp = if_find(ifs, ifname)) == NULL)
11787827cba2SAaron LI 		return;
11797827cba2SAaron LI 	if ((state = ipv6_getstate(ifp)) == NULL)
11807827cba2SAaron LI 		return;
118112af092aSRoy Marples 	anyglobal = ipv6_anyglobal(ifp) != NULL;
11827827cba2SAaron LI 
11837827cba2SAaron LI 	TAILQ_FOREACH(ia, &state->addrs, next) {
11847827cba2SAaron LI 		if (IN6_ARE_ADDR_EQUAL(&ia->addr, addr))
11857827cba2SAaron LI 			break;
11867827cba2SAaron LI 	}
11877827cba2SAaron LI 
11887827cba2SAaron LI 	switch (cmd) {
11897827cba2SAaron LI 	case RTM_DELADDR:
11907827cba2SAaron LI 		if (ia != NULL) {
11917827cba2SAaron LI 			TAILQ_REMOVE(&state->addrs, ia, next);
11928d36e1dfSRoy Marples #ifdef ND6_ADVERTISE
11938d36e1dfSRoy Marples 			/* Advertise the address if it exists on
11948d36e1dfSRoy Marples 			 * another interface. */
11958d36e1dfSRoy Marples 			ipv6nd_advertise(ia);
11968d36e1dfSRoy Marples #endif
11977827cba2SAaron LI 			/* We'll free it at the end of the function. */
11987827cba2SAaron LI 		}
11997827cba2SAaron LI 		break;
12007827cba2SAaron LI 	case RTM_NEWADDR:
12017827cba2SAaron LI 		if (ia == NULL) {
12027827cba2SAaron LI 			ia = ipv6_newaddr(ifp, addr, prefix_len, 0);
12037827cba2SAaron LI #ifdef ALIAS_ADDR
12047827cba2SAaron LI 			strlcpy(ia->alias, ifname, sizeof(ia->alias));
12057827cba2SAaron LI #endif
12067827cba2SAaron LI 			if (if_getlifetime6(ia) == -1) {
12077827cba2SAaron LI 				/* No support or address vanished.
12087827cba2SAaron LI 				 * Either way, just set a deprecated
12097827cba2SAaron LI 				 * infinite time lifetime and continue.
12107827cba2SAaron LI 				 * This is fine because we only want
12117827cba2SAaron LI 				 * to know this when trying to extend
12127827cba2SAaron LI 				 * temporary addresses.
12137827cba2SAaron LI 				 * As we can't extend infinite, we'll
12147827cba2SAaron LI 				 * create a new temporary address. */
12157827cba2SAaron LI 				ia->prefix_pltime = 0;
12167827cba2SAaron LI 				ia->prefix_vltime =
12177827cba2SAaron LI 				    ND6_INFINITE_LIFETIME;
12187827cba2SAaron LI 			}
12197827cba2SAaron LI 			/* This is a minor regression against RFC 4941
12207827cba2SAaron LI 			 * because the kernel only knows when the
12217827cba2SAaron LI 			 * lifetimes were last updated, not when the
12227827cba2SAaron LI 			 * address was initially created.
12237827cba2SAaron LI 			 * Provided dhcpcd is not restarted, this
12247827cba2SAaron LI 			 * won't be a problem.
12257827cba2SAaron LI 			 * If we don't like it, we can always
12267827cba2SAaron LI 			 * pretend lifetimes are infinite and always
12277827cba2SAaron LI 			 * generate a new temporary address on
12287827cba2SAaron LI 			 * restart. */
12297827cba2SAaron LI 			ia->acquired = ia->created;
12307827cba2SAaron LI 			TAILQ_INSERT_TAIL(&state->addrs, ia, next);
12317827cba2SAaron LI 		}
12327827cba2SAaron LI 		ia->addr_flags = addrflags;
12337827cba2SAaron LI 		ia->flags &= ~IPV6_AF_STALE;
12347827cba2SAaron LI #ifdef IPV6_MANAGETEMPADDR
12357827cba2SAaron LI 		if (ia->addr_flags & IN6_IFF_TEMPORARY)
12367827cba2SAaron LI 			ia->flags |= IPV6_AF_TEMPORARY;
12377827cba2SAaron LI #endif
12387827cba2SAaron LI 		if (IN6_IS_ADDR_LINKLOCAL(&ia->addr) || ia->dadcallback) {
12397827cba2SAaron LI #ifdef IPV6_POLLADDRFLAG
12407827cba2SAaron LI 			if (ia->addr_flags & IN6_IFF_TENTATIVE) {
12416e63cc1fSRoy Marples 				eloop_timeout_add_msec(
12427827cba2SAaron LI 				    ia->iface->ctx->eloop,
12436e63cc1fSRoy Marples 				    RETRANS_TIMER / 2, ipv6_checkaddrflags, ia);
12447827cba2SAaron LI 				break;
12457827cba2SAaron LI 			}
12467827cba2SAaron LI #endif
12477827cba2SAaron LI 
1248d4fb1e02SRoy Marples 			if (ia->dadcallback)
12497827cba2SAaron LI 				ia->dadcallback(ia);
12507827cba2SAaron LI 
12517827cba2SAaron LI 			if (IN6_IS_ADDR_LINKLOCAL(&ia->addr) &&
12527827cba2SAaron LI 			    !(ia->addr_flags & IN6_IFF_NOTUSEABLE))
12537827cba2SAaron LI 			{
12547827cba2SAaron LI 				/* Now run any callbacks.
12557827cba2SAaron LI 				 * Typically IPv6RS or DHCPv6 */
12567827cba2SAaron LI 				while ((cb =
12577827cba2SAaron LI 				    TAILQ_FIRST(&state->ll_callbacks)))
12587827cba2SAaron LI 				{
12597827cba2SAaron LI 					TAILQ_REMOVE(
12607827cba2SAaron LI 					    &state->ll_callbacks,
12617827cba2SAaron LI 					    cb, next);
12627827cba2SAaron LI 					cb->callback(cb->arg);
12637827cba2SAaron LI 					free(cb);
12647827cba2SAaron LI 				}
12657827cba2SAaron LI 			}
12667827cba2SAaron LI 		}
12677827cba2SAaron LI 		break;
12687827cba2SAaron LI 	}
12697827cba2SAaron LI 
12707827cba2SAaron LI 	if (ia == NULL)
12717827cba2SAaron LI 		return;
12727827cba2SAaron LI 
12738d36e1dfSRoy Marples 	ctx->options &= ~DHCPCD_RTBUILD;
12747827cba2SAaron LI 	ipv6nd_handleifa(cmd, ia, pid);
12757827cba2SAaron LI #ifdef DHCP6
12767827cba2SAaron LI 	dhcp6_handleifa(cmd, ia, pid);
12777827cba2SAaron LI #endif
12787827cba2SAaron LI 
12797827cba2SAaron LI 	/* Done with the ia now, so free it. */
12807827cba2SAaron LI 	if (cmd == RTM_DELADDR)
12817827cba2SAaron LI 		ipv6_freeaddr(ia);
12828d36e1dfSRoy Marples 	else if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE))
12838d36e1dfSRoy Marples 		ia->flags |= IPV6_AF_DADCOMPLETED;
12848d36e1dfSRoy Marples 
12858d36e1dfSRoy Marples 	/* If we've not already called rt_build via the IPv6ND
12868d36e1dfSRoy Marples 	 * or DHCP6 handlers and the existance of any useable
12878d36e1dfSRoy Marples 	 * global address on the interface has changed,
12888d36e1dfSRoy Marples 	 * call rt_build to add/remove the default route. */
1289ce3872cbSRoy Marples 	if (ifp->active &&
1290ce3872cbSRoy Marples 	    ((ifp->options != NULL && ifp->options->options & DHCPCD_IPV6) ||
1291ce3872cbSRoy Marples 	     (ifp->options == NULL && ctx->options & DHCPCD_IPV6)) &&
12928d36e1dfSRoy Marples 	    !(ctx->options & DHCPCD_RTBUILD) &&
129312af092aSRoy Marples 	    (ipv6_anyglobal(ifp) != NULL) != anyglobal)
12948d36e1dfSRoy Marples 		rt_build(ctx, AF_INET6);
12957827cba2SAaron LI }
12967827cba2SAaron LI 
12977827cba2SAaron LI int
12987827cba2SAaron LI ipv6_hasaddr(const struct interface *ifp)
12997827cba2SAaron LI {
13007827cba2SAaron LI 
13017827cba2SAaron LI 	if (ipv6nd_iffindaddr(ifp, NULL, 0) != NULL)
13027827cba2SAaron LI 		return 1;
13038d36e1dfSRoy Marples #ifdef DHCP6
13047827cba2SAaron LI 	if (dhcp6_iffindaddr(ifp, NULL, 0) != NULL)
13057827cba2SAaron LI 		return 1;
13068d36e1dfSRoy Marples #endif
13077827cba2SAaron LI 	return 0;
13087827cba2SAaron LI }
13097827cba2SAaron LI 
13107827cba2SAaron LI struct ipv6_addr *
13117827cba2SAaron LI ipv6_iffindaddr(struct interface *ifp, const struct in6_addr *addr,
13127827cba2SAaron LI     int revflags)
13137827cba2SAaron LI {
13147827cba2SAaron LI 	struct ipv6_state *state;
13157827cba2SAaron LI 	struct ipv6_addr *ap;
13167827cba2SAaron LI 
13177827cba2SAaron LI 	state = IPV6_STATE(ifp);
13187827cba2SAaron LI 	if (state) {
13197827cba2SAaron LI 		TAILQ_FOREACH(ap, &state->addrs, next) {
13207827cba2SAaron LI 			if (addr == NULL) {
13217827cba2SAaron LI 				if (IN6_IS_ADDR_LINKLOCAL(&ap->addr) &&
13227827cba2SAaron LI 				    (!revflags || !(ap->addr_flags & revflags)))
13237827cba2SAaron LI 					return ap;
13247827cba2SAaron LI 			} else {
13257827cba2SAaron LI 				if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr) &&
13267827cba2SAaron LI 				    (!revflags || !(ap->addr_flags & revflags)))
13277827cba2SAaron LI 					return ap;
13287827cba2SAaron LI 			}
13297827cba2SAaron LI 		}
13307827cba2SAaron LI 	}
13317827cba2SAaron LI 	return NULL;
13327827cba2SAaron LI }
13337827cba2SAaron LI 
13347827cba2SAaron LI static struct ipv6_addr *
13357827cba2SAaron LI ipv6_iffindmaskaddr(const struct interface *ifp, const struct in6_addr *addr)
13367827cba2SAaron LI {
13377827cba2SAaron LI 	struct ipv6_state *state;
13387827cba2SAaron LI 	struct ipv6_addr *ap;
13397827cba2SAaron LI 	struct in6_addr mask;
13407827cba2SAaron LI 
13417827cba2SAaron LI 	state = IPV6_STATE(ifp);
13427827cba2SAaron LI 	if (state) {
13437827cba2SAaron LI 		TAILQ_FOREACH(ap, &state->addrs, next) {
13447827cba2SAaron LI 			if (ipv6_mask(&mask, ap->prefix_len) == -1)
13457827cba2SAaron LI 				continue;
13467827cba2SAaron LI 			if (IN6_ARE_MASKED_ADDR_EQUAL(&ap->addr, addr, &mask))
13477827cba2SAaron LI 				return ap;
13487827cba2SAaron LI 		}
13497827cba2SAaron LI 	}
13507827cba2SAaron LI 	return NULL;
13517827cba2SAaron LI }
13527827cba2SAaron LI 
13537827cba2SAaron LI struct ipv6_addr *
13547827cba2SAaron LI ipv6_findmaskaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr)
13557827cba2SAaron LI {
13567827cba2SAaron LI 	struct interface *ifp;
13577827cba2SAaron LI 	struct ipv6_addr *ap;
13587827cba2SAaron LI 
13597827cba2SAaron LI 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
13607827cba2SAaron LI 		ap = ipv6_iffindmaskaddr(ifp, addr);
13617827cba2SAaron LI 		if (ap != NULL)
13627827cba2SAaron LI 			return ap;
13637827cba2SAaron LI 	}
13647827cba2SAaron LI 	return NULL;
13657827cba2SAaron LI }
13667827cba2SAaron LI 
13677827cba2SAaron LI int
13687827cba2SAaron LI ipv6_addlinklocalcallback(struct interface *ifp,
13697827cba2SAaron LI     void (*callback)(void *), void *arg)
13707827cba2SAaron LI {
13717827cba2SAaron LI 	struct ipv6_state *state;
13727827cba2SAaron LI 	struct ll_callback *cb;
13737827cba2SAaron LI 
13747827cba2SAaron LI 	state = ipv6_getstate(ifp);
13757827cba2SAaron LI 	TAILQ_FOREACH(cb, &state->ll_callbacks, next) {
13767827cba2SAaron LI 		if (cb->callback == callback && cb->arg == arg)
13777827cba2SAaron LI 			break;
13787827cba2SAaron LI 	}
13797827cba2SAaron LI 	if (cb == NULL) {
13807827cba2SAaron LI 		cb = malloc(sizeof(*cb));
13817827cba2SAaron LI 		if (cb == NULL) {
13827827cba2SAaron LI 			logerr(__func__);
13837827cba2SAaron LI 			return -1;
13847827cba2SAaron LI 		}
13857827cba2SAaron LI 		cb->callback = callback;
13867827cba2SAaron LI 		cb->arg = arg;
13877827cba2SAaron LI 		TAILQ_INSERT_TAIL(&state->ll_callbacks, cb, next);
13887827cba2SAaron LI 	}
13897827cba2SAaron LI 	return 0;
13907827cba2SAaron LI }
13917827cba2SAaron LI 
13927827cba2SAaron LI static struct ipv6_addr *
13937827cba2SAaron LI ipv6_newlinklocal(struct interface *ifp)
13947827cba2SAaron LI {
13957827cba2SAaron LI 	struct ipv6_addr *ia;
13967827cba2SAaron LI 	struct in6_addr in6;
13977827cba2SAaron LI 
13987827cba2SAaron LI 	memset(&in6, 0, sizeof(in6));
13997827cba2SAaron LI 	in6.s6_addr32[0] = htonl(0xfe800000);
14007827cba2SAaron LI 	ia = ipv6_newaddr(ifp, &in6, 64, 0);
14017827cba2SAaron LI 	if (ia != NULL) {
14027827cba2SAaron LI 		ia->prefix_pltime = ND6_INFINITE_LIFETIME;
14037827cba2SAaron LI 		ia->prefix_vltime = ND6_INFINITE_LIFETIME;
14047827cba2SAaron LI 	}
14057827cba2SAaron LI 	return ia;
14067827cba2SAaron LI }
14077827cba2SAaron LI 
14087827cba2SAaron LI static const uint8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
14097827cba2SAaron LI static const uint8_t allone[8] =
14107827cba2SAaron LI     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
14117827cba2SAaron LI 
14127827cba2SAaron LI static int
14137827cba2SAaron LI ipv6_addlinklocal(struct interface *ifp)
14147827cba2SAaron LI {
14157827cba2SAaron LI 	struct ipv6_state *state;
14167827cba2SAaron LI 	struct ipv6_addr *ap, *ap2;
14177827cba2SAaron LI 	int dadcounter;
14187827cba2SAaron LI 
1419*b2927f2bSRoy Marples 	if (!(ifp->options->options & DHCPCD_CONFIGURE))
1420*b2927f2bSRoy Marples 		return 0;
1421*b2927f2bSRoy Marples 
14227827cba2SAaron LI 	/* Check sanity before malloc */
14237827cba2SAaron LI 	if (!(ifp->options->options & DHCPCD_SLAACPRIVATE)) {
1424d4fb1e02SRoy Marples 		switch (ifp->hwtype) {
14257827cba2SAaron LI 		case ARPHRD_ETHER:
14267827cba2SAaron LI 			/* Check for a valid hardware address */
14277827cba2SAaron LI 			if (ifp->hwlen != 6 && ifp->hwlen != 8) {
14287827cba2SAaron LI 				errno = ENOTSUP;
14297827cba2SAaron LI 				return -1;
14307827cba2SAaron LI 			}
14317827cba2SAaron LI 			if (memcmp(ifp->hwaddr, allzero, ifp->hwlen) == 0 ||
14327827cba2SAaron LI 			    memcmp(ifp->hwaddr, allone, ifp->hwlen) == 0)
14337827cba2SAaron LI 			{
14347827cba2SAaron LI 				errno = EINVAL;
14357827cba2SAaron LI 				return -1;
14367827cba2SAaron LI 			}
14377827cba2SAaron LI 			break;
14387827cba2SAaron LI 		default:
14397827cba2SAaron LI 			errno = ENOTSUP;
14407827cba2SAaron LI 			return -1;
14417827cba2SAaron LI 		}
14427827cba2SAaron LI 	}
14437827cba2SAaron LI 
14447827cba2SAaron LI 	state = ipv6_getstate(ifp);
14457827cba2SAaron LI 	if (state == NULL)
14467827cba2SAaron LI 		return -1;
14477827cba2SAaron LI 
14487827cba2SAaron LI 	ap = ipv6_newlinklocal(ifp);
14497827cba2SAaron LI 	if (ap == NULL)
14507827cba2SAaron LI 		return -1;
14517827cba2SAaron LI 
14527827cba2SAaron LI 	dadcounter = 0;
14537827cba2SAaron LI 	if (ifp->options->options & DHCPCD_SLAACPRIVATE) {
14547827cba2SAaron LI nextslaacprivate:
14557827cba2SAaron LI 		if (ipv6_makestableprivate(&ap->addr,
14567827cba2SAaron LI 			&ap->prefix, ap->prefix_len, ifp, &dadcounter) == -1)
14577827cba2SAaron LI 		{
14587827cba2SAaron LI 			free(ap);
14597827cba2SAaron LI 			return -1;
14607827cba2SAaron LI 		}
14617827cba2SAaron LI 		ap->dadcounter = dadcounter;
14627827cba2SAaron LI 	} else {
14637827cba2SAaron LI 		memcpy(ap->addr.s6_addr, ap->prefix.s6_addr, 8);
1464d4fb1e02SRoy Marples 		switch (ifp->hwtype) {
14657827cba2SAaron LI 		case ARPHRD_ETHER:
14667827cba2SAaron LI 			if (ifp->hwlen == 6) {
14677827cba2SAaron LI 				ap->addr.s6_addr[ 8] = ifp->hwaddr[0];
14687827cba2SAaron LI 				ap->addr.s6_addr[ 9] = ifp->hwaddr[1];
14697827cba2SAaron LI 				ap->addr.s6_addr[10] = ifp->hwaddr[2];
14707827cba2SAaron LI 				ap->addr.s6_addr[11] = 0xff;
14717827cba2SAaron LI 				ap->addr.s6_addr[12] = 0xfe;
14727827cba2SAaron LI 				ap->addr.s6_addr[13] = ifp->hwaddr[3];
14737827cba2SAaron LI 				ap->addr.s6_addr[14] = ifp->hwaddr[4];
14747827cba2SAaron LI 				ap->addr.s6_addr[15] = ifp->hwaddr[5];
14757827cba2SAaron LI 			} else if (ifp->hwlen == 8)
14767827cba2SAaron LI 				memcpy(&ap->addr.s6_addr[8], ifp->hwaddr, 8);
14777827cba2SAaron LI 			else {
14787827cba2SAaron LI 				free(ap);
14797827cba2SAaron LI 				errno = ENOTSUP;
14807827cba2SAaron LI 				return -1;
14817827cba2SAaron LI 			}
14827827cba2SAaron LI 			break;
14837827cba2SAaron LI 		}
14847827cba2SAaron LI 
14857827cba2SAaron LI 		/* Sanity check: g bit must not indciate "group" */
14867827cba2SAaron LI 		if (EUI64_GROUP(&ap->addr)) {
14877827cba2SAaron LI 			free(ap);
14887827cba2SAaron LI 			errno = EINVAL;
14897827cba2SAaron LI 			return -1;
14907827cba2SAaron LI 		}
14917827cba2SAaron LI 		EUI64_TO_IFID(&ap->addr);
14927827cba2SAaron LI 	}
14937827cba2SAaron LI 
14947827cba2SAaron LI 	/* Do we already have this address? */
14957827cba2SAaron LI 	TAILQ_FOREACH(ap2, &state->addrs, next) {
14967827cba2SAaron LI 		if (IN6_ARE_ADDR_EQUAL(&ap->addr, &ap2->addr)) {
14977827cba2SAaron LI 			if (ap2->addr_flags & IN6_IFF_DUPLICATED) {
14987827cba2SAaron LI 				if (ifp->options->options &
14997827cba2SAaron LI 				    DHCPCD_SLAACPRIVATE)
15007827cba2SAaron LI 				{
15017827cba2SAaron LI 					dadcounter++;
15027827cba2SAaron LI 					goto nextslaacprivate;
15037827cba2SAaron LI 				}
15047827cba2SAaron LI 				free(ap);
15057827cba2SAaron LI 				errno = EADDRNOTAVAIL;
15067827cba2SAaron LI 				return -1;
15077827cba2SAaron LI 			}
15087827cba2SAaron LI 
15097827cba2SAaron LI 			logwarnx("%s: waiting for %s to complete",
15107827cba2SAaron LI 			    ap2->iface->name, ap2->saddr);
15117827cba2SAaron LI 			free(ap);
15127827cba2SAaron LI 			errno =	EEXIST;
15137827cba2SAaron LI 			return 0;
15147827cba2SAaron LI 		}
15157827cba2SAaron LI 	}
15167827cba2SAaron LI 
15177827cba2SAaron LI 	inet_ntop(AF_INET6, &ap->addr, ap->saddr, sizeof(ap->saddr));
15187827cba2SAaron LI 	TAILQ_INSERT_TAIL(&state->addrs, ap, next);
15197827cba2SAaron LI 	ipv6_addaddr(ap, NULL);
15207827cba2SAaron LI 	return 1;
15217827cba2SAaron LI }
15227827cba2SAaron LI 
15237827cba2SAaron LI static int
15247827cba2SAaron LI ipv6_tryaddlinklocal(struct interface *ifp)
15257827cba2SAaron LI {
15267827cba2SAaron LI 	struct ipv6_addr *ia;
15277827cba2SAaron LI 
15287827cba2SAaron LI 	/* We can't assign a link-locak address to this,
15297827cba2SAaron LI 	 * the ppp process has to. */
15307827cba2SAaron LI 	if (ifp->flags & IFF_POINTOPOINT)
15317827cba2SAaron LI 		return 0;
15327827cba2SAaron LI 
15337827cba2SAaron LI 	ia = ipv6_iffindaddr(ifp, NULL, IN6_IFF_DUPLICATED);
15347827cba2SAaron LI 	if (ia != NULL) {
15357827cba2SAaron LI #ifdef IPV6_POLLADDRFLAG
15367827cba2SAaron LI 		if (ia->addr_flags & IN6_IFF_TENTATIVE) {
15376e63cc1fSRoy Marples 			eloop_timeout_add_msec(
15387827cba2SAaron LI 			    ia->iface->ctx->eloop,
15396e63cc1fSRoy Marples 			    RETRANS_TIMER / 2, ipv6_checkaddrflags, ia);
15407827cba2SAaron LI 		}
15417827cba2SAaron LI #endif
15427827cba2SAaron LI 		return 0;
15437827cba2SAaron LI 	}
15447827cba2SAaron LI 	if (!CAN_ADD_LLADDR(ifp))
15457827cba2SAaron LI 		return 0;
15467827cba2SAaron LI 
15477827cba2SAaron LI 	return ipv6_addlinklocal(ifp);
15487827cba2SAaron LI }
15497827cba2SAaron LI 
1550d4fb1e02SRoy Marples void
1551d4fb1e02SRoy Marples ipv6_setscope(struct sockaddr_in6 *sin, unsigned int ifindex)
1552d4fb1e02SRoy Marples {
1553d4fb1e02SRoy Marples 
1554d4fb1e02SRoy Marples #ifdef __KAME__
1555d4fb1e02SRoy Marples 	/* KAME based systems want to store the scope inside the sin6_addr
1556d4fb1e02SRoy Marples 	 * for link local addresses */
1557d4fb1e02SRoy Marples 	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
1558d4fb1e02SRoy Marples 		uint16_t scope = htons((uint16_t)ifindex);
1559d4fb1e02SRoy Marples 		memcpy(&sin->sin6_addr.s6_addr[2], &scope,
1560d4fb1e02SRoy Marples 		    sizeof(scope));
1561d4fb1e02SRoy Marples 	}
1562d4fb1e02SRoy Marples 	sin->sin6_scope_id = 0;
1563d4fb1e02SRoy Marples #else
1564d4fb1e02SRoy Marples 	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
1565d4fb1e02SRoy Marples 		sin->sin6_scope_id = ifindex;
1566d4fb1e02SRoy Marples 	else
1567d4fb1e02SRoy Marples 		sin->sin6_scope_id = 0;
1568d4fb1e02SRoy Marples #endif
1569d4fb1e02SRoy Marples }
1570d4fb1e02SRoy Marples 
1571d4fb1e02SRoy Marples unsigned int
1572d4fb1e02SRoy Marples ipv6_getscope(const struct sockaddr_in6 *sin)
1573d4fb1e02SRoy Marples {
1574d4fb1e02SRoy Marples #ifdef __KAME__
1575d4fb1e02SRoy Marples 	uint16_t scope;
1576d4fb1e02SRoy Marples #endif
1577d4fb1e02SRoy Marples 
1578d4fb1e02SRoy Marples 	if (!IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
1579d4fb1e02SRoy Marples 		return 0;
1580d4fb1e02SRoy Marples #ifdef __KAME__
1581d4fb1e02SRoy Marples 	memcpy(&scope, &sin->sin6_addr.s6_addr[2], sizeof(scope));
1582d4fb1e02SRoy Marples 	return (unsigned int)ntohs(scope);
1583d4fb1e02SRoy Marples #else
1584d4fb1e02SRoy Marples 	return (unsigned int)sin->sin6_scope_id;
1585d4fb1e02SRoy Marples #endif
1586d4fb1e02SRoy Marples }
1587d4fb1e02SRoy Marples 
15887827cba2SAaron LI struct ipv6_addr *
15897827cba2SAaron LI ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr,
15907827cba2SAaron LI     uint8_t prefix_len, unsigned int flags)
15917827cba2SAaron LI {
15926e63cc1fSRoy Marples 	struct ipv6_addr *ia, *iaf;
15937827cba2SAaron LI 	char buf[INET6_ADDRSTRLEN];
15947827cba2SAaron LI 	const char *cbp;
15957827cba2SAaron LI 	bool tempaddr;
15967827cba2SAaron LI 	int addr_flags;
15977827cba2SAaron LI 
15986e63cc1fSRoy Marples #ifdef IPV6_AF_TEMPORARY
15996e63cc1fSRoy Marples 	tempaddr = flags & IPV6_AF_TEMPORARY;
16006e63cc1fSRoy Marples #else
16016e63cc1fSRoy Marples 	tempaddr = false;
16026e63cc1fSRoy Marples #endif
16036e63cc1fSRoy Marples 
16047827cba2SAaron LI 	/* If adding a new DHCP / RA derived address, check current flags
16057827cba2SAaron LI 	 * from an existing address. */
1606280986e4SRoy Marples 	if (tempaddr)
1607280986e4SRoy Marples 		iaf = NULL;
1608280986e4SRoy Marples 	else if (flags & IPV6_AF_AUTOCONF)
16096e63cc1fSRoy Marples 		iaf = ipv6nd_iffindprefix(ifp, addr, prefix_len);
16107827cba2SAaron LI 	else
16116e63cc1fSRoy Marples 		iaf = ipv6_iffindaddr(ifp, addr, 0);
16126e63cc1fSRoy Marples 	if (iaf != NULL) {
16136e63cc1fSRoy Marples 		addr_flags = iaf->addr_flags;
16146e63cc1fSRoy Marples 		flags |= IPV6_AF_ADDED;
16156e63cc1fSRoy Marples 	} else
16167827cba2SAaron LI 		addr_flags = IN6_IFF_TENTATIVE;
16177827cba2SAaron LI 
16187827cba2SAaron LI 	ia = calloc(1, sizeof(*ia));
16197827cba2SAaron LI 	if (ia == NULL)
16207827cba2SAaron LI 		goto err;
16217827cba2SAaron LI 
16227827cba2SAaron LI 	ia->iface = ifp;
16237827cba2SAaron LI 	ia->addr_flags = addr_flags;
16248d36e1dfSRoy Marples 	ia->flags = IPV6_AF_NEW | flags;
16258d36e1dfSRoy Marples 	if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE))
16268d36e1dfSRoy Marples 		ia->flags |= IPV6_AF_DADCOMPLETED;
16277827cba2SAaron LI 	ia->prefix_len = prefix_len;
16287827cba2SAaron LI 	ia->dhcp6_fd = -1;
16297827cba2SAaron LI 
1630ce6cc02eSRoy Marples #ifndef SMALL
1631ce6cc02eSRoy Marples 	TAILQ_INIT(&ia->pd_pfxs);
1632ce6cc02eSRoy Marples #endif
1633ce6cc02eSRoy Marples 
16346e63cc1fSRoy Marples 	if (prefix_len == 128)
16356e63cc1fSRoy Marples 		goto makepfx;
1636280986e4SRoy Marples 	else if (ia->flags & IPV6_AF_AUTOCONF) {
16377827cba2SAaron LI 		ia->prefix = *addr;
16386e63cc1fSRoy Marples 		if (iaf != NULL)
16396e63cc1fSRoy Marples 			memcpy(&ia->addr, &iaf->addr, sizeof(ia->addr));
16406e63cc1fSRoy Marples 		else {
16417827cba2SAaron LI 			ia->dadcounter = ipv6_makeaddr(&ia->addr, ifp,
16427827cba2SAaron LI 			                               &ia->prefix,
1643280986e4SRoy Marples 						       ia->prefix_len,
1644280986e4SRoy Marples 						       ia->flags);
16457827cba2SAaron LI 			if (ia->dadcounter == -1)
16467827cba2SAaron LI 				goto err;
16476e63cc1fSRoy Marples 		}
16487827cba2SAaron LI 	} else if (ia->flags & IPV6_AF_RAPFX) {
16497827cba2SAaron LI 		ia->prefix = *addr;
16508d36e1dfSRoy Marples #ifdef __sun
16518d36e1dfSRoy Marples 		ia->addr = *addr;
16528d36e1dfSRoy Marples 		cbp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf));
16538d36e1dfSRoy Marples 		goto paddr;
16548d36e1dfSRoy Marples #else
16557827cba2SAaron LI 		return ia;
16568d36e1dfSRoy Marples #endif
16576e63cc1fSRoy Marples 	} else if (ia->flags & (IPV6_AF_REQUEST | IPV6_AF_DELEGATEDPFX)) {
16587827cba2SAaron LI 		ia->prefix = *addr;
16597827cba2SAaron LI 		cbp = inet_ntop(AF_INET6, &ia->prefix, buf, sizeof(buf));
16607827cba2SAaron LI 		goto paddr;
16617827cba2SAaron LI 	} else {
16626e63cc1fSRoy Marples makepfx:
16637827cba2SAaron LI 		ia->addr = *addr;
16647827cba2SAaron LI 		if (ipv6_makeprefix(&ia->prefix,
16657827cba2SAaron LI 		                    &ia->addr, ia->prefix_len) == -1)
16667827cba2SAaron LI 			goto err;
16677827cba2SAaron LI 	}
16687827cba2SAaron LI 
16697827cba2SAaron LI 	cbp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf));
16707827cba2SAaron LI paddr:
16717827cba2SAaron LI 	if (cbp == NULL)
16727827cba2SAaron LI 		goto err;
16737827cba2SAaron LI 	snprintf(ia->saddr, sizeof(ia->saddr), "%s/%d", cbp, ia->prefix_len);
16747827cba2SAaron LI 
16757827cba2SAaron LI 	return ia;
16767827cba2SAaron LI 
16777827cba2SAaron LI err:
16787827cba2SAaron LI 	logerr(__func__);
16797827cba2SAaron LI 	free(ia);
16807827cba2SAaron LI 	return NULL;
16817827cba2SAaron LI }
16827827cba2SAaron LI 
16837827cba2SAaron LI static void
16847827cba2SAaron LI ipv6_staticdadcallback(void *arg)
16857827cba2SAaron LI {
16867827cba2SAaron LI 	struct ipv6_addr *ia = arg;
16877827cba2SAaron LI 	int wascompleted;
16887827cba2SAaron LI 
16897827cba2SAaron LI 	wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
16907827cba2SAaron LI 	ia->flags |= IPV6_AF_DADCOMPLETED;
1691d4fb1e02SRoy Marples 	if (ia->addr_flags & IN6_IFF_DUPLICATED)
16927827cba2SAaron LI 		logwarnx("%s: DAD detected %s", ia->iface->name,
16937827cba2SAaron LI 		    ia->saddr);
16947827cba2SAaron LI 	else if (!wascompleted) {
16957827cba2SAaron LI 		logdebugx("%s: IPv6 static DAD completed",
16967827cba2SAaron LI 		    ia->iface->name);
16977827cba2SAaron LI 	}
16987827cba2SAaron LI 
16997827cba2SAaron LI #define FINISHED (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)
17007827cba2SAaron LI 	if (!wascompleted) {
17017827cba2SAaron LI 		struct interface *ifp;
17027827cba2SAaron LI 		struct ipv6_state *state;
17037827cba2SAaron LI 
17047827cba2SAaron LI 		ifp = ia->iface;
17057827cba2SAaron LI 		state = IPV6_STATE(ifp);
17067827cba2SAaron LI 		TAILQ_FOREACH(ia, &state->addrs, next) {
17077827cba2SAaron LI 			if (ia->flags & IPV6_AF_STATIC &&
17087827cba2SAaron LI 			    (ia->flags & FINISHED) != FINISHED)
17097827cba2SAaron LI 			{
17107827cba2SAaron LI 				wascompleted = 1;
17117827cba2SAaron LI 				break;
17127827cba2SAaron LI 			}
17137827cba2SAaron LI 		}
17147827cba2SAaron LI 		if (!wascompleted)
17157827cba2SAaron LI 			script_runreason(ifp, "STATIC6");
17167827cba2SAaron LI 	}
17177827cba2SAaron LI #undef FINISHED
17187827cba2SAaron LI }
17197827cba2SAaron LI 
17207827cba2SAaron LI ssize_t
17218d36e1dfSRoy Marples ipv6_env(FILE *fp, const char *prefix, const struct interface *ifp)
17227827cba2SAaron LI {
17237827cba2SAaron LI 	struct ipv6_addr *ia;
17247827cba2SAaron LI 
17257827cba2SAaron LI 	ia = ipv6_iffindaddr(UNCONST(ifp), &ifp->options->req_addr6,
17267827cba2SAaron LI 	    IN6_IFF_NOTUSEABLE);
17278d36e1dfSRoy Marples 	if (ia == NULL)
17288d36e1dfSRoy Marples 		return 0;
17298d36e1dfSRoy Marples 	if (efprintf(fp, "%s_ip6_address=%s", prefix, ia->saddr) == -1)
17308d36e1dfSRoy Marples 		return -1;
17318d36e1dfSRoy Marples 	return 1;
17327827cba2SAaron LI }
17337827cba2SAaron LI 
17347827cba2SAaron LI int
17357827cba2SAaron LI ipv6_staticdadcompleted(const struct interface *ifp)
17367827cba2SAaron LI {
17377827cba2SAaron LI 	const struct ipv6_state *state;
17387827cba2SAaron LI 	const struct ipv6_addr *ia;
17397827cba2SAaron LI 	int n;
17407827cba2SAaron LI 
17417827cba2SAaron LI 	if ((state = IPV6_CSTATE(ifp)) == NULL)
17427827cba2SAaron LI 		return 0;
17437827cba2SAaron LI 	n = 0;
17447827cba2SAaron LI #define COMPLETED (IPV6_AF_STATIC | IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)
17457827cba2SAaron LI 	TAILQ_FOREACH(ia, &state->addrs, next) {
17467827cba2SAaron LI 		if ((ia->flags & COMPLETED) == COMPLETED &&
17477827cba2SAaron LI 		    !(ia->addr_flags & IN6_IFF_NOTUSEABLE))
17487827cba2SAaron LI 			n++;
17497827cba2SAaron LI 	}
17507827cba2SAaron LI 	return n;
17517827cba2SAaron LI }
17527827cba2SAaron LI 
17537827cba2SAaron LI int
17547827cba2SAaron LI ipv6_startstatic(struct interface *ifp)
17557827cba2SAaron LI {
17567827cba2SAaron LI 	struct ipv6_addr *ia;
17577827cba2SAaron LI 	int run_script;
17587827cba2SAaron LI 
17597827cba2SAaron LI 	if (IN6_IS_ADDR_UNSPECIFIED(&ifp->options->req_addr6))
17607827cba2SAaron LI 		return 0;
17617827cba2SAaron LI 
17627827cba2SAaron LI 	ia = ipv6_iffindaddr(ifp, &ifp->options->req_addr6, 0);
17637827cba2SAaron LI 	if (ia != NULL &&
17647827cba2SAaron LI 	    (ia->prefix_len != ifp->options->req_prefix_len ||
17657827cba2SAaron LI 	    ia->addr_flags & IN6_IFF_NOTUSEABLE))
17667827cba2SAaron LI 	{
17677827cba2SAaron LI 		ipv6_deleteaddr(ia);
17687827cba2SAaron LI 		ia = NULL;
17697827cba2SAaron LI 	}
17707827cba2SAaron LI 	if (ia == NULL) {
17717827cba2SAaron LI 		struct ipv6_state *state;
17727827cba2SAaron LI 
17737827cba2SAaron LI 		ia = ipv6_newaddr(ifp, &ifp->options->req_addr6,
17747827cba2SAaron LI 		    ifp->options->req_prefix_len, 0);
17757827cba2SAaron LI 		if (ia == NULL)
17767827cba2SAaron LI 			return -1;
17777827cba2SAaron LI 		state = IPV6_STATE(ifp);
17787827cba2SAaron LI 		TAILQ_INSERT_TAIL(&state->addrs, ia, next);
17797827cba2SAaron LI 		run_script = 0;
17807827cba2SAaron LI 	} else
17817827cba2SAaron LI 		run_script = 1;
17827827cba2SAaron LI 	ia->flags |= IPV6_AF_STATIC | IPV6_AF_ONLINK;
17837827cba2SAaron LI 	ia->prefix_vltime = ND6_INFINITE_LIFETIME;
17847827cba2SAaron LI 	ia->prefix_pltime = ND6_INFINITE_LIFETIME;
17857827cba2SAaron LI 	ia->dadcallback = ipv6_staticdadcallback;
17867827cba2SAaron LI 	ipv6_addaddr(ia, NULL);
17877827cba2SAaron LI 	rt_build(ifp->ctx, AF_INET6);
17887827cba2SAaron LI 	if (run_script)
17897827cba2SAaron LI 		script_runreason(ifp, "STATIC6");
17907827cba2SAaron LI 	return 1;
17917827cba2SAaron LI }
17927827cba2SAaron LI 
17937827cba2SAaron LI /* Ensure the interface has a link-local address */
17947827cba2SAaron LI int
17957827cba2SAaron LI ipv6_start(struct interface *ifp)
17967827cba2SAaron LI {
17977827cba2SAaron LI #ifdef IPV6_POLLADDRFLAG
17987827cba2SAaron LI 	struct ipv6_state *state;
17997827cba2SAaron LI 
18007827cba2SAaron LI 	/* We need to update the address flags. */
18017827cba2SAaron LI 	if ((state = IPV6_STATE(ifp)) != NULL) {
18027827cba2SAaron LI 		struct ipv6_addr *ia;
18037827cba2SAaron LI 		const char *alias;
18047827cba2SAaron LI 		int flags;
18057827cba2SAaron LI 
18067827cba2SAaron LI 		TAILQ_FOREACH(ia, &state->addrs, next) {
18077827cba2SAaron LI #ifdef ALIAS_ADDR
18087827cba2SAaron LI 			alias = ia->alias;
18097827cba2SAaron LI #else
18107827cba2SAaron LI 			alias = NULL;
18117827cba2SAaron LI #endif
18127827cba2SAaron LI 			flags = if_addrflags6(ia->iface, &ia->addr, alias);
18137827cba2SAaron LI 			if (flags != -1)
18147827cba2SAaron LI 				ia->addr_flags = flags;
18157827cba2SAaron LI 		}
18167827cba2SAaron LI 	}
18177827cba2SAaron LI #endif
18187827cba2SAaron LI 
18197827cba2SAaron LI 	if (ipv6_tryaddlinklocal(ifp) == -1)
18207827cba2SAaron LI 		return -1;
18217827cba2SAaron LI 
18227827cba2SAaron LI 	return 0;
18237827cba2SAaron LI }
18247827cba2SAaron LI 
18257827cba2SAaron LI void
18267827cba2SAaron LI ipv6_freedrop(struct interface *ifp, int drop)
18277827cba2SAaron LI {
18287827cba2SAaron LI 	struct ipv6_state *state;
18297827cba2SAaron LI 	struct ll_callback *cb;
18307827cba2SAaron LI 
18317827cba2SAaron LI 	if (ifp == NULL)
18327827cba2SAaron LI 		return;
18337827cba2SAaron LI 
18347827cba2SAaron LI 	if ((state = IPV6_STATE(ifp)) == NULL)
18357827cba2SAaron LI 		return;
18367827cba2SAaron LI 
18377827cba2SAaron LI 	/* If we got here, we can get rid of any LL callbacks. */
18387827cba2SAaron LI 	while ((cb = TAILQ_FIRST(&state->ll_callbacks))) {
18397827cba2SAaron LI 		TAILQ_REMOVE(&state->ll_callbacks, cb, next);
18407827cba2SAaron LI 		free(cb);
18417827cba2SAaron LI 	}
18427827cba2SAaron LI 
18437827cba2SAaron LI 	ipv6_freedrop_addrs(&state->addrs, drop ? 2 : 0, NULL);
18447827cba2SAaron LI 	if (drop) {
18458d36e1dfSRoy Marples 		if (ifp->ctx->ra_routers != NULL)
18467827cba2SAaron LI 			rt_build(ifp->ctx, AF_INET6);
18477827cba2SAaron LI 	} else {
18487827cba2SAaron LI 		/* Because we need to cache the addresses we don't control,
18497827cba2SAaron LI 		 * we only free the state on when NOT dropping addresses. */
18507827cba2SAaron LI 		free(state);
18517827cba2SAaron LI 		ifp->if_data[IF_DATA_IPV6] = NULL;
18527827cba2SAaron LI 		eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
18537827cba2SAaron LI 	}
18547827cba2SAaron LI }
18557827cba2SAaron LI 
18567827cba2SAaron LI void
18577827cba2SAaron LI ipv6_ctxfree(struct dhcpcd_ctx *ctx)
18587827cba2SAaron LI {
18597827cba2SAaron LI 
18607827cba2SAaron LI 	free(ctx->ra_routers);
18617827cba2SAaron LI 	free(ctx->secret);
18627827cba2SAaron LI }
18637827cba2SAaron LI 
18647827cba2SAaron LI int
18657827cba2SAaron LI ipv6_handleifa_addrs(int cmd,
18667827cba2SAaron LI     struct ipv6_addrhead *addrs, const struct ipv6_addr *addr, pid_t pid)
18677827cba2SAaron LI {
18687827cba2SAaron LI 	struct ipv6_addr *ia, *ian;
18697827cba2SAaron LI 	uint8_t found, alldadcompleted;
18707827cba2SAaron LI 
18717827cba2SAaron LI 	alldadcompleted = 1;
18727827cba2SAaron LI 	found = 0;
18737827cba2SAaron LI 	TAILQ_FOREACH_SAFE(ia, addrs, next, ian) {
18747827cba2SAaron LI 		if (!IN6_ARE_ADDR_EQUAL(&addr->addr, &ia->addr)) {
18757827cba2SAaron LI 			if (ia->flags & IPV6_AF_ADDED &&
18767827cba2SAaron LI 			    !(ia->flags & IPV6_AF_DADCOMPLETED))
18777827cba2SAaron LI 				alldadcompleted = 0;
18787827cba2SAaron LI 			continue;
18797827cba2SAaron LI 		}
18807827cba2SAaron LI 		switch (cmd) {
18817827cba2SAaron LI 		case RTM_DELADDR:
18827827cba2SAaron LI 			if (ia->flags & IPV6_AF_ADDED) {
18837827cba2SAaron LI 				logwarnx("%s: pid %d deleted address %s",
18847827cba2SAaron LI 				    ia->iface->name, pid, ia->saddr);
18857827cba2SAaron LI 				ia->flags &= ~IPV6_AF_ADDED;
18867827cba2SAaron LI 			}
18876e63cc1fSRoy Marples 			ipv6_deletedaddr(ia);
18887827cba2SAaron LI 			if (ia->flags & IPV6_AF_DELEGATED) {
18897827cba2SAaron LI 				TAILQ_REMOVE(addrs, ia, next);
18907827cba2SAaron LI 				ipv6_freeaddr(ia);
18917827cba2SAaron LI 			}
18927827cba2SAaron LI 			break;
18937827cba2SAaron LI 		case RTM_NEWADDR:
1894d4fb1e02SRoy Marples 			ia->addr_flags = addr->addr_flags;
18957827cba2SAaron LI 			/* Safety - ignore tentative announcements */
1896d4fb1e02SRoy Marples 			if (ia->addr_flags &
18977827cba2SAaron LI 			    (IN6_IFF_DETACHED | IN6_IFF_TENTATIVE))
18987827cba2SAaron LI 				break;
18997827cba2SAaron LI 			if ((ia->flags & IPV6_AF_DADCOMPLETED) == 0) {
19007827cba2SAaron LI 				found++;
19017827cba2SAaron LI 				if (ia->dadcallback)
19027827cba2SAaron LI 					ia->dadcallback(ia);
19037827cba2SAaron LI 				/* We need to set this here in-case the
19047827cba2SAaron LI 				 * dadcallback function checks it */
19057827cba2SAaron LI 				ia->flags |= IPV6_AF_DADCOMPLETED;
19067827cba2SAaron LI 			}
19077827cba2SAaron LI 			break;
19087827cba2SAaron LI 		}
19097827cba2SAaron LI 	}
19107827cba2SAaron LI 
19117827cba2SAaron LI 	return alldadcompleted ? found : 0;
19127827cba2SAaron LI }
19137827cba2SAaron LI 
19147827cba2SAaron LI #ifdef IPV6_MANAGETEMPADDR
19157827cba2SAaron LI static void
1916280986e4SRoy Marples ipv6_regen_desync(struct interface *ifp, bool force)
19177827cba2SAaron LI {
19187827cba2SAaron LI 	struct ipv6_state *state;
19197a0236bfSRoy Marples 	unsigned int max;
19207827cba2SAaron LI 
19217827cba2SAaron LI 	state = IPV6_STATE(ifp);
19227827cba2SAaron LI 
19237827cba2SAaron LI 	/* RFC4941 Section 5 states that DESYNC_FACTOR must never be
19247827cba2SAaron LI 	 * greater than TEMP_VALID_LIFETIME - REGEN_ADVANCE.
1925280986e4SRoy Marples 	 * I believe this is an error and it should be never be greater than
19267827cba2SAaron LI 	 * TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE. */
19277a0236bfSRoy Marples 	max = TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE;
19287827cba2SAaron LI 	if (state->desync_factor && !force && state->desync_factor < max)
19297827cba2SAaron LI 		return;
19307827cba2SAaron LI 	if (state->desync_factor == 0)
19317827cba2SAaron LI 		state->desync_factor =
19326e63cc1fSRoy Marples 		    arc4random_uniform(MIN(MAX_DESYNC_FACTOR, max));
19337a0236bfSRoy Marples 	max = TEMP_PREFERRED_LIFETIME - state->desync_factor - REGEN_ADVANCE;
1934280986e4SRoy Marples 	eloop_timeout_add_sec(ifp->ctx->eloop, max, ipv6_regentempaddrs, ifp);
19357827cba2SAaron LI }
19367827cba2SAaron LI 
19377827cba2SAaron LI /* RFC4941 Section 3.3.7 */
19387827cba2SAaron LI static void
19397827cba2SAaron LI ipv6_tempdadcallback(void *arg)
19407827cba2SAaron LI {
19417827cba2SAaron LI 	struct ipv6_addr *ia = arg;
19427827cba2SAaron LI 
1943d4fb1e02SRoy Marples 	if (ia->addr_flags & IN6_IFF_DUPLICATED) {
19447827cba2SAaron LI 		struct ipv6_addr *ia1;
19457827cba2SAaron LI 		struct timespec tv;
19467827cba2SAaron LI 
19477827cba2SAaron LI 		if (++ia->dadcounter == TEMP_IDGEN_RETRIES) {
19487827cba2SAaron LI 			logerrx("%s: too many duplicate temporary addresses",
19497827cba2SAaron LI 			    ia->iface->name);
19507827cba2SAaron LI 			return;
19517827cba2SAaron LI 		}
19527827cba2SAaron LI 		clock_gettime(CLOCK_MONOTONIC, &tv);
19537827cba2SAaron LI 		if ((ia1 = ipv6_createtempaddr(ia, &tv)) == NULL)
19547827cba2SAaron LI 			logerr(__func__);
19557827cba2SAaron LI 		else
19567827cba2SAaron LI 			ia1->dadcounter = ia->dadcounter;
19577827cba2SAaron LI 		ipv6_deleteaddr(ia);
19587827cba2SAaron LI 		if (ia1)
19597827cba2SAaron LI 			ipv6_addaddr(ia1, &ia1->acquired);
19607827cba2SAaron LI 	}
19617827cba2SAaron LI }
19627827cba2SAaron LI 
19637827cba2SAaron LI struct ipv6_addr *
19647827cba2SAaron LI ipv6_createtempaddr(struct ipv6_addr *ia0, const struct timespec *now)
19657827cba2SAaron LI {
19667827cba2SAaron LI 	struct ipv6_state *state;
1967280986e4SRoy Marples 	struct interface *ifp = ia0->iface;
19687827cba2SAaron LI 	struct ipv6_addr *ia;
19697827cba2SAaron LI 
1970280986e4SRoy Marples 	ia = ipv6_newaddr(ifp, &ia0->prefix, ia0->prefix_len,
19717827cba2SAaron LI 	    IPV6_AF_AUTOCONF | IPV6_AF_TEMPORARY);
1972280986e4SRoy Marples 	if (ia == NULL)
1973280986e4SRoy Marples 		return NULL;
1974280986e4SRoy Marples 
19757827cba2SAaron LI 	ia->dadcallback = ipv6_tempdadcallback;
19767827cba2SAaron LI 	ia->created = ia->acquired = now ? *now : ia0->acquired;
19777827cba2SAaron LI 
19787827cba2SAaron LI 	/* Ensure desync is still valid */
1979280986e4SRoy Marples 	ipv6_regen_desync(ifp, false);
19807827cba2SAaron LI 
19817827cba2SAaron LI 	/* RFC4941 Section 3.3.4 */
1982280986e4SRoy Marples 	state = IPV6_STATE(ia->iface);
19837a0236bfSRoy Marples 	ia->prefix_pltime = MIN(ia0->prefix_pltime,
19847a0236bfSRoy Marples 	    TEMP_PREFERRED_LIFETIME - state->desync_factor);
19857a0236bfSRoy Marples 	ia->prefix_vltime = MIN(ia0->prefix_vltime, TEMP_VALID_LIFETIME);
19867827cba2SAaron LI 	if (ia->prefix_pltime <= REGEN_ADVANCE ||
19877827cba2SAaron LI 	    ia->prefix_pltime > ia0->prefix_vltime)
19887827cba2SAaron LI 	{
19897827cba2SAaron LI 		errno =	EINVAL;
19907827cba2SAaron LI 		free(ia);
19917827cba2SAaron LI 		return NULL;
19927827cba2SAaron LI 	}
19937827cba2SAaron LI 
19947827cba2SAaron LI 	TAILQ_INSERT_TAIL(&state->addrs, ia, next);
19957827cba2SAaron LI 	return ia;
19967827cba2SAaron LI }
19977827cba2SAaron LI 
19987827cba2SAaron LI struct ipv6_addr *
19997827cba2SAaron LI ipv6_settemptime(struct ipv6_addr *ia, int flags)
20007827cba2SAaron LI {
20017827cba2SAaron LI 	struct ipv6_state *state;
20027827cba2SAaron LI 	struct ipv6_addr *ap, *first;
20037827cba2SAaron LI 
20047827cba2SAaron LI 	state = IPV6_STATE(ia->iface);
20057827cba2SAaron LI 	first = NULL;
20067827cba2SAaron LI 	TAILQ_FOREACH_REVERSE(ap, &state->addrs, ipv6_addrhead, next) {
20077827cba2SAaron LI 		if (ap->flags & IPV6_AF_TEMPORARY &&
20087827cba2SAaron LI 		    ap->prefix_pltime &&
20097827cba2SAaron LI 		    IN6_ARE_ADDR_EQUAL(&ia->prefix, &ap->prefix))
20107827cba2SAaron LI 		{
20116e63cc1fSRoy Marples 			unsigned int max, ext;
20127827cba2SAaron LI 
20137827cba2SAaron LI 			if (flags == 0) {
20147827cba2SAaron LI 				if (ap->prefix_pltime -
20157827cba2SAaron LI 				    (uint32_t)(ia->acquired.tv_sec -
20167827cba2SAaron LI 				    ap->acquired.tv_sec)
20177827cba2SAaron LI 				    < REGEN_ADVANCE)
20187827cba2SAaron LI 					continue;
20197827cba2SAaron LI 
20207827cba2SAaron LI 				return ap;
20217827cba2SAaron LI 			}
20227827cba2SAaron LI 
20237827cba2SAaron LI 			if (!(ap->flags & IPV6_AF_ADDED))
20247827cba2SAaron LI 				ap->flags |= IPV6_AF_NEW | IPV6_AF_AUTOCONF;
20257827cba2SAaron LI 			ap->flags &= ~IPV6_AF_STALE;
20267827cba2SAaron LI 
20277827cba2SAaron LI 			/* RFC4941 Section 3.4
20287827cba2SAaron LI 			 * Deprecated prefix, deprecate the temporary address */
20297827cba2SAaron LI 			if (ia->prefix_pltime == 0) {
20307827cba2SAaron LI 				ap->prefix_pltime = 0;
20317827cba2SAaron LI 				goto valid;
20327827cba2SAaron LI 			}
20337827cba2SAaron LI 
20347827cba2SAaron LI 			/* Ensure desync is still valid */
2035280986e4SRoy Marples 			ipv6_regen_desync(ap->iface, false);
20367827cba2SAaron LI 
20377827cba2SAaron LI 			/* RFC4941 Section 3.3.2
20387827cba2SAaron LI 			 * Extend temporary times, but ensure that they
20397827cba2SAaron LI 			 * never last beyond the system limit. */
20406e63cc1fSRoy Marples 			ext = (unsigned int)ia->acquired.tv_sec
20416e63cc1fSRoy Marples 			    + ia->prefix_pltime;
20426e63cc1fSRoy Marples 			max = (unsigned int)(ap->created.tv_sec +
20437a0236bfSRoy Marples 			    TEMP_PREFERRED_LIFETIME -
20446e63cc1fSRoy Marples 			    state->desync_factor);
20457827cba2SAaron LI 			if (ext < max)
20467827cba2SAaron LI 				ap->prefix_pltime = ia->prefix_pltime;
20477827cba2SAaron LI 			else
20487827cba2SAaron LI 				ap->prefix_pltime =
20497827cba2SAaron LI 				    (uint32_t)(max - ia->acquired.tv_sec);
20507827cba2SAaron LI 
20517827cba2SAaron LI valid:
20526e63cc1fSRoy Marples 			ext = (unsigned int)ia->acquired.tv_sec +
20536e63cc1fSRoy Marples 			    ia->prefix_vltime;
20546e63cc1fSRoy Marples 			max = (unsigned int)(ap->created.tv_sec +
20557a0236bfSRoy Marples 			    TEMP_VALID_LIFETIME);
20567827cba2SAaron LI 			if (ext < max)
20577827cba2SAaron LI 				ap->prefix_vltime = ia->prefix_vltime;
20587827cba2SAaron LI 			else
20597827cba2SAaron LI 				ap->prefix_vltime =
20607827cba2SAaron LI 				    (uint32_t)(max - ia->acquired.tv_sec);
20617827cba2SAaron LI 
20627827cba2SAaron LI 			/* Just extend the latest matching prefix */
20637827cba2SAaron LI 			ap->acquired = ia->acquired;
20647827cba2SAaron LI 
20657827cba2SAaron LI 			/* If extending return the last match as
20667827cba2SAaron LI 			 * it's the most current.
20677827cba2SAaron LI 			 * If deprecating, deprecate any other addresses we
20687827cba2SAaron LI 			 * may have, although this should not be needed */
20697827cba2SAaron LI 			if (ia->prefix_pltime)
20707827cba2SAaron LI 				return ap;
20717827cba2SAaron LI 			if (first == NULL)
20727827cba2SAaron LI 				first = ap;
20737827cba2SAaron LI 		}
20747827cba2SAaron LI 	}
20757827cba2SAaron LI 	return first;
20767827cba2SAaron LI }
20777827cba2SAaron LI 
20787827cba2SAaron LI void
20797827cba2SAaron LI ipv6_addtempaddrs(struct interface *ifp, const struct timespec *now)
20807827cba2SAaron LI {
20817827cba2SAaron LI 	struct ipv6_state *state;
20827827cba2SAaron LI 	struct ipv6_addr *ia;
20837827cba2SAaron LI 
20847827cba2SAaron LI 	state = IPV6_STATE(ifp);
20857827cba2SAaron LI 	TAILQ_FOREACH(ia, &state->addrs, next) {
20867827cba2SAaron LI 		if (ia->flags & IPV6_AF_TEMPORARY &&
20877827cba2SAaron LI 		    !(ia->flags & IPV6_AF_STALE))
20887827cba2SAaron LI 			ipv6_addaddr(ia, now);
20897827cba2SAaron LI 	}
20907827cba2SAaron LI }
20917827cba2SAaron LI 
20927827cba2SAaron LI static void
2093280986e4SRoy Marples ipv6_regentempaddr0(struct ipv6_addr *ia, struct timespec *tv)
20947827cba2SAaron LI {
2095280986e4SRoy Marples 	struct ipv6_addr *ia1;
20967827cba2SAaron LI 
20977827cba2SAaron LI 	logdebugx("%s: regen temp addr %s", ia->iface->name, ia->saddr);
2098280986e4SRoy Marples 	ia1 = ipv6_createtempaddr(ia, tv);
20997827cba2SAaron LI 	if (ia1)
2100280986e4SRoy Marples 		ipv6_addaddr(ia1, tv);
21017827cba2SAaron LI 	else
21027827cba2SAaron LI 		logerr(__func__);
21037827cba2SAaron LI }
21047827cba2SAaron LI 
21057827cba2SAaron LI static void
2106280986e4SRoy Marples ipv6_regentempaddr(void *arg)
2107280986e4SRoy Marples {
2108280986e4SRoy Marples 	struct timespec tv;
2109280986e4SRoy Marples 
2110280986e4SRoy Marples 	clock_gettime(CLOCK_MONOTONIC, &tv);
2111280986e4SRoy Marples 	ipv6_regentempaddr0(arg, &tv);
2112280986e4SRoy Marples }
2113280986e4SRoy Marples 
2114280986e4SRoy Marples void
2115280986e4SRoy Marples ipv6_regentempaddrs(void *arg)
21167827cba2SAaron LI {
21177827cba2SAaron LI 	struct interface *ifp = arg;
2118280986e4SRoy Marples 	struct timespec tv;
21197827cba2SAaron LI 	struct ipv6_state *state;
2120280986e4SRoy Marples 	struct ipv6_addr *ia;
21217827cba2SAaron LI 
21227a0236bfSRoy Marples 	state = IPV6_STATE(ifp);
21237a0236bfSRoy Marples 	if (state == NULL)
21247a0236bfSRoy Marples 		return;
21257a0236bfSRoy Marples 
2126280986e4SRoy Marples 	ipv6_regen_desync(ifp, true);
2127280986e4SRoy Marples 
2128280986e4SRoy Marples 	clock_gettime(CLOCK_MONOTONIC, &tv);
2129d4fb1e02SRoy Marples 
2130d4fb1e02SRoy Marples 	/* Mark addresses for regen so we don't infinite loop. */
2131280986e4SRoy Marples 	TAILQ_FOREACH(ia, &state->addrs, next) {
2132280986e4SRoy Marples 		if (ia->flags & IPV6_AF_TEMPORARY &&
2133cc34ba0cSRoy Marples 		    ia->flags & IPV6_AF_ADDED &&
2134280986e4SRoy Marples 		    !(ia->flags & IPV6_AF_STALE))
2135d4fb1e02SRoy Marples 			ia->flags |= IPV6_AF_REGEN;
2136d4fb1e02SRoy Marples 		else
2137d4fb1e02SRoy Marples 			ia->flags &= ~IPV6_AF_REGEN;
2138d4fb1e02SRoy Marples 	}
2139d4fb1e02SRoy Marples 
2140d4fb1e02SRoy Marples 	/* Now regen temp addrs */
2141d4fb1e02SRoy Marples 	TAILQ_FOREACH(ia, &state->addrs, next) {
2142d4fb1e02SRoy Marples 		if (ia->flags & IPV6_AF_REGEN) {
2143280986e4SRoy Marples 			ipv6_regentempaddr0(ia, &tv);
2144d4fb1e02SRoy Marples 			ia->flags &= ~IPV6_AF_REGEN;
2145d4fb1e02SRoy Marples 		}
2146280986e4SRoy Marples 	}
21477827cba2SAaron LI }
21487827cba2SAaron LI #endif /* IPV6_MANAGETEMPADDR */
21497827cba2SAaron LI 
21507827cba2SAaron LI void
21517827cba2SAaron LI ipv6_markaddrsstale(struct interface *ifp, unsigned int flags)
21527827cba2SAaron LI {
21537827cba2SAaron LI 	struct ipv6_state *state;
21547827cba2SAaron LI 	struct ipv6_addr *ia;
21557827cba2SAaron LI 
21567827cba2SAaron LI 	state = IPV6_STATE(ifp);
21577827cba2SAaron LI 	if (state == NULL)
21587827cba2SAaron LI 		return;
21597827cba2SAaron LI 
21607827cba2SAaron LI 	TAILQ_FOREACH(ia, &state->addrs, next) {
21617827cba2SAaron LI 		if (flags == 0 || ia->flags & flags)
21627827cba2SAaron LI 			ia->flags |= IPV6_AF_STALE;
21637827cba2SAaron LI 	}
21647827cba2SAaron LI }
21657827cba2SAaron LI 
21667827cba2SAaron LI void
21677827cba2SAaron LI ipv6_deletestaleaddrs(struct interface *ifp)
21687827cba2SAaron LI {
21697827cba2SAaron LI 	struct ipv6_state *state;
21707827cba2SAaron LI 	struct ipv6_addr *ia, *ia1;
21717827cba2SAaron LI 
21727827cba2SAaron LI 	state = IPV6_STATE(ifp);
21737827cba2SAaron LI 	if (state == NULL)
21747827cba2SAaron LI 		return;
21757827cba2SAaron LI 
21767827cba2SAaron LI 	TAILQ_FOREACH_SAFE(ia, &state->addrs, next, ia1) {
21777827cba2SAaron LI 		if (ia->flags & IPV6_AF_STALE)
21787827cba2SAaron LI 			ipv6_handleifa(ifp->ctx, RTM_DELADDR,
21797827cba2SAaron LI 			    ifp->ctx->ifaces, ifp->name,
21807827cba2SAaron LI 			    &ia->addr, ia->prefix_len, 0, getpid());
21817827cba2SAaron LI 	}
21827827cba2SAaron LI }
21837827cba2SAaron LI 
21847827cba2SAaron LI 
21857827cba2SAaron LI static struct rt *
21867827cba2SAaron LI inet6_makeroute(struct interface *ifp, const struct ra *rap)
21877827cba2SAaron LI {
21887827cba2SAaron LI 	struct rt *rt;
21897827cba2SAaron LI 
21907827cba2SAaron LI 	if ((rt = rt_new(ifp)) == NULL)
21917827cba2SAaron LI 		return NULL;
21927827cba2SAaron LI 
21937827cba2SAaron LI #ifdef HAVE_ROUTE_METRIC
21947827cba2SAaron LI 	rt->rt_metric = ifp->metric;
21957827cba2SAaron LI #endif
21967827cba2SAaron LI 	if (rap != NULL)
21977827cba2SAaron LI 		rt->rt_mtu = rap->mtu;
21987827cba2SAaron LI 	return rt;
21997827cba2SAaron LI }
22007827cba2SAaron LI 
22017827cba2SAaron LI static struct rt *
22027827cba2SAaron LI inet6_makeprefix(struct interface *ifp, const struct ra *rap,
22037827cba2SAaron LI     const struct ipv6_addr *addr)
22047827cba2SAaron LI {
22057827cba2SAaron LI 	struct rt *rt;
22067827cba2SAaron LI 	struct in6_addr netmask;
22077827cba2SAaron LI 
22087827cba2SAaron LI 	if (addr == NULL || addr->prefix_len > 128) {
22097827cba2SAaron LI 		errno = EINVAL;
22107827cba2SAaron LI 		return NULL;
22117827cba2SAaron LI 	}
22127827cba2SAaron LI 
22137827cba2SAaron LI 	/* There is no point in trying to manage a /128 prefix,
22148d36e1dfSRoy Marples 	 * ones without a lifetime.  */
22158d36e1dfSRoy Marples 	if (addr->prefix_len == 128 || addr->prefix_vltime == 0)
22167827cba2SAaron LI 		return NULL;
22177827cba2SAaron LI 
22188d36e1dfSRoy Marples 	/* Don't install a reject route when not creating bigger prefixes. */
22197827cba2SAaron LI 	if (addr->flags & IPV6_AF_NOREJECT)
22207827cba2SAaron LI 		return NULL;
22217827cba2SAaron LI 
22227827cba2SAaron LI 	/* This address is the delegated prefix, so add a reject route for
22237827cba2SAaron LI 	 * it via the loopback interface. */
22247827cba2SAaron LI 	if (addr->flags & IPV6_AF_DELEGATEDPFX) {
22257827cba2SAaron LI 		struct interface *lo0;
22267827cba2SAaron LI 
22277827cba2SAaron LI 		TAILQ_FOREACH(lo0, ifp->ctx->ifaces, next) {
22287827cba2SAaron LI 			if (lo0->flags & IFF_LOOPBACK)
22297827cba2SAaron LI 				break;
22307827cba2SAaron LI 		}
22317827cba2SAaron LI 		if (lo0 == NULL)
22327827cba2SAaron LI 			logwarnx("cannot find a loopback interface "
22337827cba2SAaron LI 			    "to reject via");
22347827cba2SAaron LI 		else
22357827cba2SAaron LI 			ifp = lo0;
22367827cba2SAaron LI 	}
22377827cba2SAaron LI 
22387827cba2SAaron LI 	if ((rt = inet6_makeroute(ifp, rap)) == NULL)
22397827cba2SAaron LI 		return NULL;
22407827cba2SAaron LI 
22417827cba2SAaron LI 	sa_in6_init(&rt->rt_dest, &addr->prefix);
22427827cba2SAaron LI 	ipv6_mask(&netmask, addr->prefix_len);
22437827cba2SAaron LI 	sa_in6_init(&rt->rt_netmask, &netmask);
22447827cba2SAaron LI 	if (addr->flags & IPV6_AF_DELEGATEDPFX) {
22457827cba2SAaron LI 		rt->rt_flags |= RTF_REJECT;
22467827cba2SAaron LI 		/* Linux does not like a gateway for a reject route. */
22477827cba2SAaron LI #ifndef __linux__
22487827cba2SAaron LI 		sa_in6_init(&rt->rt_gateway, &in6addr_loopback);
22497827cba2SAaron LI #endif
22508d36e1dfSRoy Marples 	} else if (!(addr->flags & IPV6_AF_ONLINK))
22518d36e1dfSRoy Marples 		sa_in6_init(&rt->rt_gateway, &rap->from);
22528d36e1dfSRoy Marples 	else
22537827cba2SAaron LI 		rt->rt_gateway.sa_family = AF_UNSPEC;
22547827cba2SAaron LI 	sa_in6_init(&rt->rt_ifa, &addr->addr);
22557827cba2SAaron LI 	return rt;
22567827cba2SAaron LI }
22577827cba2SAaron LI 
22587827cba2SAaron LI static struct rt *
22597827cba2SAaron LI inet6_makerouter(struct ra *rap)
22607827cba2SAaron LI {
22617827cba2SAaron LI 	struct rt *rt;
22627827cba2SAaron LI 
22637827cba2SAaron LI 	if ((rt = inet6_makeroute(rap->iface, rap)) == NULL)
22647827cba2SAaron LI 		return NULL;
22657827cba2SAaron LI 	sa_in6_init(&rt->rt_dest, &in6addr_any);
22667827cba2SAaron LI 	sa_in6_init(&rt->rt_netmask, &in6addr_any);
22677827cba2SAaron LI 	sa_in6_init(&rt->rt_gateway, &rap->from);
22687827cba2SAaron LI 	return rt;
22697827cba2SAaron LI }
22707827cba2SAaron LI 
22717827cba2SAaron LI #define RT_IS_DEFAULT(rtp) \
22727827cba2SAaron LI 	(IN6_ARE_ADDR_EQUAL(&((rtp)->dest), &in6addr_any) &&		      \
22737827cba2SAaron LI 	    IN6_ARE_ADDR_EQUAL(&((rtp)->mask), &in6addr_any))
22747827cba2SAaron LI 
22757827cba2SAaron LI static int
22768d36e1dfSRoy Marples inet6_staticroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
22777827cba2SAaron LI {
22787827cba2SAaron LI 	struct interface *ifp;
22797827cba2SAaron LI 	struct ipv6_state *state;
22807827cba2SAaron LI 	struct ipv6_addr *ia;
22817827cba2SAaron LI 	struct rt *rt;
22827827cba2SAaron LI 
22837827cba2SAaron LI 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
22847827cba2SAaron LI 		if ((state = IPV6_STATE(ifp)) == NULL)
22857827cba2SAaron LI 			continue;
22867827cba2SAaron LI 		TAILQ_FOREACH(ia, &state->addrs, next) {
22877827cba2SAaron LI 			if ((ia->flags & (IPV6_AF_ADDED | IPV6_AF_STATIC)) ==
22887827cba2SAaron LI 			    (IPV6_AF_ADDED | IPV6_AF_STATIC))
22897827cba2SAaron LI 			{
22907827cba2SAaron LI 				rt = inet6_makeprefix(ifp, NULL, ia);
22917827cba2SAaron LI 				if (rt)
22928d36e1dfSRoy Marples 					rt_proto_add(routes, rt);
22937827cba2SAaron LI 			}
22947827cba2SAaron LI 		}
22957827cba2SAaron LI 	}
22967827cba2SAaron LI 	return 0;
22977827cba2SAaron LI }
22987827cba2SAaron LI 
22997827cba2SAaron LI static int
23008d36e1dfSRoy Marples inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
23017827cba2SAaron LI {
23027827cba2SAaron LI 	struct rt *rt;
23037827cba2SAaron LI 	struct ra *rap;
23047827cba2SAaron LI 	const struct ipv6_addr *addr;
23057827cba2SAaron LI 
23066e63cc1fSRoy Marples 	if (ctx->ra_routers == NULL)
23076e63cc1fSRoy Marples 		return 0;
23086e63cc1fSRoy Marples 
23097827cba2SAaron LI 	TAILQ_FOREACH(rap, ctx->ra_routers, next) {
23108d36e1dfSRoy Marples 		if (rap->expired)
23117827cba2SAaron LI 			continue;
23127827cba2SAaron LI 		TAILQ_FOREACH(addr, &rap->addrs, next) {
23137827cba2SAaron LI 			if (addr->prefix_vltime == 0)
23147827cba2SAaron LI 				continue;
23157827cba2SAaron LI 			rt = inet6_makeprefix(rap->iface, rap, addr);
23167827cba2SAaron LI 			if (rt) {
23177827cba2SAaron LI 				rt->rt_dflags |= RTDF_RA;
2318280986e4SRoy Marples #ifdef HAVE_ROUTE_PREF
2319280986e4SRoy Marples 				rt->rt_pref = ipv6nd_rtpref(rap);
2320280986e4SRoy Marples #endif
23218d36e1dfSRoy Marples 				rt_proto_add(routes, rt);
23227827cba2SAaron LI 			}
23237827cba2SAaron LI 		}
23248d36e1dfSRoy Marples 		if (rap->lifetime == 0)
23258d36e1dfSRoy Marples 			continue;
232612af092aSRoy Marples 		if (ipv6_anyglobal(rap->iface) == NULL)
23278d36e1dfSRoy Marples 			continue;
23287827cba2SAaron LI 		rt = inet6_makerouter(rap);
23298d36e1dfSRoy Marples 		if (rt == NULL)
23308d36e1dfSRoy Marples 			continue;
23317827cba2SAaron LI 		rt->rt_dflags |= RTDF_RA;
2332280986e4SRoy Marples #ifdef HAVE_ROUTE_PREF
2333280986e4SRoy Marples 		rt->rt_pref = ipv6nd_rtpref(rap);
2334280986e4SRoy Marples #endif
23358d36e1dfSRoy Marples 		rt_proto_add(routes, rt);
23367827cba2SAaron LI 	}
23377827cba2SAaron LI 	return 0;
23387827cba2SAaron LI }
23397827cba2SAaron LI 
23408d36e1dfSRoy Marples #ifdef DHCP6
23417827cba2SAaron LI static int
23428d36e1dfSRoy Marples inet6_dhcproutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx,
23437827cba2SAaron LI     enum DH6S dstate)
23447827cba2SAaron LI {
23457827cba2SAaron LI 	struct interface *ifp;
23467827cba2SAaron LI 	const struct dhcp6_state *d6_state;
23477827cba2SAaron LI 	const struct ipv6_addr *addr;
23487827cba2SAaron LI 	struct rt *rt;
23497827cba2SAaron LI 
23507827cba2SAaron LI 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
23517827cba2SAaron LI 		d6_state = D6_CSTATE(ifp);
23527827cba2SAaron LI 		if (d6_state && d6_state->state == dstate) {
23537827cba2SAaron LI 			TAILQ_FOREACH(addr, &d6_state->addrs, next) {
23547827cba2SAaron LI 				rt = inet6_makeprefix(ifp, NULL, addr);
23558d36e1dfSRoy Marples 				if (rt == NULL)
23568d36e1dfSRoy Marples 					continue;
23577827cba2SAaron LI 				rt->rt_dflags |= RTDF_DHCP;
23588d36e1dfSRoy Marples 				rt_proto_add(routes, rt);
23597827cba2SAaron LI 			}
23607827cba2SAaron LI 		}
23617827cba2SAaron LI 	}
23627827cba2SAaron LI 	return 0;
23637827cba2SAaron LI }
23648d36e1dfSRoy Marples #endif
23657827cba2SAaron LI 
23667827cba2SAaron LI bool
23678d36e1dfSRoy Marples inet6_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes)
23687827cba2SAaron LI {
23697827cba2SAaron LI 
23707827cba2SAaron LI 	/* Should static take priority? */
23717827cba2SAaron LI 	if (inet6_staticroutes(routes, ctx) == -1)
23727827cba2SAaron LI 		return false;
23737827cba2SAaron LI 
23747827cba2SAaron LI 	/* First add reachable routers and their prefixes */
23758d36e1dfSRoy Marples 	if (inet6_raroutes(routes, ctx) == -1)
23767827cba2SAaron LI 		return false;
23777827cba2SAaron LI 
23788d36e1dfSRoy Marples #ifdef DHCP6
23797827cba2SAaron LI 	/* We have no way of knowing if prefixes added by DHCP are reachable
23807827cba2SAaron LI 	 * or not, so we have to assume they are.
23818d36e1dfSRoy Marples 	 * Add bound before delegated so we can prefer interfaces better. */
23827827cba2SAaron LI 	if (inet6_dhcproutes(routes, ctx, DH6S_BOUND) == -1)
23837827cba2SAaron LI 		return false;
23847827cba2SAaron LI 	if (inet6_dhcproutes(routes, ctx, DH6S_DELEGATED) == -1)
23857827cba2SAaron LI 		return false;
23867827cba2SAaron LI #endif
23877827cba2SAaron LI 
23887827cba2SAaron LI 	return true;
23897827cba2SAaron LI }
2390