xref: /freebsd/usr.sbin/rtsold/rtsol.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
18e7409edSHajimu UMEMOTO /*	$KAME: rtsol.c,v 1.27 2003/10/05 00:09:36 itojun Exp $	*/
2804c83d4SKris Kennaway 
38a16b7a1SPedro F. Giffuni /*-
48a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
58a16b7a1SPedro F. Giffuni  *
67d56d374SYoshinobu Inoue  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7db82af41SHiroki Sato  * Copyright (C) 2011 Hiroki Sato
87d56d374SYoshinobu Inoue  * All rights reserved.
97d56d374SYoshinobu Inoue  *
107d56d374SYoshinobu Inoue  * Redistribution and use in source and binary forms, with or without
117d56d374SYoshinobu Inoue  * modification, are permitted provided that the following conditions
127d56d374SYoshinobu Inoue  * are met:
137d56d374SYoshinobu Inoue  * 1. Redistributions of source code must retain the above copyright
147d56d374SYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer.
157d56d374SYoshinobu Inoue  * 2. Redistributions in binary form must reproduce the above copyright
167d56d374SYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer in the
177d56d374SYoshinobu Inoue  *    documentation and/or other materials provided with the distribution.
187d56d374SYoshinobu Inoue  * 3. Neither the name of the project nor the names of its contributors
197d56d374SYoshinobu Inoue  *    may be used to endorse or promote products derived from this software
207d56d374SYoshinobu Inoue  *    without specific prior written permission.
217d56d374SYoshinobu Inoue  *
227d56d374SYoshinobu Inoue  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
237d56d374SYoshinobu Inoue  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
247d56d374SYoshinobu Inoue  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
257d56d374SYoshinobu Inoue  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
267d56d374SYoshinobu Inoue  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
277d56d374SYoshinobu Inoue  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
287d56d374SYoshinobu Inoue  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
297d56d374SYoshinobu Inoue  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
307d56d374SYoshinobu Inoue  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
317d56d374SYoshinobu Inoue  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
327d56d374SYoshinobu Inoue  * SUCH DAMAGE.
337d56d374SYoshinobu Inoue  */
347d56d374SYoshinobu Inoue 
357d56d374SYoshinobu Inoue #include <sys/param.h>
3604e9edb5SMark Johnston #include <sys/capsicum.h>
37804c83d4SKris Kennaway #include <sys/queue.h>
3804e9edb5SMark Johnston #include <sys/socket.h>
3986b032afSHajimu UMEMOTO #include <sys/stat.h>
4004e9edb5SMark Johnston #include <sys/uio.h>
4104e9edb5SMark Johnston #include <sys/wait.h>
427d56d374SYoshinobu Inoue 
437d56d374SYoshinobu Inoue #include <net/if.h>
447d56d374SYoshinobu Inoue #include <net/route.h>
457d56d374SYoshinobu Inoue #include <net/if_dl.h>
467d56d374SYoshinobu Inoue 
47db82af41SHiroki Sato #define	__BSD_VISIBLE	1	/* IN6ADDR_LINKLOCAL_ALLROUTERS_INIT */
487d56d374SYoshinobu Inoue #include <netinet/in.h>
49db82af41SHiroki Sato #undef 	__BSD_VISIBLE
507d56d374SYoshinobu Inoue #include <netinet/ip6.h>
517d56d374SYoshinobu Inoue #include <netinet6/ip6_var.h>
527d56d374SYoshinobu Inoue #include <netinet/icmp6.h>
537d56d374SYoshinobu Inoue 
547d56d374SYoshinobu Inoue #include <arpa/inet.h>
557d56d374SYoshinobu Inoue 
5604e9edb5SMark Johnston #include <capsicum_helpers.h>
57db82af41SHiroki Sato #include <netdb.h>
587d56d374SYoshinobu Inoue #include <time.h>
599a364ca3SHajimu UMEMOTO #include <fcntl.h>
607d56d374SYoshinobu Inoue #include <unistd.h>
617d56d374SYoshinobu Inoue #include <stdio.h>
627d26db17SHiroki Sato #include <time.h>
637d56d374SYoshinobu Inoue #include <err.h>
647d56d374SYoshinobu Inoue #include <errno.h>
657d56d374SYoshinobu Inoue #include <string.h>
667d56d374SYoshinobu Inoue #include <stdlib.h>
677d56d374SYoshinobu Inoue #include <syslog.h>
687d56d374SYoshinobu Inoue #include "rtsold.h"
697d56d374SYoshinobu Inoue 
70489e04d0SHiroki Sato static char rsid[IFNAMSIZ + 1 + sizeof(DNSINFO_ORIGIN_LABEL) + 1 + NI_MAXHOST];
7104e9edb5SMark Johnston struct ifinfo_head_t ifinfo_head = TAILQ_HEAD_INITIALIZER(ifinfo_head);
727d56d374SYoshinobu Inoue 
7304e9edb5SMark Johnston static void call_script(const char *const *, struct script_msg_head_t *);
74db82af41SHiroki Sato static size_t dname_labeldec(char *, size_t, const char *);
7506056832SHiroki Sato static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t);
76489e04d0SHiroki Sato static int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *,
77489e04d0SHiroki Sato     struct script_msg_head_t *, struct script_msg_head_t *);
78489e04d0SHiroki Sato static char *make_rsid(const char *, const char *, struct rainfo *);
7986b032afSHajimu UMEMOTO 
8002508a3dSFranco Fitchner #define	_ARGS_MANAGED	managedconf_script, ifi->ifname, rasender
8102508a3dSFranco Fitchner #define	_ARGS_OTHER	otherconf_script, ifi->ifname, rasender
82*476babaeSKristof Provost #define	_ARGS_ALWAYS	alwaysconf_script, ifi->ifname, rasender
83489e04d0SHiroki Sato #define	_ARGS_RESADD	resolvconf_script, "-a", rsid
84489e04d0SHiroki Sato #define	_ARGS_RESDEL	resolvconf_script, "-d", rsid
85db82af41SHiroki Sato 
862b2135b8SMark Johnston #define	CALL_SCRIPT(name, sm_head) do {				\
87db82af41SHiroki Sato 	const char *const sarg[] = { _ARGS_##name, NULL };	\
8804e9edb5SMark Johnston 	call_script(sarg, sm_head);				\
89db82af41SHiroki Sato } while (0)
90db82af41SHiroki Sato 
912b2135b8SMark Johnston #define	ELM_MALLOC(p, error_action) do {			\
92db82af41SHiroki Sato 	p = malloc(sizeof(*p));					\
93db82af41SHiroki Sato 	if (p == NULL) {					\
94db82af41SHiroki Sato 		warnmsg(LOG_ERR, __func__, "malloc failed: %s", \
95db82af41SHiroki Sato 		    strerror(errno));				\
96db82af41SHiroki Sato 		error_action;					\
97db82af41SHiroki Sato 	}							\
98db82af41SHiroki Sato 	memset(p, 0, sizeof(*p));				\
99db82af41SHiroki Sato } while (0)
100db82af41SHiroki Sato 
1017d56d374SYoshinobu Inoue int
recvsockopen(void)10204e9edb5SMark Johnston recvsockopen(void)
1037d56d374SYoshinobu Inoue {
1049a364ca3SHajimu UMEMOTO 	struct icmp6_filter filt;
10504e9edb5SMark Johnston 	cap_rights_t rights;
10604e9edb5SMark Johnston 	int on, sock;
1077d56d374SYoshinobu Inoue 
10804e9edb5SMark Johnston 	if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
1091533bed0SHajimu UMEMOTO 		warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno));
11004e9edb5SMark Johnston 		goto fail;
1117d56d374SYoshinobu Inoue 	}
1127d56d374SYoshinobu Inoue 
11304e9edb5SMark Johnston 	/* Provide info about the receiving interface. */
1147d56d374SYoshinobu Inoue 	on = 1;
11504e9edb5SMark Johnston 	if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
116259df286SKris Kennaway 	    sizeof(on)) < 0) {
11704e9edb5SMark Johnston 		warnmsg(LOG_ERR, __func__, "setsockopt(IPV6_RECVPKTINFO): %s",
118259df286SKris Kennaway 		    strerror(errno));
11904e9edb5SMark Johnston 		goto fail;
120259df286SKris Kennaway 	}
1217d56d374SYoshinobu Inoue 
12204e9edb5SMark Johnston 	/* Include the hop limit from the received header. */
123db82af41SHiroki Sato 	on = 1;
12404e9edb5SMark Johnston 	if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
125259df286SKris Kennaway 	    sizeof(on)) < 0) {
12604e9edb5SMark Johnston 		warnmsg(LOG_ERR, __func__, "setsockopt(IPV6_RECVHOPLIMIT): %s",
127259df286SKris Kennaway 		    strerror(errno));
12804e9edb5SMark Johnston 		goto fail;
129259df286SKris Kennaway 	}
1307d56d374SYoshinobu Inoue 
13104e9edb5SMark Johnston 	/* Filter out everything except for Router Advertisements. */
1327d56d374SYoshinobu Inoue 	ICMP6_FILTER_SETBLOCKALL(&filt);
1337d56d374SYoshinobu Inoue 	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
13404e9edb5SMark Johnston 	if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
1357d56d374SYoshinobu Inoue 	    sizeof(filt)) == -1) {
1361533bed0SHajimu UMEMOTO 		warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s",
1377d56d374SYoshinobu Inoue 		    strerror(errno));
13804e9edb5SMark Johnston 		goto fail;
13904e9edb5SMark Johnston 	}
14004e9edb5SMark Johnston 
14104e9edb5SMark Johnston 	cap_rights_init(&rights, CAP_EVENT, CAP_RECV);
14204e9edb5SMark Johnston 	if (caph_rights_limit(sock, &rights) < 0) {
14304e9edb5SMark Johnston 		warnmsg(LOG_ERR, __func__, "caph_rights_limit(): %s",
14404e9edb5SMark Johnston 		    strerror(errno));
14504e9edb5SMark Johnston 		goto fail;
14604e9edb5SMark Johnston 	}
14704e9edb5SMark Johnston 
14804e9edb5SMark Johnston 	return (sock);
14904e9edb5SMark Johnston 
15004e9edb5SMark Johnston fail:
15104e9edb5SMark Johnston 	if (sock >= 0)
15204e9edb5SMark Johnston 		(void)close(sock);
1537d56d374SYoshinobu Inoue 	return (-1);
1547d56d374SYoshinobu Inoue }
1557d56d374SYoshinobu Inoue 
1567d56d374SYoshinobu Inoue void
rtsol_input(int sock)15704e9edb5SMark Johnston rtsol_input(int sock)
1587d56d374SYoshinobu Inoue {
15904e9edb5SMark Johnston 	uint8_t cmsg[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
16004e9edb5SMark Johnston 	    CMSG_SPACE(sizeof(int))];
16104e9edb5SMark Johnston 	struct iovec iov;
16204e9edb5SMark Johnston 	struct msghdr hdr;
16304e9edb5SMark Johnston 	struct sockaddr_in6 from;
16404e9edb5SMark Johnston 	char answer[1500], ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
165db82af41SHiroki Sato 	int l, ifindex = 0, *hlimp = NULL;
166db82af41SHiroki Sato 	ssize_t msglen;
1677d56d374SYoshinobu Inoue 	struct in6_pktinfo *pi = NULL;
1687d56d374SYoshinobu Inoue 	struct ifinfo *ifi = NULL;
169db82af41SHiroki Sato 	struct ra_opt *rao = NULL;
1709a364ca3SHajimu UMEMOTO 	struct icmp6_hdr *icp;
17186b032afSHajimu UMEMOTO 	struct nd_router_advert *nd_ra;
1729a364ca3SHajimu UMEMOTO 	struct cmsghdr *cm;
17306056832SHiroki Sato 	struct rainfo *rai;
17404e9edb5SMark Johnston 	char *p, *raoptp;
175db82af41SHiroki Sato 	struct in6_addr *addr;
176db82af41SHiroki Sato 	struct nd_opt_hdr *ndo;
177db82af41SHiroki Sato 	struct nd_opt_rdnss *rdnss;
178db82af41SHiroki Sato 	struct nd_opt_dnssl *dnssl;
179db82af41SHiroki Sato 	size_t len;
180db82af41SHiroki Sato 	char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1];
181db82af41SHiroki Sato 	char dname[NI_MAXHOST];
18204e9edb5SMark Johnston 	struct timespec lifetime, now;
18304e9edb5SMark Johnston 	int newent_rai, newent_rao;
1847d56d374SYoshinobu Inoue 
18504e9edb5SMark Johnston 	memset(&hdr, 0, sizeof(hdr));
18604e9edb5SMark Johnston 	hdr.msg_iov = &iov;
18704e9edb5SMark Johnston 	hdr.msg_iovlen = 1;
18804e9edb5SMark Johnston 	hdr.msg_name = &from;
18904e9edb5SMark Johnston 	hdr.msg_namelen = sizeof(from);
19004e9edb5SMark Johnston 	hdr.msg_control = cmsg;
19104e9edb5SMark Johnston 	hdr.msg_controllen = sizeof(cmsg);
19204e9edb5SMark Johnston 
19304e9edb5SMark Johnston 	iov.iov_base = (caddr_t)answer;
19404e9edb5SMark Johnston 	iov.iov_len = sizeof(answer);
19504e9edb5SMark Johnston 
19604e9edb5SMark Johnston 	if ((msglen = recvmsg(sock, &hdr, 0)) < 0) {
1971533bed0SHajimu UMEMOTO 		warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno));
1987d56d374SYoshinobu Inoue 		return;
1997d56d374SYoshinobu Inoue 	}
2007d56d374SYoshinobu Inoue 
20104e9edb5SMark Johnston 	/* Extract control message info. */
20204e9edb5SMark Johnston 	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&hdr); cm != NULL;
20304e9edb5SMark Johnston 	    cm = (struct cmsghdr *)CMSG_NXTHDR(&hdr, cm)) {
2047d56d374SYoshinobu Inoue 		if (cm->cmsg_level == IPPROTO_IPV6 &&
2057d56d374SYoshinobu Inoue 		    cm->cmsg_type == IPV6_PKTINFO &&
2067d56d374SYoshinobu Inoue 		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
207a678ca23SHiroki Sato 			pi = (struct in6_pktinfo *)(void *)(CMSG_DATA(cm));
2087d56d374SYoshinobu Inoue 			ifindex = pi->ipi6_ifindex;
2097d56d374SYoshinobu Inoue 		}
2107d56d374SYoshinobu Inoue 		if (cm->cmsg_level == IPPROTO_IPV6 &&
2117d56d374SYoshinobu Inoue 		    cm->cmsg_type == IPV6_HOPLIMIT &&
2127d56d374SYoshinobu Inoue 		    cm->cmsg_len == CMSG_LEN(sizeof(int)))
213a678ca23SHiroki Sato 			hlimp = (int *)(void *)CMSG_DATA(cm);
2147d56d374SYoshinobu Inoue 	}
2157d56d374SYoshinobu Inoue 
2167d56d374SYoshinobu Inoue 	if (ifindex == 0) {
217fa19f9beSHajimu UMEMOTO 		warnmsg(LOG_ERR, __func__,
218fa19f9beSHajimu UMEMOTO 		    "failed to get receiving interface");
2197d56d374SYoshinobu Inoue 		return;
2207d56d374SYoshinobu Inoue 	}
2217d56d374SYoshinobu Inoue 	if (hlimp == NULL) {
222fa19f9beSHajimu UMEMOTO 		warnmsg(LOG_ERR, __func__,
223fa19f9beSHajimu UMEMOTO 		    "failed to get receiving hop limit");
2247d56d374SYoshinobu Inoue 		return;
2257d56d374SYoshinobu Inoue 	}
2267d56d374SYoshinobu Inoue 
227db82af41SHiroki Sato 	if ((size_t)msglen < sizeof(struct nd_router_advert)) {
2287bc56ec4SHajimu UMEMOTO 		warnmsg(LOG_INFO, __func__,
229db82af41SHiroki Sato 		    "packet size(%zd) is too short", msglen);
2307d56d374SYoshinobu Inoue 		return;
2317d56d374SYoshinobu Inoue 	}
2327d56d374SYoshinobu Inoue 
23304e9edb5SMark Johnston 	icp = (struct icmp6_hdr *)iov.iov_base;
2347d56d374SYoshinobu Inoue 	if (icp->icmp6_type != ND_ROUTER_ADVERT) {
2357bc56ec4SHajimu UMEMOTO 		/*
2367bc56ec4SHajimu UMEMOTO 		 * this should not happen because we configured a filter
2377bc56ec4SHajimu UMEMOTO 		 * that only passes RAs on the receiving socket.
2387bc56ec4SHajimu UMEMOTO 		 */
2391533bed0SHajimu UMEMOTO 		warnmsg(LOG_ERR, __func__,
2407d56d374SYoshinobu Inoue 		    "invalid icmp type(%d) from %s on %s", icp->icmp6_type,
2417d56d374SYoshinobu Inoue 		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
242489e04d0SHiroki Sato 			sizeof(ntopbuf)),
2437d56d374SYoshinobu Inoue 		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
2447d56d374SYoshinobu Inoue 		return;
2457d56d374SYoshinobu Inoue 	}
2467d56d374SYoshinobu Inoue 
2477d56d374SYoshinobu Inoue 	if (icp->icmp6_code != 0) {
2487bc56ec4SHajimu UMEMOTO 		warnmsg(LOG_INFO, __func__,
2497d56d374SYoshinobu Inoue 		    "invalid icmp code(%d) from %s on %s", icp->icmp6_code,
2507d56d374SYoshinobu Inoue 		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
251489e04d0SHiroki Sato 			sizeof(ntopbuf)),
2527d56d374SYoshinobu Inoue 		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
2537d56d374SYoshinobu Inoue 		return;
2547d56d374SYoshinobu Inoue 	}
2557d56d374SYoshinobu Inoue 
2567d56d374SYoshinobu Inoue 	if (*hlimp != 255) {
2577bc56ec4SHajimu UMEMOTO 		warnmsg(LOG_INFO, __func__,
2587d56d374SYoshinobu Inoue 		    "invalid RA with hop limit(%d) from %s on %s",
2597d56d374SYoshinobu Inoue 		    *hlimp,
2607d56d374SYoshinobu Inoue 		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
261489e04d0SHiroki Sato 			sizeof(ntopbuf)),
2627d56d374SYoshinobu Inoue 		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
2637d56d374SYoshinobu Inoue 		return;
2647d56d374SYoshinobu Inoue 	}
2657d56d374SYoshinobu Inoue 
2667d56d374SYoshinobu Inoue 	if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
2677bc56ec4SHajimu UMEMOTO 		warnmsg(LOG_INFO, __func__,
2687d56d374SYoshinobu Inoue 		    "invalid RA with non link-local source from %s on %s",
2697d56d374SYoshinobu Inoue 		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
270489e04d0SHiroki Sato 			sizeof(ntopbuf)),
2717d56d374SYoshinobu Inoue 		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
2727d56d374SYoshinobu Inoue 		return;
2737d56d374SYoshinobu Inoue 	}
2747d56d374SYoshinobu Inoue 
2757d56d374SYoshinobu Inoue 	/* xxx: more validation? */
2767d56d374SYoshinobu Inoue 
2777d56d374SYoshinobu Inoue 	if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) {
2785c3f6d49SMark Johnston 		warnmsg(LOG_DEBUG, __func__,
279fa19f9beSHajimu UMEMOTO 		    "received RA from %s on an unexpected IF(%s)",
2807d56d374SYoshinobu Inoue 		    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
281489e04d0SHiroki Sato 			sizeof(ntopbuf)),
2827d56d374SYoshinobu Inoue 		    if_indextoname(pi->ipi6_ifindex, ifnamebuf));
2837d56d374SYoshinobu Inoue 		return;
2847d56d374SYoshinobu Inoue 	}
2857d56d374SYoshinobu Inoue 
2861533bed0SHajimu UMEMOTO 	warnmsg(LOG_DEBUG, __func__,
2877d56d374SYoshinobu Inoue 	    "received RA from %s on %s, state is %d",
288489e04d0SHiroki Sato 	    inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)),
2897d56d374SYoshinobu Inoue 	    ifi->ifname, ifi->state);
2907d56d374SYoshinobu Inoue 
29186b032afSHajimu UMEMOTO 	nd_ra = (struct nd_router_advert *)icp;
29286b032afSHajimu UMEMOTO 
29386b032afSHajimu UMEMOTO 	/*
29460e7f669SBjoern A. Zeeb 	 * Process the "M bit."
29560e7f669SBjoern A. Zeeb 	 * If the value of ManagedConfigFlag changes from FALSE to TRUE, the
29660e7f669SBjoern A. Zeeb 	 * host should invoke the stateful autoconfiguration protocol,
29760e7f669SBjoern A. Zeeb 	 * requesting information.
29860e7f669SBjoern A. Zeeb 	 * [RFC 4861 Section 4.2]
29960e7f669SBjoern A. Zeeb 	 * XXX ??? [draft-ietf-v6ops-dhcpv6-slaac-problem-07]
30060e7f669SBjoern A. Zeeb 	 */
30160e7f669SBjoern A. Zeeb 	if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_MANAGED) &&
30260e7f669SBjoern A. Zeeb 	    !ifi->managedconfig) {
30302508a3dSFranco Fitchner 		const char *rasender = inet_ntop(AF_INET6, &from.sin6_addr,
30402508a3dSFranco Fitchner 		    ntopbuf, sizeof(ntopbuf));
30560e7f669SBjoern A. Zeeb 		warnmsg(LOG_DEBUG, __func__,
30660e7f669SBjoern A. Zeeb 		    "ManagedConfigFlag on %s is turned on", ifi->ifname);
30760e7f669SBjoern A. Zeeb 		ifi->managedconfig = 1;
30860e7f669SBjoern A. Zeeb 		CALL_SCRIPT(MANAGED, NULL);
30960e7f669SBjoern A. Zeeb 	}
31060e7f669SBjoern A. Zeeb 
31160e7f669SBjoern A. Zeeb 	/*
31286b032afSHajimu UMEMOTO 	 * Process the "O bit."
31386b032afSHajimu UMEMOTO 	 * If the value of OtherConfigFlag changes from FALSE to TRUE, the
31486b032afSHajimu UMEMOTO 	 * host should invoke the stateful autoconfiguration protocol,
31560e7f669SBjoern A. Zeeb 	 * requesting information unless the "M bit" was set as well in
31660e7f669SBjoern A. Zeeb 	 * which case the "O bit" is redundant.
31760e7f669SBjoern A. Zeeb 	 * [RFC 4861 Section 4.2]
31886b032afSHajimu UMEMOTO 	 */
31986b032afSHajimu UMEMOTO 	if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) &&
32086b032afSHajimu UMEMOTO 	    !ifi->otherconfig) {
32102508a3dSFranco Fitchner 		const char *rasender = inet_ntop(AF_INET6, &from.sin6_addr,
32202508a3dSFranco Fitchner 		    ntopbuf, sizeof(ntopbuf));
32386b032afSHajimu UMEMOTO 		warnmsg(LOG_DEBUG, __func__,
32486b032afSHajimu UMEMOTO 		    "OtherConfigFlag on %s is turned on", ifi->ifname);
32586b032afSHajimu UMEMOTO 		ifi->otherconfig = 1;
32660e7f669SBjoern A. Zeeb 		if (!ifi->managedconfig)
327db82af41SHiroki Sato 			CALL_SCRIPT(OTHER, NULL);
32886b032afSHajimu UMEMOTO 	}
329*476babaeSKristof Provost 
330*476babaeSKristof Provost 	/*
331*476babaeSKristof Provost 	 * "Always" script.
332*476babaeSKristof Provost 	 */
333*476babaeSKristof Provost 	if (!ifi->alwaysconfig) {
334*476babaeSKristof Provost 		const char *rasender = inet_ntop(AF_INET6, &from.sin6_addr,
335*476babaeSKristof Provost 		    ntopbuf, sizeof(ntopbuf));
336*476babaeSKristof Provost 		ifi->alwaysconfig = 1;
337*476babaeSKristof Provost 		CALL_SCRIPT(ALWAYS, NULL);
338*476babaeSKristof Provost 	}
339*476babaeSKristof Provost 
3407d26db17SHiroki Sato 	clock_gettime(CLOCK_MONOTONIC_FAST, &now);
34106056832SHiroki Sato 	newent_rai = 0;
34206056832SHiroki Sato 	rai = find_rainfo(ifi, &from);
34306056832SHiroki Sato 	if (rai == NULL) {
34406056832SHiroki Sato 		ELM_MALLOC(rai, exit(1));
34506056832SHiroki Sato 		rai->rai_ifinfo = ifi;
34606056832SHiroki Sato 		TAILQ_INIT(&rai->rai_ra_opt);
347489e04d0SHiroki Sato 		rai->rai_saddr.sin6_family = AF_INET6;
348489e04d0SHiroki Sato 		rai->rai_saddr.sin6_len = sizeof(rai->rai_saddr);
34906056832SHiroki Sato 		memcpy(&rai->rai_saddr.sin6_addr, &from.sin6_addr,
35006056832SHiroki Sato 		    sizeof(rai->rai_saddr.sin6_addr));
35106056832SHiroki Sato 		newent_rai = 1;
352db82af41SHiroki Sato 	}
353db82af41SHiroki Sato 
354f2fb8af1SMark Johnston #define	RA_OPT_NEXT_HDR(x)	(struct nd_opt_hdr *)((char *)(x) + \
355f2fb8af1SMark Johnston 				(((struct nd_opt_hdr *)(x))->nd_opt_len * 8))
356db82af41SHiroki Sato 	/* Process RA options. */
357db82af41SHiroki Sato 	warnmsg(LOG_DEBUG, __func__, "Processing RA");
358db82af41SHiroki Sato 	raoptp = (char *)icp + sizeof(struct nd_router_advert);
359db82af41SHiroki Sato 	while (raoptp < (char *)icp + msglen) {
360db82af41SHiroki Sato 		ndo = (struct nd_opt_hdr *)raoptp;
361db82af41SHiroki Sato 		warnmsg(LOG_DEBUG, __func__, "ndo = %p", raoptp);
362db82af41SHiroki Sato 		warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_type = %d",
363db82af41SHiroki Sato 		    ndo->nd_opt_type);
364db82af41SHiroki Sato 		warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_len = %d",
365db82af41SHiroki Sato 		    ndo->nd_opt_len);
366db82af41SHiroki Sato 
367f2fb8af1SMark Johnston 		if (ndo->nd_opt_len == 0) {
368f2fb8af1SMark Johnston 			warnmsg(LOG_INFO, __func__, "invalid option length 0.");
369f2fb8af1SMark Johnston 			break;
370f2fb8af1SMark Johnston 		}
371f2fb8af1SMark Johnston 		if ((char *)RA_OPT_NEXT_HDR(raoptp) > (char *)icp + msglen) {
372f2fb8af1SMark Johnston 			warnmsg(LOG_INFO, __func__, "option length overflow.");
373f2fb8af1SMark Johnston 			break;
374f2fb8af1SMark Johnston 		}
375f2fb8af1SMark Johnston 
376db82af41SHiroki Sato 		switch (ndo->nd_opt_type) {
377db82af41SHiroki Sato 		case ND_OPT_RDNSS:
378db82af41SHiroki Sato 			rdnss = (struct nd_opt_rdnss *)raoptp;
379db82af41SHiroki Sato 
3801af332a7SMark Johnston 			/*
3811af332a7SMark Johnston 			 * The option header is 8 bytes long and each address
3821af332a7SMark Johnston 			 * occupies 16 bytes, so the option length must be
3831af332a7SMark Johnston 			 * greater than or equal to 24 bytes and an odd multiple
3841af332a7SMark Johnston 			 * of 8 bytes.  See section 5.1 in RFC 6106.
3851af332a7SMark Johnston 			 */
3861af332a7SMark Johnston 			if (rdnss->nd_opt_rdnss_len < 3 ||
3871af332a7SMark Johnston 			    rdnss->nd_opt_rdnss_len % 2 == 0) {
388db82af41SHiroki Sato 				warnmsg(LOG_INFO, __func__,
3891af332a7SMark Johnston 				    "too short RDNSS option in RA from %s "
3901af332a7SMark Johnston 				    "was ignored.",
3911af332a7SMark Johnston 				inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
3921af332a7SMark Johnston 				    sizeof(ntopbuf)));
393db82af41SHiroki Sato 				break;
394db82af41SHiroki Sato 			}
395db82af41SHiroki Sato 
396a678ca23SHiroki Sato 			addr = (struct in6_addr *)(void *)(raoptp + sizeof(*rdnss));
397db82af41SHiroki Sato 			while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) {
398db82af41SHiroki Sato 				if (inet_ntop(AF_INET6, addr, ntopbuf,
399489e04d0SHiroki Sato 					sizeof(ntopbuf)) == NULL) {
400db82af41SHiroki Sato 					warnmsg(LOG_INFO, __func__,
401db82af41SHiroki Sato 		    			    "an invalid address in RDNSS option"
402db82af41SHiroki Sato 					    " in RA from %s was ignored.",
403db82af41SHiroki Sato 					    inet_ntop(AF_INET6, &from.sin6_addr,
404489e04d0SHiroki Sato 						ntopbuf, sizeof(ntopbuf)));
405db82af41SHiroki Sato 					addr++;
406db82af41SHiroki Sato 					continue;
407db82af41SHiroki Sato 				}
408db82af41SHiroki Sato 				if (IN6_IS_ADDR_LINKLOCAL(addr))
409db82af41SHiroki Sato 					/* XXX: % has to be escaped here */
410db82af41SHiroki Sato 					l = snprintf(nsbuf, sizeof(nsbuf),
411db82af41SHiroki Sato 					    "%s%c%s", ntopbuf,
412db82af41SHiroki Sato 					    SCOPE_DELIMITER,
413db82af41SHiroki Sato 					    ifi->ifname);
414db82af41SHiroki Sato 				else
415db82af41SHiroki Sato 					l = snprintf(nsbuf, sizeof(nsbuf),
416db82af41SHiroki Sato 					    "%s", ntopbuf);
417db82af41SHiroki Sato 				if (l < 0 || (size_t)l >= sizeof(nsbuf)) {
418db82af41SHiroki Sato 					warnmsg(LOG_ERR, __func__,
419db82af41SHiroki Sato 					    "address copying error in "
420db82af41SHiroki Sato 					    "RDNSS option: %d.", l);
421db82af41SHiroki Sato 					addr++;
422db82af41SHiroki Sato 					continue;
423db82af41SHiroki Sato 				}
424db82af41SHiroki Sato 				warnmsg(LOG_DEBUG, __func__, "nsbuf = %s",
425db82af41SHiroki Sato 				    nsbuf);
426db82af41SHiroki Sato 
42706056832SHiroki Sato 				newent_rao = 0;
42806056832SHiroki Sato 				rao = find_raopt(rai, ndo->nd_opt_type, nsbuf,
42906056832SHiroki Sato 				    strlen(nsbuf));
43006056832SHiroki Sato 				if (rao == NULL) {
431db82af41SHiroki Sato 					ELM_MALLOC(rao, break);
432db82af41SHiroki Sato 					rao->rao_type = ndo->nd_opt_type;
433db82af41SHiroki Sato 					rao->rao_len = strlen(nsbuf);
434db82af41SHiroki Sato 					rao->rao_msg = strdup(nsbuf);
435db82af41SHiroki Sato 					if (rao->rao_msg == NULL) {
436db82af41SHiroki Sato 						warnmsg(LOG_ERR, __func__,
437db82af41SHiroki Sato 						    "strdup failed: %s",
438db82af41SHiroki Sato 						    strerror(errno));
439db82af41SHiroki Sato 						free(rao);
440db82af41SHiroki Sato 						addr++;
441db82af41SHiroki Sato 						continue;
442db82af41SHiroki Sato 					}
44306056832SHiroki Sato 					newent_rao = 1;
44406056832SHiroki Sato 				}
445db82af41SHiroki Sato 				/* Set expiration timer */
44606056832SHiroki Sato 				memset(&rao->rao_expire, 0,
44706056832SHiroki Sato 				    sizeof(rao->rao_expire));
448db82af41SHiroki Sato 				memset(&lifetime, 0, sizeof(lifetime));
44906056832SHiroki Sato 				lifetime.tv_sec =
45006056832SHiroki Sato 				    ntohl(rdnss->nd_opt_rdnss_lifetime);
4517d26db17SHiroki Sato 				TS_ADD(&now, &lifetime, &rao->rao_expire);
452db82af41SHiroki Sato 
45306056832SHiroki Sato 				if (newent_rao)
45406056832SHiroki Sato 					TAILQ_INSERT_TAIL(&rai->rai_ra_opt,
45506056832SHiroki Sato 					    rao, rao_next);
456db82af41SHiroki Sato 				addr++;
457db82af41SHiroki Sato 			}
458db82af41SHiroki Sato 			break;
459db82af41SHiroki Sato 		case ND_OPT_DNSSL:
460db82af41SHiroki Sato 			dnssl = (struct nd_opt_dnssl *)raoptp;
461db82af41SHiroki Sato 
462db82af41SHiroki Sato 			/* Optlen sanity check (Section 5.3.1 in RFC 6106) */
463db82af41SHiroki Sato 			if (dnssl->nd_opt_dnssl_len < 2) {
464db82af41SHiroki Sato 				warnmsg(LOG_INFO, __func__,
465db82af41SHiroki Sato 		    			"too short DNSSL option"
466db82af41SHiroki Sato 					"in RA from %s was ignored.",
467db82af41SHiroki Sato 					inet_ntop(AF_INET6, &from.sin6_addr,
468489e04d0SHiroki Sato 					    ntopbuf, sizeof(ntopbuf)));
469db82af41SHiroki Sato 				break;
470db82af41SHiroki Sato 			}
471db82af41SHiroki Sato 
472db82af41SHiroki Sato 			/*
473db82af41SHiroki Sato 			 * Ensure NUL-termination in DNSSL in case of
474db82af41SHiroki Sato 			 * malformed field.
475db82af41SHiroki Sato 			 */
476db82af41SHiroki Sato 			p = (char *)RA_OPT_NEXT_HDR(raoptp);
477db82af41SHiroki Sato 			*(p - 1) = '\0';
478db82af41SHiroki Sato 
479db82af41SHiroki Sato 			p = raoptp + sizeof(*dnssl);
480db82af41SHiroki Sato 			while (1 < (len = dname_labeldec(dname, sizeof(dname),
481db82af41SHiroki Sato 			    p))) {
482db82af41SHiroki Sato 				/* length == 1 means empty string */
483db82af41SHiroki Sato 				warnmsg(LOG_DEBUG, __func__, "dname = %s",
484db82af41SHiroki Sato 				    dname);
485db82af41SHiroki Sato 
48606056832SHiroki Sato 				newent_rao = 0;
48706056832SHiroki Sato 				rao = find_raopt(rai, ndo->nd_opt_type, dname,
48806056832SHiroki Sato 				    strlen(dname));
48906056832SHiroki Sato 				if (rao == NULL) {
490db82af41SHiroki Sato 					ELM_MALLOC(rao, break);
491db82af41SHiroki Sato 					rao->rao_type = ndo->nd_opt_type;
492db82af41SHiroki Sato 					rao->rao_len = strlen(dname);
493db82af41SHiroki Sato 					rao->rao_msg = strdup(dname);
494db82af41SHiroki Sato 					if (rao->rao_msg == NULL) {
495db82af41SHiroki Sato 						warnmsg(LOG_ERR, __func__,
496db82af41SHiroki Sato 						    "strdup failed: %s",
497db82af41SHiroki Sato 						    strerror(errno));
498db82af41SHiroki Sato 						free(rao);
49906056832SHiroki Sato 						addr++;
50006056832SHiroki Sato 						continue;
50106056832SHiroki Sato 					}
50206056832SHiroki Sato 					newent_rao = 1;
503db82af41SHiroki Sato 				}
504db82af41SHiroki Sato 				/* Set expiration timer */
50506056832SHiroki Sato 				memset(&rao->rao_expire, 0,
50606056832SHiroki Sato 				    sizeof(rao->rao_expire));
507db82af41SHiroki Sato 				memset(&lifetime, 0, sizeof(lifetime));
50806056832SHiroki Sato 				lifetime.tv_sec =
50906056832SHiroki Sato 				    ntohl(dnssl->nd_opt_dnssl_lifetime);
5107d26db17SHiroki Sato 				TS_ADD(&now, &lifetime, &rao->rao_expire);
511db82af41SHiroki Sato 
51206056832SHiroki Sato 				if (newent_rao)
51306056832SHiroki Sato 					TAILQ_INSERT_TAIL(&rai->rai_ra_opt,
51406056832SHiroki Sato 					    rao, rao_next);
515db82af41SHiroki Sato 				p += len;
516db82af41SHiroki Sato 			}
517db82af41SHiroki Sato 			break;
518db82af41SHiroki Sato 		default:
519db82af41SHiroki Sato 			/* nothing to do for other options */
520db82af41SHiroki Sato 			break;
521db82af41SHiroki Sato 		}
522db82af41SHiroki Sato 		raoptp = (char *)RA_OPT_NEXT_HDR(raoptp);
523db82af41SHiroki Sato 	}
52406056832SHiroki Sato 	if (newent_rai)
52506056832SHiroki Sato 		TAILQ_INSERT_TAIL(&ifi->ifi_rainfo, rai, rai_next);
52606056832SHiroki Sato 
527db82af41SHiroki Sato 	ra_opt_handler(ifi);
5287d56d374SYoshinobu Inoue 	ifi->racnt++;
5297d56d374SYoshinobu Inoue 
5307d56d374SYoshinobu Inoue 	switch (ifi->state) {
5317d56d374SYoshinobu Inoue 	case IFS_IDLE:		/* should be ignored */
5327d56d374SYoshinobu Inoue 	case IFS_DELAY:		/* right? */
5337d56d374SYoshinobu Inoue 		break;
5347d56d374SYoshinobu Inoue 	case IFS_PROBE:
5357d56d374SYoshinobu Inoue 		ifi->state = IFS_IDLE;
5367d56d374SYoshinobu Inoue 		ifi->probes = 0;
5377d56d374SYoshinobu Inoue 		rtsol_timer_update(ifi);
5387d56d374SYoshinobu Inoue 		break;
5397d56d374SYoshinobu Inoue 	}
5407d56d374SYoshinobu Inoue }
54186b032afSHajimu UMEMOTO 
542db82af41SHiroki Sato static char resstr_ns_prefix[] = "nameserver ";
543db82af41SHiroki Sato static char resstr_sh_prefix[] = "search ";
544db82af41SHiroki Sato static char resstr_nl[] = "\n";
545db82af41SHiroki Sato static char resstr_sp[] = " ";
54686b032afSHajimu UMEMOTO 
547db82af41SHiroki Sato int
ra_opt_handler(struct ifinfo * ifi)548db82af41SHiroki Sato ra_opt_handler(struct ifinfo *ifi)
549db82af41SHiroki Sato {
550db82af41SHiroki Sato 	struct ra_opt *rao;
55106056832SHiroki Sato 	struct rainfo *rai;
552db82af41SHiroki Sato 	struct script_msg *smp1, *smp2, *smp3;
5537d26db17SHiroki Sato 	struct timespec now;
554489e04d0SHiroki Sato 	struct script_msg_head_t sm_rdnss_head =
555db82af41SHiroki Sato 	    TAILQ_HEAD_INITIALIZER(sm_rdnss_head);
556489e04d0SHiroki Sato 	struct script_msg_head_t sm_dnssl_head =
557db82af41SHiroki Sato 	    TAILQ_HEAD_INITIALIZER(sm_dnssl_head);
558489e04d0SHiroki Sato 
559db82af41SHiroki Sato 	int dcount, dlen;
560db82af41SHiroki Sato 
561db82af41SHiroki Sato 	dcount = 0;
562db82af41SHiroki Sato 	dlen = strlen(resstr_sh_prefix) + strlen(resstr_nl);
5637d26db17SHiroki Sato 	clock_gettime(CLOCK_MONOTONIC_FAST, &now);
56406056832SHiroki Sato 
56506056832SHiroki Sato 	/*
56606056832SHiroki Sato 	 * All options from multiple RAs with the same or different
56706056832SHiroki Sato 	 * source addresses on a single interface will be gathered and
56806056832SHiroki Sato 	 * handled, not overridden.  [RFC 4861 6.3.4]
56906056832SHiroki Sato 	 */
57006056832SHiroki Sato 	TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) {
57106056832SHiroki Sato 		TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
572db82af41SHiroki Sato 			switch (rao->rao_type) {
573db82af41SHiroki Sato 			case ND_OPT_RDNSS:
5747d26db17SHiroki Sato 				if (TS_CMP(&now, &rao->rao_expire, >)) {
575db82af41SHiroki Sato 					warnmsg(LOG_INFO, __func__,
576db82af41SHiroki Sato 					    "expired rdnss entry: %s",
577db82af41SHiroki Sato 					    (char *)rao->rao_msg);
578db82af41SHiroki Sato 					break;
579db82af41SHiroki Sato 				}
580db82af41SHiroki Sato 				ELM_MALLOC(smp1, continue);
581db82af41SHiroki Sato 				ELM_MALLOC(smp2, goto free1);
582db82af41SHiroki Sato 				ELM_MALLOC(smp3, goto free2);
583db82af41SHiroki Sato 				smp1->sm_msg = resstr_ns_prefix;
58406056832SHiroki Sato 				TAILQ_INSERT_TAIL(&sm_rdnss_head, smp1,
58506056832SHiroki Sato 				    sm_next);
586db82af41SHiroki Sato 				smp2->sm_msg = rao->rao_msg;
58706056832SHiroki Sato 				TAILQ_INSERT_TAIL(&sm_rdnss_head, smp2,
58806056832SHiroki Sato 				    sm_next);
589db82af41SHiroki Sato 				smp3->sm_msg = resstr_nl;
59006056832SHiroki Sato 				TAILQ_INSERT_TAIL(&sm_rdnss_head, smp3,
59106056832SHiroki Sato 				    sm_next);
59206056832SHiroki Sato 				ifi->ifi_rdnss = IFI_DNSOPT_STATE_RECEIVED;
593db82af41SHiroki Sato 				break;
594db82af41SHiroki Sato 			case ND_OPT_DNSSL:
5957d26db17SHiroki Sato 				if (TS_CMP(&now, &rao->rao_expire, >)) {
596db82af41SHiroki Sato 					warnmsg(LOG_INFO, __func__,
597db82af41SHiroki Sato 					    "expired dnssl entry: %s",
598db82af41SHiroki Sato 					    (char *)rao->rao_msg);
599db82af41SHiroki Sato 					break;
600db82af41SHiroki Sato 				}
601db82af41SHiroki Sato 				dcount++;
602db82af41SHiroki Sato 				/* Check resolv.conf(5) restrictions. */
603db82af41SHiroki Sato 				if (dcount > 6) {
604db82af41SHiroki Sato 					warnmsg(LOG_INFO, __func__,
605db82af41SHiroki Sato 					    "dnssl entry exceeding maximum count (%d>6)"
606db82af41SHiroki Sato 					    ": %s", dcount, (char *)rao->rao_msg);
607db82af41SHiroki Sato 					break;
608db82af41SHiroki Sato 				}
609db82af41SHiroki Sato 				if (256 < dlen + strlen(rao->rao_msg) +
610db82af41SHiroki Sato 				    strlen(resstr_sp)) {
611db82af41SHiroki Sato 					warnmsg(LOG_INFO, __func__,
612db82af41SHiroki Sato 					    "dnssl entry exceeding maximum length "
613db82af41SHiroki Sato 					    "(>256): %s", (char *)rao->rao_msg);
614db82af41SHiroki Sato 					break;
615db82af41SHiroki Sato 				}
616db82af41SHiroki Sato 				ELM_MALLOC(smp1, continue);
617db82af41SHiroki Sato 				ELM_MALLOC(smp2, goto free1);
618db82af41SHiroki Sato 				if (TAILQ_EMPTY(&sm_dnssl_head)) {
619db82af41SHiroki Sato 					ELM_MALLOC(smp3, goto free2);
620db82af41SHiroki Sato 					smp3->sm_msg = resstr_sh_prefix;
621db82af41SHiroki Sato 					TAILQ_INSERT_TAIL(&sm_dnssl_head, smp3,
622db82af41SHiroki Sato 					    sm_next);
623db82af41SHiroki Sato 				}
624db82af41SHiroki Sato 				smp1->sm_msg = rao->rao_msg;
62506056832SHiroki Sato 				TAILQ_INSERT_TAIL(&sm_dnssl_head, smp1,
62606056832SHiroki Sato 				    sm_next);
627db82af41SHiroki Sato 				smp2->sm_msg = resstr_sp;
62806056832SHiroki Sato 				TAILQ_INSERT_TAIL(&sm_dnssl_head, smp2,
62906056832SHiroki Sato 				    sm_next);
63006056832SHiroki Sato 				dlen += strlen(rao->rao_msg) +
63106056832SHiroki Sato 				    strlen(resstr_sp);
63206056832SHiroki Sato 				ifi->ifi_dnssl = IFI_DNSOPT_STATE_RECEIVED;
633db82af41SHiroki Sato 				break;
634db82af41SHiroki Sato 			}
635db82af41SHiroki Sato 			continue;
636db82af41SHiroki Sato free2:
637db82af41SHiroki Sato 			free(smp2);
638db82af41SHiroki Sato free1:
639db82af41SHiroki Sato 			free(smp1);
640db82af41SHiroki Sato 		}
641489e04d0SHiroki Sato 		/* Call the script for each information source. */
642489e04d0SHiroki Sato 		if (uflag)
643489e04d0SHiroki Sato 			ra_opt_rdnss_dispatch(ifi, rai, &sm_rdnss_head,
644489e04d0SHiroki Sato 			    &sm_dnssl_head);
64506056832SHiroki Sato 	}
646489e04d0SHiroki Sato 	/* Call the script for each interface. */
647489e04d0SHiroki Sato 	if (!uflag)
648489e04d0SHiroki Sato 		ra_opt_rdnss_dispatch(ifi, NULL, &sm_rdnss_head,
649489e04d0SHiroki Sato 		    &sm_dnssl_head);
650489e04d0SHiroki Sato 	return (0);
651db82af41SHiroki Sato }
652db82af41SHiroki Sato 
653489e04d0SHiroki Sato char *
make_rsid(const char * ifname,const char * origin,struct rainfo * rai)654489e04d0SHiroki Sato make_rsid(const char *ifname, const char *origin, struct rainfo *rai)
655489e04d0SHiroki Sato {
656489e04d0SHiroki Sato 	char hbuf[NI_MAXHOST];
657489e04d0SHiroki Sato 
658489e04d0SHiroki Sato 	if (rai == NULL)
659489e04d0SHiroki Sato 		sprintf(rsid, "%s:%s", ifname, origin);
660489e04d0SHiroki Sato 	else {
661489e04d0SHiroki Sato 		if (!IN6_IS_ADDR_LINKLOCAL(&rai->rai_saddr.sin6_addr))
662489e04d0SHiroki Sato 			return (NULL);
663489e04d0SHiroki Sato 		if (getnameinfo((struct sockaddr *)&rai->rai_saddr,
664489e04d0SHiroki Sato 			rai->rai_saddr.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
665489e04d0SHiroki Sato 			NI_NUMERICHOST) != 0)
666489e04d0SHiroki Sato 			return (NULL);
667489e04d0SHiroki Sato 		sprintf(rsid, "%s:%s:[%s]", ifname, origin, hbuf);
668489e04d0SHiroki Sato 	}
669489e04d0SHiroki Sato 	warnmsg(LOG_DEBUG, __func__, "rsid = [%s]", rsid);
670489e04d0SHiroki Sato 	return (rsid);
671489e04d0SHiroki Sato }
672489e04d0SHiroki Sato 
673489e04d0SHiroki Sato int
ra_opt_rdnss_dispatch(struct ifinfo * ifi,struct rainfo * rai,struct script_msg_head_t * sm_rdnss_head,struct script_msg_head_t * sm_dnssl_head)6742b2135b8SMark Johnston ra_opt_rdnss_dispatch(struct ifinfo *ifi, struct rainfo *rai,
675489e04d0SHiroki Sato     struct script_msg_head_t *sm_rdnss_head,
676489e04d0SHiroki Sato     struct script_msg_head_t *sm_dnssl_head)
677489e04d0SHiroki Sato {
678489e04d0SHiroki Sato 	struct script_msg *smp1;
6792b2135b8SMark Johnston 	const char *r;
680489e04d0SHiroki Sato 	int error;
681489e04d0SHiroki Sato 
682489e04d0SHiroki Sato 	error = 0;
683489e04d0SHiroki Sato 	/* Add \n for DNSSL list. */
684489e04d0SHiroki Sato 	if (!TAILQ_EMPTY(sm_dnssl_head)) {
685489e04d0SHiroki Sato 		ELM_MALLOC(smp1, goto ra_opt_rdnss_freeit);
686489e04d0SHiroki Sato 		smp1->sm_msg = resstr_nl;
687489e04d0SHiroki Sato 		TAILQ_INSERT_TAIL(sm_dnssl_head, smp1, sm_next);
688489e04d0SHiroki Sato 	}
689489e04d0SHiroki Sato 	TAILQ_CONCAT(sm_rdnss_head, sm_dnssl_head, sm_next);
690489e04d0SHiroki Sato 
6912b2135b8SMark Johnston 	r = make_rsid(ifi->ifname, DNSINFO_ORIGIN_LABEL, uflag ? rai : NULL);
692489e04d0SHiroki Sato 	if (r == NULL) {
693489e04d0SHiroki Sato 		warnmsg(LOG_ERR, __func__, "make_rsid() failed.  "
694489e04d0SHiroki Sato 		    "Script was not invoked.");
695489e04d0SHiroki Sato 		error = 1;
696489e04d0SHiroki Sato 		goto ra_opt_rdnss_freeit;
697489e04d0SHiroki Sato 	}
698489e04d0SHiroki Sato 	if (!TAILQ_EMPTY(sm_rdnss_head))
699489e04d0SHiroki Sato 		CALL_SCRIPT(RESADD, sm_rdnss_head);
70006056832SHiroki Sato 	else if (ifi->ifi_rdnss == IFI_DNSOPT_STATE_RECEIVED ||
70106056832SHiroki Sato 	    ifi->ifi_dnssl == IFI_DNSOPT_STATE_RECEIVED) {
702db82af41SHiroki Sato 		CALL_SCRIPT(RESDEL, NULL);
70306056832SHiroki Sato 		ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO;
70406056832SHiroki Sato 		ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO;
70506056832SHiroki Sato 	}
706db82af41SHiroki Sato 
707489e04d0SHiroki Sato ra_opt_rdnss_freeit:
708db82af41SHiroki Sato 	/* Clear script message queue. */
709489e04d0SHiroki Sato 	if (!TAILQ_EMPTY(sm_rdnss_head)) {
710489e04d0SHiroki Sato 		while ((smp1 = TAILQ_FIRST(sm_rdnss_head)) != NULL) {
711489e04d0SHiroki Sato 			TAILQ_REMOVE(sm_rdnss_head, smp1, sm_next);
712db82af41SHiroki Sato 			free(smp1);
713db82af41SHiroki Sato 		}
714db82af41SHiroki Sato 	}
715489e04d0SHiroki Sato 	if (!TAILQ_EMPTY(sm_dnssl_head)) {
716489e04d0SHiroki Sato 		while ((smp1 = TAILQ_FIRST(sm_dnssl_head)) != NULL) {
717489e04d0SHiroki Sato 			TAILQ_REMOVE(sm_dnssl_head, smp1, sm_next);
71806056832SHiroki Sato 			free(smp1);
71906056832SHiroki Sato 		}
72006056832SHiroki Sato 	}
721489e04d0SHiroki Sato 	return (error);
722db82af41SHiroki Sato }
723db82af41SHiroki Sato 
72406056832SHiroki Sato static struct ra_opt *
find_raopt(struct rainfo * rai,int type,void * msg,size_t len)72506056832SHiroki Sato find_raopt(struct rainfo *rai, int type, void *msg, size_t len)
72606056832SHiroki Sato {
72706056832SHiroki Sato 	struct ra_opt *rao;
72806056832SHiroki Sato 
72906056832SHiroki Sato 	TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
73006056832SHiroki Sato 		if (rao->rao_type == type &&
73106056832SHiroki Sato 		    rao->rao_len == strlen(msg) &&
73206056832SHiroki Sato 		    memcmp(rao->rao_msg, msg, len) == 0)
73306056832SHiroki Sato 			break;
73406056832SHiroki Sato 	}
73506056832SHiroki Sato 
73606056832SHiroki Sato 	return (rao);
73706056832SHiroki Sato }
73806056832SHiroki Sato 
739db82af41SHiroki Sato static void
call_script(const char * const argv[],struct script_msg_head_t * sm_head)74004e9edb5SMark Johnston call_script(const char *const argv[], struct script_msg_head_t *sm_head)
741db82af41SHiroki Sato {
742db82af41SHiroki Sato 	struct script_msg *smp;
74304e9edb5SMark Johnston 	ssize_t len;
74404e9edb5SMark Johnston 	int status, wfd;
745db82af41SHiroki Sato 
74604e9edb5SMark Johnston 	if (argv[0] == NULL)
74704e9edb5SMark Johnston 		return;
74804e9edb5SMark Johnston 
74904e9edb5SMark Johnston 	wfd = cap_script_run(capscript, argv);
75004e9edb5SMark Johnston 	if (wfd == -1) {
75104e9edb5SMark Johnston 		warnmsg(LOG_ERR, __func__,
75204e9edb5SMark Johnston 		    "failed to run %s: %s", argv[0], strerror(errno));
75304e9edb5SMark Johnston 		return;
75404e9edb5SMark Johnston 	}
75504e9edb5SMark Johnston 
75604e9edb5SMark Johnston 	if (sm_head != NULL) {
757db82af41SHiroki Sato 		TAILQ_FOREACH(smp, sm_head, sm_next) {
758db82af41SHiroki Sato 			len = strlen(smp->sm_msg);
75904e9edb5SMark Johnston 			warnmsg(LOG_DEBUG, __func__, "write to child = %s(%zd)",
760db82af41SHiroki Sato 			    smp->sm_msg, len);
76104e9edb5SMark Johnston 			if (write(wfd, smp->sm_msg, len) != len) {
762db82af41SHiroki Sato 				warnmsg(LOG_ERR, __func__,
763db82af41SHiroki Sato 				    "write to child failed: %s",
764db82af41SHiroki Sato 				    strerror(errno));
765db82af41SHiroki Sato 				break;
766db82af41SHiroki Sato 			}
767db82af41SHiroki Sato 		}
768db82af41SHiroki Sato 	}
76986b032afSHajimu UMEMOTO 
77004e9edb5SMark Johnston 	(void)close(wfd);
77104e9edb5SMark Johnston 
77204e9edb5SMark Johnston 	if (cap_script_wait(capscript, &status) != 0)
77304e9edb5SMark Johnston 		warnmsg(LOG_ERR, __func__, "wait(): %s", strerror(errno));
774db82af41SHiroki Sato 	else
77504e9edb5SMark Johnston 		warnmsg(LOG_DEBUG, __func__, "script \"%s\" status %d",
77604e9edb5SMark Johnston 		    argv[0], status);
77786b032afSHajimu UMEMOTO }
778db82af41SHiroki Sato 
779db82af41SHiroki Sato /* Decode domain name label encoding in RFC 1035 Section 3.1 */
780db82af41SHiroki Sato static size_t
dname_labeldec(char * dst,size_t dlen,const char * src)781db82af41SHiroki Sato dname_labeldec(char *dst, size_t dlen, const char *src)
782db82af41SHiroki Sato {
783db82af41SHiroki Sato 	size_t len;
784db82af41SHiroki Sato 	const char *src_origin;
785db82af41SHiroki Sato 	const char *src_last;
786db82af41SHiroki Sato 	const char *dst_origin;
787db82af41SHiroki Sato 
788db82af41SHiroki Sato 	src_origin = src;
789db82af41SHiroki Sato 	src_last = strchr(src, '\0');
790db82af41SHiroki Sato 	dst_origin = dst;
791db82af41SHiroki Sato 	memset(dst, '\0', dlen);
792f2fb8af1SMark Johnston 	while ((len = (*src++) & 0x3f) &&
793f2fb8af1SMark Johnston 	    src + len <= src_last &&
794f2fb8af1SMark Johnston 	    len + (dst == dst_origin ? 0 : 1) < dlen) {
795f2fb8af1SMark Johnston 		if (dst != dst_origin) {
796db82af41SHiroki Sato 			*dst++ = '.';
797f2fb8af1SMark Johnston 			dlen--;
798f2fb8af1SMark Johnston 		}
799db82af41SHiroki Sato 		warnmsg(LOG_DEBUG, __func__, "labellen = %zd", len);
800db82af41SHiroki Sato 		memcpy(dst, src, len);
801db82af41SHiroki Sato 		src += len;
802db82af41SHiroki Sato 		dst += len;
803f2fb8af1SMark Johnston 		dlen -= len;
804db82af41SHiroki Sato 	}
805db82af41SHiroki Sato 	*dst = '\0';
806db82af41SHiroki Sato 
807db82af41SHiroki Sato 	/*
808db82af41SHiroki Sato 	 * XXX validate that domain name only contains valid characters
809db82af41SHiroki Sato 	 * for two reasons: 1) correctness, 2) we do not want to pass
810db82af41SHiroki Sato 	 * possible malicious, unescaped characters like `` to a script
811db82af41SHiroki Sato 	 * or program that could be exploited that way.
812db82af41SHiroki Sato 	 */
813db82af41SHiroki Sato 
814db82af41SHiroki Sato 	return (src - src_origin);
815db82af41SHiroki Sato }
816