1ab3e3dbume/*	$KAME: rtsol.c,v 1.27 2003/10/05 00:09:36 itojun Exp $	*/
244c3d48kris
3872b698pfg/*-
4872b698pfg * SPDX-License-Identifier: BSD-3-Clause
5872b698pfg *
68c2ccb5shin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
710df0afhrs * Copyright (C) 2011 Hiroki Sato
88c2ccb5shin * All rights reserved.
9e041633kris *
108c2ccb5shin * Redistribution and use in source and binary forms, with or without
118c2ccb5shin * modification, are permitted provided that the following conditions
128c2ccb5shin * are met:
138c2ccb5shin * 1. Redistributions of source code must retain the above copyright
148c2ccb5shin *    notice, this list of conditions and the following disclaimer.
158c2ccb5shin * 2. Redistributions in binary form must reproduce the above copyright
168c2ccb5shin *    notice, this list of conditions and the following disclaimer in the
178c2ccb5shin *    documentation and/or other materials provided with the distribution.
188c2ccb5shin * 3. Neither the name of the project nor the names of its contributors
198c2ccb5shin *    may be used to endorse or promote products derived from this software
208c2ccb5shin *    without specific prior written permission.
21e041633kris *
228c2ccb5shin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
238c2ccb5shin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
248c2ccb5shin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
258c2ccb5shin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
268c2ccb5shin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
278c2ccb5shin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
288c2ccb5shin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
298c2ccb5shin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
308c2ccb5shin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
318c2ccb5shin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
328c2ccb5shin * SUCH DAMAGE.
338c2ccb5shin *
348c2ccb5shin * $FreeBSD$
358c2ccb5shin */
368c2ccb5shin
378c2ccb5shin#include <sys/param.h>
385944defmarkj#include <sys/capsicum.h>
395944defmarkj#include <sys/queue.h>
408c2ccb5shin#include <sys/socket.h>
415944defmarkj#include <sys/stat.h>
428c2ccb5shin#include <sys/uio.h>
4302ba92cume#include <sys/wait.h>
448c2ccb5shin
458c2ccb5shin#include <net/if.h>
468c2ccb5shin#include <net/route.h>
478c2ccb5shin#include <net/if_dl.h>
488c2ccb5shin
4910df0afhrs#define	__BSD_VISIBLE	1	/* IN6ADDR_LINKLOCAL_ALLROUTERS_INIT */
508c2ccb5shin#include <netinet/in.h>
5110df0afhrs#undef 	__BSD_VISIBLE
528c2ccb5shin#include <netinet/ip6.h>
538c2ccb5shin#include <netinet6/ip6_var.h>
548c2ccb5shin#include <netinet/icmp6.h>
558c2ccb5shin
568c2ccb5shin#include <arpa/inet.h>
578c2ccb5shin
585944defmarkj#include <capsicum_helpers.h>
5910df0afhrs#include <netdb.h>
608c2ccb5shin#include <time.h>
61e63e485ume#include <fcntl.h>
628c2ccb5shin#include <unistd.h>
638c2ccb5shin#include <stdio.h>
6413c1bcfhrs#include <time.h>
658c2ccb5shin#include <err.h>
668c2ccb5shin#include <errno.h>
678c2ccb5shin#include <string.h>
688c2ccb5shin#include <stdlib.h>
698c2ccb5shin#include <syslog.h>
708c2ccb5shin#include "rtsold.h"
718c2ccb5shin
7206dd203hrsstatic char rsid[IFNAMSIZ + 1 + sizeof(DNSINFO_ORIGIN_LABEL) + 1 + NI_MAXHOST];
735944defmarkjstruct ifinfo_head_t ifinfo_head = TAILQ_HEAD_INITIALIZER(ifinfo_head);
748c2ccb5shin
755944defmarkjstatic void call_script(const char *const *, struct script_msg_head_t *);
7610df0afhrsstatic size_t dname_labeldec(char *, size_t, const char *);
771eefc7ahrsstatic struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t);
7806dd203hrsstatic int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *,
7906dd203hrs    struct script_msg_head_t *, struct script_msg_head_t *);
8006dd203hrsstatic char *make_rsid(const char *, const char *, struct rainfo *);
8102ba92cume
8210df0afhrs#define	_ARGS_OTHER	otherconf_script, ifi->ifname
8306dd203hrs#define	_ARGS_RESADD	resolvconf_script, "-a", rsid
8406dd203hrs#define	_ARGS_RESDEL	resolvconf_script, "-d", rsid
8510df0afhrs
869029874markj#define	CALL_SCRIPT(name, sm_head) do {				\
879029874markj	const char *const sarg[] = { _ARGS_##name, NULL };	\
885944defmarkj	call_script(sarg, sm_head);				\
899029874markj} while (0)
909029874markj
919029874markj#define	ELM_MALLOC(p, error_action) do {			\
929029874markj	p = malloc(sizeof(*p));					\
939029874markj	if (p == NULL) {					\
949029874markj		warnmsg(LOG_ERR, __func__, "malloc failed: %s", \
959029874markj		    strerror(errno));				\
969029874markj		error_action;					\
979029874markj	}							\
989029874markj	memset(p, 0, sizeof(*p));				\
999029874markj} while (0)
10010df0afhrs
1018c2ccb5shinint
1025944defmarkjrecvsockopen(void)
1038c2ccb5shin{
104e63e485ume	struct icmp6_filter filt;
1055944defmarkj	cap_rights_t rights;
1065944defmarkj	int on, sock;
107e041633kris
1085944defmarkj	if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
10952b4a69ume		warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno));
1105944defmarkj		goto fail;
1118c2ccb5shin	}
1128c2ccb5shin
1135944defmarkj	/* Provide info about the receiving interface. */
1148c2ccb5shin	on = 1;
1155944defmarkj	if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
116958362fume	    sizeof(on)) < 0) {
1175944defmarkj		warnmsg(LOG_ERR, __func__, "setsockopt(IPV6_RECVPKTINFO): %s",
118958362fume		    strerror(errno));
1195944defmarkj		goto fail;
120e041633kris	}
1218c2ccb5shin
1225944defmarkj	/* Include the hop limit from the received header. */
12310df0afhrs	on = 1;
1245944defmarkj	if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
125958362fume	    sizeof(on)) < 0) {
1265944defmarkj		warnmsg(LOG_ERR, __func__, "setsockopt(IPV6_RECVHOPLIMIT): %s",
127958362fume		    strerror(errno));
1285944defmarkj		goto fail;
129e041633kris	}
1308c2ccb5shin
1315944defmarkj	/* Filter out everything except for Router Advertisements. */
1328c2ccb5shin	ICMP6_FILTER_SETBLOCKALL(&filt);
1338c2ccb5shin	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
1345944defmarkj	if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
135958362fume	    sizeof(filt)) == -1) {
13652b4a69ume		warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s",
137958362fume		    strerror(errno));
1385944defmarkj		goto fail;
1398c2ccb5shin	}
1408c2ccb5shin
1415944defmarkj	cap_rights_init(&rights, CAP_EVENT, CAP_RECV);
1425944defmarkj	if (caph_rights_limit(sock, &rights) < 0) {
1435944defmarkj		warnmsg(LOG_ERR, __func__, "caph_rights_limit(): %s",
1445944defmarkj		    strerror(errno));
1455944defmarkj		goto fail;
1468c2ccb5shin	}
1478c2ccb5shin
1485944defmarkj	return (sock);
1495944defmarkj
1505944defmarkjfail:
1515944defmarkj	if (sock >= 0)
1525944defmarkj		(void)close(sock);
1535944defmarkj	return (-1);
1548c2ccb5shin}
1558c2ccb5shin
1568c2ccb5shinvoid
1575944defmarkjrtsol_input(int sock)
1588c2ccb5shin{
1595944defmarkj	uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
1605944defmarkj	    CMSG_SPACE(sizeof(int))];
1615944defmarkj	struct iovec iov;
1625944defmarkj	struct msghdr hdr;
1635944defmarkj	struct sockaddr_in6 from;
1645944defmarkj	char answer[1500], ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
16510df0afhrs	int l, ifindex = 0, *hlimp = NULL;
16610df0afhrs	ssize_t msglen;
1678c2ccb5shin	struct in6_pktinfo *pi = NULL;
1688c2ccb5shin	struct ifinfo *ifi = NULL;
16910df0afhrs	struct ra_opt *rao = NULL;
170e63e485ume	struct icmp6_hdr *icp;
17102ba92cume	struct nd_router_advert *nd_ra;
172e63e485ume	struct cmsghdr *cm;
1731eefc7ahrs	struct rainfo *rai;
1745944defmarkj	char *p, *raoptp;
17510df0afhrs	struct in6_addr *addr;
17610df0afhrs	struct nd_opt_hdr *ndo;
17710df0afhrs	struct nd_opt_rdnss *rdnss;
17810df0afhrs	struct nd_opt_dnssl *dnssl;
17910df0afhrs	size_t len;
18010df0afhrs	char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1];
18110df0afhrs	char dname[NI_MAXHOST];
1825944defmarkj	struct timespec lifetime, now;
1835944defmarkj	int newent_rai, newent_rao;
1845944defmarkj
1855944defmarkj	memset(&hdr, 0, sizeof(hdr));
1865944defmarkj	hdr.msg_iov = &iov;
1875944defmarkj	hdr.msg_iovlen = 1;
1885944defmarkj	hdr.msg_name = &from;
1895944defmarkj	hdr.msg_namelen = sizeof(from);
1905944defmarkj	hdr.msg_control = cmsg;
1915944defmarkj	hdr.msg_controllen = sizeof(cmsg);
1925944defmarkj
1935944defmarkj	iov.iov_base = (caddr_t)answer;
1945944defmarkj	iov.iov_len = sizeof(answer);
1955944defmarkj
1965944defmarkj	if ((msglen = recvmsg(sock, &hdr, 0)) < 0) {
19752b4a69ume		warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno));
1988c2ccb5shin		return;
1998c2ccb5shin	}
2008c2ccb5shin
2015944defmarkj	/* Extract control message info. */
2025944defmarkj	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&hdr); cm != NULL;
2035944defmarkj	    cm = (struct cmsghdr *)CMSG_NXTHDR(&hdr, cm)) {
2048c2ccb5shin		if (cm->cmsg_level == IPPROTO_IPV6 &&
2058c2ccb5shin		    cm->cmsg_type == IPV6_PKTINFO &&
2068c2ccb5shin		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
207a8298dchrs			pi = (struct in6_pktinfo *)(void *)(CMSG_DATA(cm));
2088c2ccb5shin			ifindex = pi->ipi6_ifindex;
2098c2ccb5shin		}
2108c2ccb5shin		if (cm->cmsg_level == IPPROTO_IPV6 &&
2118c2ccb5shin		    cm->cmsg_type == IPV6_HOPLIMIT &&
2128c2ccb5shin		    cm->cmsg_len == CMSG_LEN(sizeof(int)))
213a8298dchrs			hlimp = (int *)(void *)CMSG_DATA(cm);
2148c2ccb5shin	}
2158c2ccb5shin
2168c2ccb5shin	if (ifindex == 0) {
217958362fume		warnmsg(LOG_ERR, __func__,
218958362fume		    "failed to get receiving interface");
2198c2ccb5shin		return;
2208c2ccb5shin	}
2218c2ccb5shin	if (hlimp == NULL) {
222958362fume		warnmsg(LOG_ERR, __func__,
223958362fume		    "failed to get receiving hop limit");
2248c2ccb5shin		return;
2258c2ccb5shin	}
2268c2ccb5shin
22710df0afhrs	if ((size_t)msglen < sizeof(struct nd_router_advert)) {
22830892caume		warnmsg(LOG_INFO, __func__,
22910df0afhrs		    "packet size(%zd) is too short", msglen);
2308c2ccb5shin		return;
2318c2ccb5shin	}
2328c2ccb5shin
2335944defmarkj	icp = (struct icmp6_hdr *)iov.iov_base;
2348c2ccb5shin	if (icp->icmp6_type != ND_ROUTER_ADVERT) {
23530892caume		/*
23630892caume		 * this should not happen because we configured a filter
23730892caume		 * that only passes RAs on the receiving socket.
23830892caume		 */
23952b4a69ume		warnmsg(LOG_ERR, __func__,
240958362fume		    "invalid icmp type(%d) from %s on %s", icp->icmp6_type,
241958362fume		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
24206dd203hrs			sizeof(ntopbuf)),
243958362fume		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
2448c2ccb5shin		return;
2458c2ccb5shin	}
2468c2ccb5shin
2478c2ccb5shin	if (icp->icmp6_code != 0) {
24830892caume		warnmsg(LOG_INFO, __func__,
249958362fume		    "invalid icmp code(%d) from %s on %s", icp->icmp6_code,
250958362fume		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
25106dd203hrs			sizeof(ntopbuf)),
252958362fume		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
2538c2ccb5shin		return;
2548c2ccb5shin	}
2558c2ccb5shin
2568c2ccb5shin	if (*hlimp != 255) {
25730892caume		warnmsg(LOG_INFO, __func__,
258958362fume		    "invalid RA with hop limit(%d) from %s on %s",
259958362fume		    *hlimp,
260958362fume		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
26106dd203hrs			sizeof(ntopbuf)),
262958362fume		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
2638c2ccb5shin		return;
2648c2ccb5shin	}
2658c2ccb5shin
2668c2ccb5shin	if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
26730892caume		warnmsg(LOG_INFO, __func__,
268958362fume		    "invalid RA with non link-local source from %s on %s",
269958362fume		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
27006dd203hrs			sizeof(ntopbuf)),
271958362fume		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
2728c2ccb5shin		return;
2738c2ccb5shin	}
2748c2ccb5shin
2758c2ccb5shin	/* xxx: more validation? */
2768c2ccb5shin
2778c2ccb5shin	if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) {
278e3e51c1markj		warnmsg(LOG_DEBUG, __func__,
279958362fume		    "received RA from %s on an unexpected IF(%s)",
280958362fume		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
28106dd203hrs			sizeof(ntopbuf)),
282958362fume		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
2838c2ccb5shin		return;
2848c2ccb5shin	}
2858c2ccb5shin
28652b4a69ume	warnmsg(LOG_DEBUG, __func__,
287958362fume	    "received RA from %s on %s, state is %d",
28806dd203hrs	    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)),
289958362fume	    ifi->ifname, ifi->state);
2908c2ccb5shin
29102ba92cume	nd_ra = (struct nd_router_advert *)icp;
29202ba92cume
29302ba92cume	/*
29402ba92cume	 * Process the "O bit."
29502ba92cume	 * If the value of OtherConfigFlag changes from FALSE to TRUE, the
29602ba92cume	 * host should invoke the stateful autoconfiguration protocol,
29702ba92cume	 * requesting information.
29802ba92cume	 * [RFC 2462 Section 5.5.3]
29902ba92cume	 */
30002ba92cume	if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) &&
30102ba92cume	    !ifi->otherconfig) {
30202ba92cume		warnmsg(LOG_DEBUG, __func__,
30302ba92cume		    "OtherConfigFlag on %s is turned on", ifi->ifname);
30402ba92cume		ifi->otherconfig = 1;
30510df0afhrs		CALL_SCRIPT(OTHER, NULL);
30602ba92cume	}
30713c1bcfhrs	clock_gettime(CLOCK_MONOTONIC_FAST, &now);
3081eefc7ahrs	newent_rai = 0;
3091eefc7ahrs	rai = find_rainfo(ifi, &from);
3101eefc7ahrs	if (rai == NULL) {
3111eefc7ahrs		ELM_MALLOC(rai, exit(1));
3121eefc7ahrs		rai->rai_ifinfo = ifi;
3131eefc7ahrs		TAILQ_INIT(&rai->rai_ra_opt);
31406dd203hrs		rai->rai_saddr.sin6_family = AF_INET6;
31506dd203hrs		rai->rai_saddr.sin6_len = sizeof(rai->rai_saddr);
3161eefc7ahrs		memcpy(&rai->rai_saddr.sin6_addr, &from.sin6_addr,
3171eefc7ahrs		    sizeof(rai->rai_saddr.sin6_addr));
3181eefc7ahrs		newent_rai = 1;
3191eefc7ahrs	}
32010df0afhrs
32110df0afhrs#define	RA_OPT_NEXT_HDR(x)	(struct nd_opt_hdr *)((char *)x + \
32210df0afhrs				(((struct nd_opt_hdr *)x)->nd_opt_len * 8))
32310df0afhrs	/* Process RA options. */
32410df0afhrs	warnmsg(LOG_DEBUG, __func__, "Processing RA");
32510df0afhrs	raoptp = (char *)icp + sizeof(struct nd_router_advert);
32610df0afhrs	while (raoptp < (char *)icp + msglen) {
32710df0afhrs		ndo = (struct nd_opt_hdr *)raoptp;
32810df0afhrs		warnmsg(LOG_DEBUG, __func__, "ndo = %p", raoptp);
32910df0afhrs		warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_type = %d",
33010df0afhrs		    ndo->nd_opt_type);
33110df0afhrs		warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_len = %d",
33210df0afhrs		    ndo->nd_opt_len);
33310df0afhrs
33410df0afhrs		switch (ndo->nd_opt_type) {
33510df0afhrs		case ND_OPT_RDNSS:
33610df0afhrs			rdnss = (struct nd_opt_rdnss *)raoptp;
33710df0afhrs
33810df0afhrs			/* Optlen sanity check (Section 5.3.1 in RFC 6106) */
33910df0afhrs			if (rdnss->nd_opt_rdnss_len < 3) {
34010df0afhrs				warnmsg(LOG_INFO, __func__,
34110df0afhrs		    			"too short RDNSS option"
34210df0afhrs					"in RA from %s was ignored.",
34310df0afhrs					inet_ntop(AF_INET6, &from.sin6_addr,
34406dd203hrs					    ntopbuf, sizeof(ntopbuf)));
34510df0afhrs				break;
34610df0afhrs			}
34710df0afhrs
348a8298dchrs			addr = (struct in6_addr *)(void *)(raoptp + sizeof(*rdnss));
34910df0afhrs			while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) {
35010df0afhrs				if (inet_ntop(AF_INET6, addr, ntopbuf,
35106dd203hrs					sizeof(ntopbuf)) == NULL) {
35210df0afhrs					warnmsg(LOG_INFO, __func__,
35310df0afhrs		    			    "an invalid address in RDNSS option"
35410df0afhrs					    " in RA from %s was ignored.",
35510df0afhrs					    inet_ntop(AF_INET6, &from.sin6_addr,
35606dd203hrs						ntopbuf, sizeof(ntopbuf)));
35710df0afhrs					addr++;
35810df0afhrs					continue;
35910df0afhrs				}
36010df0afhrs				if (IN6_IS_ADDR_LINKLOCAL(addr))
36110df0afhrs					/* XXX: % has to be escaped here */
36210df0afhrs					l = snprintf(nsbuf, sizeof(nsbuf),
36310df0afhrs					    "%s%c%s", ntopbuf,
36410df0afhrs					    SCOPE_DELIMITER,
36510df0afhrs					    ifi->ifname);
36610df0afhrs				else
36710df0afhrs					l = snprintf(nsbuf, sizeof(nsbuf),
36810df0afhrs					    "%s", ntopbuf);
36910df0afhrs				if (l < 0 || (size_t)l >= sizeof(nsbuf)) {
37010df0afhrs					warnmsg(LOG_ERR, __func__,
37110df0afhrs					    "address copying error in "
37210df0afhrs					    "RDNSS option: %d.", l);
37310df0afhrs					addr++;
37410df0afhrs					continue;
37510df0afhrs				}
37610df0afhrs				warnmsg(LOG_DEBUG, __func__, "nsbuf = %s",
37710df0afhrs				    nsbuf);
37810df0afhrs
3791eefc7ahrs				newent_rao = 0;
3801eefc7ahrs				rao = find_raopt(rai, ndo->nd_opt_type, nsbuf,
3811eefc7ahrs				    strlen(nsbuf));
3821eefc7ahrs				if (rao == NULL) {
3831eefc7ahrs					ELM_MALLOC(rao, break);
3841eefc7ahrs					rao->rao_type = ndo->nd_opt_type;
3851eefc7ahrs					rao->rao_len = strlen(nsbuf);
3861eefc7ahrs					rao->rao_msg = strdup(nsbuf);
3871eefc7ahrs					if (rao->rao_msg == NULL) {
3881eefc7ahrs						warnmsg(LOG_ERR, __func__,
3891eefc7ahrs						    "strdup failed: %s",
3901eefc7ahrs						    strerror(errno));
3911eefc7ahrs						free(rao);
3921eefc7ahrs						addr++;
3931eefc7ahrs						continue;
3941eefc7ahrs					}
3951eefc7ahrs					newent_rao = 1;
39610df0afhrs				}
39710df0afhrs				/* Set expiration timer */
3981eefc7ahrs				memset(&rao->rao_expire, 0,
3991eefc7ahrs				    sizeof(rao->rao_expire));
40010df0afhrs				memset(&lifetime, 0, sizeof(lifetime));
4011eefc7ahrs				lifetime.tv_sec =
4021eefc7ahrs				    ntohl(rdnss->nd_opt_rdnss_lifetime);
40313c1bcfhrs				TS_ADD(&now, &lifetime, &rao->rao_expire);
40410df0afhrs
4051eefc7ahrs				if (newent_rao)
4061eefc7ahrs					TAILQ_INSERT_TAIL(&rai->rai_ra_opt,
4071eefc7ahrs					    rao, rao_next);
40810df0afhrs				addr++;
40910df0afhrs			}
41010df0afhrs			break;
41110df0afhrs		case ND_OPT_DNSSL:
41210df0afhrs			dnssl = (struct nd_opt_dnssl *)raoptp;
41310df0afhrs
41410df0afhrs			/* Optlen sanity check (Section 5.3.1 in RFC 6106) */
41510df0afhrs			if (dnssl->nd_opt_dnssl_len < 2) {
41610df0afhrs				warnmsg(LOG_INFO, __func__,
41710df0afhrs		    			"too short DNSSL option"
41810df0afhrs					"in RA from %s was ignored.",
41910df0afhrs					inet_ntop(AF_INET6, &from.sin6_addr,
42006dd203hrs					    ntopbuf, sizeof(ntopbuf)));
42110df0afhrs				break;
42210df0afhrs			}
42310df0afhrs
42410df0afhrs			/*
42510df0afhrs			 * Ensure NUL-termination in DNSSL in case of
42610df0afhrs			 * malformed field.
42710df0afhrs			 */
42810df0afhrs			p = (char *)RA_OPT_NEXT_HDR(raoptp);
42910df0afhrs			*(p - 1) = '\0';
43010df0afhrs
43110df0afhrs			p = raoptp + sizeof(*dnssl);
43210df0afhrs			while (1 < (len = dname_labeldec(dname, sizeof(dname),
43310df0afhrs			    p))) {
43410df0afhrs				/* length == 1 means empty string */
43510df0afhrs				warnmsg(LOG_DEBUG, __func__, "dname = %s",
43610df0afhrs				    dname);
43710df0afhrs
4381eefc7ahrs				newent_rao = 0;
4391eefc7ahrs				rao = find_raopt(rai, ndo->nd_opt_type, dname,
4401eefc7ahrs				    strlen(dname));
4411eefc7ahrs				if (rao == NULL) {
4421eefc7ahrs					ELM_MALLOC(rao, break);
4431eefc7ahrs					rao->rao_type = ndo->nd_opt_type;
4441eefc7ahrs					rao->rao_len = strlen(dname);
4451eefc7ahrs					rao->rao_msg = strdup(dname);
4461eefc7ahrs					if (rao->rao_msg == NULL) {
4471eefc7ahrs						warnmsg(LOG_ERR, __func__,
4481eefc7ahrs						    "strdup failed: %s",
4491eefc7ahrs						    strerror(errno));
4501eefc7ahrs						free(rao);
4511eefc7ahrs						addr++;
4521eefc7ahrs						continue;
4531eefc7ahrs					}
4541eefc7ahrs					newent_rao = 1;
45510df0afhrs				}
45610df0afhrs				/* Set expiration timer */
4571eefc7ahrs				memset(&rao->rao_expire, 0,
4581eefc7ahrs				    sizeof(rao->rao_expire));
45910df0afhrs				memset(&lifetime, 0, sizeof(lifetime));
4601eefc7ahrs				lifetime.tv_sec =
4611eefc7ahrs				    ntohl(dnssl->nd_opt_dnssl_lifetime);
46213c1bcfhrs				TS_ADD(&now, &lifetime, &rao->rao_expire);
46310df0afhrs
4641eefc7ahrs				if (newent_rao)
4651eefc7ahrs					TAILQ_INSERT_TAIL(&rai->rai_ra_opt,
4661eefc7ahrs					    rao, rao_next);
46710df0afhrs				p += len;
46810df0afhrs			}
46910df0afhrs			break;
47010df0afhrs		default:
47110df0afhrs			/* nothing to do for other options */
47210df0afhrs			break;
473