xref: /openbsd-src/sbin/slaacd/frontend.c (revision 0e59d0d19ca6a10a17663d531bcea1b99c1bfe09)
1*0e59d0d1Sclaudio /*	$OpenBSD: frontend.c,v 1.74 2024/11/21 13:35:20 claudio Exp $	*/
20acf3e2dSflorian 
30acf3e2dSflorian /*
40acf3e2dSflorian  * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
50acf3e2dSflorian  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
60acf3e2dSflorian  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
70acf3e2dSflorian  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
80acf3e2dSflorian  *
90acf3e2dSflorian  * Permission to use, copy, modify, and distribute this software for any
100acf3e2dSflorian  * purpose with or without fee is hereby granted, provided that the above
110acf3e2dSflorian  * copyright notice and this permission notice appear in all copies.
120acf3e2dSflorian  *
130acf3e2dSflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
140acf3e2dSflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
150acf3e2dSflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
160acf3e2dSflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
170acf3e2dSflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
180acf3e2dSflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
190acf3e2dSflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
200acf3e2dSflorian  */
210acf3e2dSflorian #include <sys/types.h>
220acf3e2dSflorian #include <sys/ioctl.h>
230acf3e2dSflorian #include <sys/queue.h>
240acf3e2dSflorian #include <sys/socket.h>
250acf3e2dSflorian #include <sys/syslog.h>
260acf3e2dSflorian #include <sys/uio.h>
270acf3e2dSflorian 
280acf3e2dSflorian #include <net/if.h>
290acf3e2dSflorian #include <net/if_dl.h>
300acf3e2dSflorian #include <net/if_types.h>
310acf3e2dSflorian #include <net/route.h>
320acf3e2dSflorian 
330acf3e2dSflorian #include <arpa/inet.h>
340acf3e2dSflorian 
350acf3e2dSflorian #include <netinet/in.h>
360acf3e2dSflorian #include <netinet/if_ether.h>
37060a7308Sflorian #include <netinet6/nd6.h>
38060a7308Sflorian #include <netinet6/in6_var.h>
390acf3e2dSflorian #include <netinet/ip6.h>
400acf3e2dSflorian #include <netinet/icmp6.h>
410acf3e2dSflorian 
420acf3e2dSflorian #include <errno.h>
430acf3e2dSflorian #include <event.h>
440acf3e2dSflorian #include <ifaddrs.h>
450acf3e2dSflorian #include <imsg.h>
460acf3e2dSflorian #include <pwd.h>
470acf3e2dSflorian #include <signal.h>
480acf3e2dSflorian #include <stdio.h>
490acf3e2dSflorian #include <stdlib.h>
500acf3e2dSflorian #include <string.h>
510acf3e2dSflorian #include <unistd.h>
520acf3e2dSflorian 
530acf3e2dSflorian #include "log.h"
540acf3e2dSflorian #include "slaacd.h"
550acf3e2dSflorian #include "frontend.h"
560acf3e2dSflorian #include "control.h"
570acf3e2dSflorian 
580acf3e2dSflorian #define	ROUTE_SOCKET_BUF_SIZE	16384
590acf3e2dSflorian #define	ALLROUTER		"ff02::2"
600acf3e2dSflorian 
61dd19964dSflorian struct icmp6_ev {
62dd19964dSflorian 	struct event		 ev;
63dd19964dSflorian 	uint8_t			 answer[1500];
64dd19964dSflorian 	struct msghdr		 rcvmhdr;
65dd19964dSflorian 	struct iovec		 rcviov[1];
66dd19964dSflorian 	struct sockaddr_in6	 from;
67dd19964dSflorian 	int			 refcnt;
68dd19964dSflorian };
69dd19964dSflorian 
70dd19964dSflorian struct iface {
71dd19964dSflorian 	LIST_ENTRY(iface)	 entries;
72dd19964dSflorian 	struct icmp6_ev		*icmp6ev;
73dd19964dSflorian 	struct ether_addr	 hw_address;
74dd19964dSflorian 	uint32_t		 if_index;
75dd19964dSflorian 	int			 rdomain;
76dd19964dSflorian 	int			 send_solicitation;
7711cc0cd9Sflorian 	int			 ll_tentative;
78dd19964dSflorian };
79dd19964dSflorian 
800acf3e2dSflorian __dead void	 frontend_shutdown(void);
810acf3e2dSflorian void		 frontend_sig_handler(int, short, void *);
820acf3e2dSflorian void		 update_iface(uint32_t, char*);
830acf3e2dSflorian void		 frontend_startup(void);
840acf3e2dSflorian void		 route_receive(int, short, void *);
850acf3e2dSflorian void		 handle_route_message(struct rt_msghdr *, struct sockaddr **);
860acf3e2dSflorian void		 get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
870acf3e2dSflorian void		 icmp6_receive(int, short, void *);
880acf3e2dSflorian int		 get_flags(char *);
890acf3e2dSflorian int		 get_xflags(char *);
90c966f6c0Sflorian int		 get_ifrdomain(char *);
91dd19964dSflorian struct iface	*get_iface_by_id(uint32_t);
92dd19964dSflorian void		 remove_iface(uint32_t);
93dd19964dSflorian struct icmp6_ev	*get_icmp6ev_by_rdomain(int);
94eaf8d593Sflorian void		 unref_icmp6ev(struct iface *);
95dd19964dSflorian void		 set_icmp6sock(int, int);
960acf3e2dSflorian void		 send_solicitation(uint32_t);
97060a7308Sflorian #ifndef	SMALL
9805b87f88Sflorian const char	*flags_to_str(int);
99060a7308Sflorian #endif	/* SMALL */
1000acf3e2dSflorian 
101dd19964dSflorian LIST_HEAD(, iface)		 interfaces;
1025ebbfac0Sflorian static struct imsgev		*iev_main;
1035ebbfac0Sflorian static struct imsgev		*iev_engine;
1040acf3e2dSflorian struct event			 ev_route;
1050acf3e2dSflorian struct msghdr			 sndmhdr;
1060acf3e2dSflorian struct iovec			 sndiov[4];
1070acf3e2dSflorian struct nd_router_solicit	 rs;
1080acf3e2dSflorian struct nd_opt_hdr		 nd_opt_hdr;
1090acf3e2dSflorian struct ether_addr		 nd_opt_source_link_addr;
1100acf3e2dSflorian struct sockaddr_in6		 dst;
111dd19964dSflorian int				 ioctlsock;
1120acf3e2dSflorian 
1130acf3e2dSflorian void
1140acf3e2dSflorian frontend_sig_handler(int sig, short event, void *bula)
1150acf3e2dSflorian {
1160acf3e2dSflorian 	/*
1170acf3e2dSflorian 	 * Normal signal handler rules don't apply because libevent
1180acf3e2dSflorian 	 * decouples for us.
1190acf3e2dSflorian 	 */
1200acf3e2dSflorian 
1210acf3e2dSflorian 	switch (sig) {
1220acf3e2dSflorian 	case SIGINT:
1230acf3e2dSflorian 	case SIGTERM:
1240acf3e2dSflorian 		frontend_shutdown();
1250acf3e2dSflorian 	default:
1260acf3e2dSflorian 		fatalx("unexpected signal");
1270acf3e2dSflorian 	}
1280acf3e2dSflorian }
1290acf3e2dSflorian 
1300acf3e2dSflorian void
131f9631f15Sflorian frontend(int debug, int verbose)
1320acf3e2dSflorian {
1330acf3e2dSflorian 	struct event		 ev_sigint, ev_sigterm;
1340acf3e2dSflorian 	struct passwd		*pw;
1350acf3e2dSflorian 	struct in6_pktinfo	*pi;
1360acf3e2dSflorian 	struct cmsghdr		*cm;
137dd19964dSflorian 	size_t			 sndcmsglen;
138f9631f15Sflorian 	int			 hoplimit = 255;
139dd19964dSflorian 	uint8_t			*sndcmsgbuf;
1400acf3e2dSflorian 
1410acf3e2dSflorian 	log_init(debug, LOG_DAEMON);
1420acf3e2dSflorian 	log_setverbose(verbose);
1430acf3e2dSflorian 
1440acf3e2dSflorian 	if ((pw = getpwnam(SLAACD_USER)) == NULL)
1450acf3e2dSflorian 		fatal("getpwnam");
1460acf3e2dSflorian 
1470acf3e2dSflorian 	if (chdir("/") == -1)
1480acf3e2dSflorian 		fatal("chdir(\"/\")");
1490acf3e2dSflorian 
150ec8f555eSflorian 	if (unveil("/", "") == -1)
151bc5a8259Sbeck 		fatal("unveil /");
152ec8f555eSflorian 	if (unveil(NULL, NULL) == -1)
153bc5a8259Sbeck 		fatal("unveil");
154ec8f555eSflorian 
15531525baaSflorian 	setproctitle("%s", "frontend");
15631525baaSflorian 	log_procinit("frontend");
1570acf3e2dSflorian 
158df69c215Sderaadt 	if ((ioctlsock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
1590acf3e2dSflorian 		fatal("socket");
1600acf3e2dSflorian 
1610acf3e2dSflorian 	if (setgroups(1, &pw->pw_gid) ||
1620acf3e2dSflorian 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
1630acf3e2dSflorian 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
1640acf3e2dSflorian 		fatal("can't drop privileges");
1650acf3e2dSflorian 
1660c8652d6Sflorian 	if (pledge("stdio unix recvfd route", NULL) == -1)
1670acf3e2dSflorian 		fatal("pledge");
1680acf3e2dSflorian 
1690acf3e2dSflorian 	event_init();
1700acf3e2dSflorian 
1710acf3e2dSflorian 	/* Setup signal handler. */
1720acf3e2dSflorian 	signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
1730acf3e2dSflorian 	signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
1740acf3e2dSflorian 	signal_add(&ev_sigint, NULL);
1750acf3e2dSflorian 	signal_add(&ev_sigterm, NULL);
1760acf3e2dSflorian 	signal(SIGPIPE, SIG_IGN);
1770acf3e2dSflorian 	signal(SIGHUP, SIG_IGN);
1780acf3e2dSflorian 
1790acf3e2dSflorian 	/* Setup pipe and event handler to the parent process. */
1800acf3e2dSflorian 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
1810acf3e2dSflorian 		fatal(NULL);
182*0e59d0d1Sclaudio 	if (imsgbuf_init(&iev_main->ibuf, 3) == -1)
183*0e59d0d1Sclaudio 		fatal(NULL);
184*0e59d0d1Sclaudio 	imsgbuf_allow_fdpass(&iev_main->ibuf);
1850acf3e2dSflorian 	iev_main->handler = frontend_dispatch_main;
1860acf3e2dSflorian 	iev_main->events = EV_READ;
1870acf3e2dSflorian 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
1880acf3e2dSflorian 	    iev_main->handler, iev_main);
1890acf3e2dSflorian 	event_add(&iev_main->ev, NULL);
1900acf3e2dSflorian 
1910acf3e2dSflorian 	sndcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
1920acf3e2dSflorian 	    CMSG_SPACE(sizeof(int));
1930acf3e2dSflorian 
1940acf3e2dSflorian 	if ((sndcmsgbuf = malloc(sndcmsglen)) == NULL)
1950acf3e2dSflorian 		fatal("malloc");
1960acf3e2dSflorian 
1970acf3e2dSflorian 	rs.nd_rs_type = ND_ROUTER_SOLICIT;
1980acf3e2dSflorian 	rs.nd_rs_code = 0;
1990acf3e2dSflorian 	rs.nd_rs_cksum = 0;
2000acf3e2dSflorian 	rs.nd_rs_reserved = 0;
2010acf3e2dSflorian 
2020acf3e2dSflorian 	nd_opt_hdr.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
2030acf3e2dSflorian 	nd_opt_hdr.nd_opt_len = 1;
2040acf3e2dSflorian 
2050acf3e2dSflorian 	memset(&dst, 0, sizeof(dst));
2060acf3e2dSflorian 	dst.sin6_family = AF_INET6;
2070acf3e2dSflorian 	if (inet_pton(AF_INET6, ALLROUTER, &dst.sin6_addr.s6_addr) != 1)
2080acf3e2dSflorian 		fatal("inet_pton");
2090acf3e2dSflorian 
2100acf3e2dSflorian 	sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
2110acf3e2dSflorian 	sndmhdr.msg_iov = sndiov;
2120acf3e2dSflorian 	sndmhdr.msg_iovlen = 3;
2130acf3e2dSflorian 	sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
2140acf3e2dSflorian 	sndmhdr.msg_controllen = sndcmsglen;
2150acf3e2dSflorian 
2160acf3e2dSflorian 	sndmhdr.msg_name = (caddr_t)&dst;
2170acf3e2dSflorian 	sndmhdr.msg_iov[0].iov_base = (caddr_t)&rs;
2180acf3e2dSflorian 	sndmhdr.msg_iov[0].iov_len = sizeof(rs);
2190acf3e2dSflorian 	sndmhdr.msg_iov[1].iov_base = (caddr_t)&nd_opt_hdr;
2200acf3e2dSflorian 	sndmhdr.msg_iov[1].iov_len = sizeof(nd_opt_hdr);
2210acf3e2dSflorian 	sndmhdr.msg_iov[2].iov_base = (caddr_t)&nd_opt_source_link_addr;
2220acf3e2dSflorian 	sndmhdr.msg_iov[2].iov_len = sizeof(nd_opt_source_link_addr);
2230acf3e2dSflorian 
2240acf3e2dSflorian 	cm = CMSG_FIRSTHDR(&sndmhdr);
2250acf3e2dSflorian 
2260acf3e2dSflorian 	cm->cmsg_level = IPPROTO_IPV6;
2270acf3e2dSflorian 	cm->cmsg_type = IPV6_PKTINFO;
2280acf3e2dSflorian 	cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
2290acf3e2dSflorian 	pi = (struct in6_pktinfo *)CMSG_DATA(cm);
2300acf3e2dSflorian 	memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));
2310acf3e2dSflorian 	pi->ipi6_ifindex = 0;
2320acf3e2dSflorian 
2330acf3e2dSflorian 	cm = CMSG_NXTHDR(&sndmhdr, cm);
2340acf3e2dSflorian 	cm->cmsg_level = IPPROTO_IPV6;
2350acf3e2dSflorian 	cm->cmsg_type = IPV6_HOPLIMIT;
2360acf3e2dSflorian 	cm->cmsg_len = CMSG_LEN(sizeof(int));
2370acf3e2dSflorian 	memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
2380acf3e2dSflorian 
239dd19964dSflorian 	LIST_INIT(&interfaces);
240dd19964dSflorian 
2410acf3e2dSflorian 	event_dispatch();
2420acf3e2dSflorian 
2430acf3e2dSflorian 	frontend_shutdown();
2440acf3e2dSflorian }
2450acf3e2dSflorian 
2460acf3e2dSflorian __dead void
2470acf3e2dSflorian frontend_shutdown(void)
2480acf3e2dSflorian {
2490acf3e2dSflorian 	/* Close pipes. */
250dd7efffeSclaudio 	imsgbuf_write(&iev_engine->ibuf);
2519cbf9e90Sclaudio 	imsgbuf_clear(&iev_engine->ibuf);
2520acf3e2dSflorian 	close(iev_engine->ibuf.fd);
253dd7efffeSclaudio 	imsgbuf_write(&iev_main->ibuf);
2549cbf9e90Sclaudio 	imsgbuf_clear(&iev_main->ibuf);
2550acf3e2dSflorian 	close(iev_main->ibuf.fd);
2560acf3e2dSflorian 
2570acf3e2dSflorian 	free(iev_engine);
2580acf3e2dSflorian 	free(iev_main);
2590acf3e2dSflorian 
2600acf3e2dSflorian 	log_info("frontend exiting");
2610acf3e2dSflorian 	exit(0);
2620acf3e2dSflorian }
2630acf3e2dSflorian 
2640acf3e2dSflorian int
2650acf3e2dSflorian frontend_imsg_compose_main(int type, pid_t pid, void *data,
2660acf3e2dSflorian     uint16_t datalen)
2670acf3e2dSflorian {
2680acf3e2dSflorian 	return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
2690acf3e2dSflorian 	    datalen));
2700acf3e2dSflorian }
2710acf3e2dSflorian 
2720acf3e2dSflorian int
2730acf3e2dSflorian frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid,
2740acf3e2dSflorian     void *data, uint16_t datalen)
2750acf3e2dSflorian {
2760acf3e2dSflorian 	return (imsg_compose_event(iev_engine, type, peerid, pid, -1,
2770acf3e2dSflorian 	    data, datalen));
2780acf3e2dSflorian }
2790acf3e2dSflorian 
2800acf3e2dSflorian void
2810acf3e2dSflorian frontend_dispatch_main(int fd, short event, void *bula)
2820acf3e2dSflorian {
2830acf3e2dSflorian 	struct imsg		 imsg;
2840acf3e2dSflorian 	struct imsgev		*iev = bula;
2850acf3e2dSflorian 	struct imsgbuf		*ibuf = &iev->ibuf;
2860acf3e2dSflorian 	ssize_t			 n;
28733d2acb6Sflorian 	uint32_t		 type;
288dd19964dSflorian 	int			 shut = 0, icmp6sock, rdomain;
2890acf3e2dSflorian 
2900acf3e2dSflorian 	if (event & EV_READ) {
291668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
292dd7efffeSclaudio 			fatal("imsgbuf_read error");
2930acf3e2dSflorian 		if (n == 0)	/* Connection closed. */
2940acf3e2dSflorian 			shut = 1;
2950acf3e2dSflorian 	}
2960acf3e2dSflorian 	if (event & EV_WRITE) {
297dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
298e3b6409cSclaudio 			if (errno == EPIPE)	/* Connection closed. */
2990acf3e2dSflorian 				shut = 1;
300e3b6409cSclaudio 			else
301dd7efffeSclaudio 				fatal("imsgbuf_write");
302e3b6409cSclaudio 		}
3030acf3e2dSflorian 	}
3040acf3e2dSflorian 
3050acf3e2dSflorian 	for (;;) {
3060acf3e2dSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
3070acf3e2dSflorian 			fatal("%s: imsg_get error", __func__);
3080acf3e2dSflorian 		if (n == 0)	/* No more messages. */
3090acf3e2dSflorian 			break;
3100acf3e2dSflorian 
31133d2acb6Sflorian 		type = imsg_get_type(&imsg);
31233d2acb6Sflorian 
31333d2acb6Sflorian 		switch (type) {
3140acf3e2dSflorian 		case IMSG_SOCKET_IPC:
3150acf3e2dSflorian 			/*
3160acf3e2dSflorian 			 * Setup pipe and event handler to the engine
3170acf3e2dSflorian 			 * process.
3180acf3e2dSflorian 			 */
31958a273d8Sflorian 			if (iev_engine)
32058a273d8Sflorian 				fatalx("%s: received unexpected imsg fd "
3210acf3e2dSflorian 				    "to frontend", __func__);
32258a273d8Sflorian 
3238de8a5baSclaudio 			if ((fd = imsg_get_fd(&imsg)) == -1)
32458a273d8Sflorian 				fatalx("%s: expected to receive imsg fd to "
3250acf3e2dSflorian 				   "frontend but didn't receive any",
3260acf3e2dSflorian 				   __func__);
3270acf3e2dSflorian 
3280acf3e2dSflorian 			iev_engine = malloc(sizeof(struct imsgev));
3290acf3e2dSflorian 			if (iev_engine == NULL)
3300acf3e2dSflorian 				fatal(NULL);
3310acf3e2dSflorian 
332*0e59d0d1Sclaudio 			if (imsgbuf_init(&iev_engine->ibuf, fd) == -1)
333*0e59d0d1Sclaudio 				fatal(NULL);
3340acf3e2dSflorian 			iev_engine->handler = frontend_dispatch_engine;
3350acf3e2dSflorian 			iev_engine->events = EV_READ;
3360acf3e2dSflorian 
3370acf3e2dSflorian 			event_set(&iev_engine->ev, iev_engine->ibuf.fd,
3380acf3e2dSflorian 			iev_engine->events, iev_engine->handler, iev_engine);
3390acf3e2dSflorian 			event_add(&iev_engine->ev, NULL);
340f9631f15Sflorian 			break;
341f9631f15Sflorian 		case IMSG_ICMP6SOCK:
3428de8a5baSclaudio 			if ((icmp6sock = imsg_get_fd(&imsg)) == -1)
343f9631f15Sflorian 				fatalx("%s: expected to receive imsg "
344f9631f15Sflorian 				    "ICMPv6 fd but didn't receive any",
345f9631f15Sflorian 				    __func__);
34633d2acb6Sflorian 			if (imsg_get_data(&imsg, &rdomain,
34733d2acb6Sflorian 			    sizeof(rdomain)) == -1)
34833d2acb6Sflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
34933d2acb6Sflorian 
350dd19964dSflorian 			set_icmp6sock(icmp6sock, rdomain);
351f9631f15Sflorian 			break;
352f9631f15Sflorian 		case IMSG_ROUTESOCK:
3538de8a5baSclaudio 			if ((fd = imsg_get_fd(&imsg)) == -1)
354f9631f15Sflorian 				fatalx("%s: expected to receive imsg "
355f9631f15Sflorian 				    "routesocket fd but didn't receive any",
356f9631f15Sflorian 				    __func__);
35733d2acb6Sflorian 
358f9631f15Sflorian 			event_set(&ev_route, fd, EV_READ | EV_PERSIST,
359f9631f15Sflorian 			    route_receive, NULL);
360f9631f15Sflorian 			break;
3617e5ccecdSflorian 		case IMSG_STARTUP:
3627e5ccecdSflorian 			frontend_startup();
3637e5ccecdSflorian 			break;
364f9631f15Sflorian #ifndef	SMALL
365f9631f15Sflorian 		case IMSG_CONTROLFD:
3668de8a5baSclaudio 			if ((fd = imsg_get_fd(&imsg)) == -1)
367f9631f15Sflorian 				fatalx("%s: expected to receive imsg "
368f9631f15Sflorian 				    "control fd but didn't receive any",
369f9631f15Sflorian 				    __func__);
37033d2acb6Sflorian 
371f9631f15Sflorian 			/* Listen on control socket. */
372bed1cdeeSflorian 			control_listen(fd);
3730acf3e2dSflorian 			break;
3740acf3e2dSflorian 		case IMSG_CTL_END:
3750acf3e2dSflorian 			control_imsg_relay(&imsg);
3760acf3e2dSflorian 			break;
377bf0ed931Sflorian #endif	/* SMALL */
3780acf3e2dSflorian 		default:
37933d2acb6Sflorian 			log_debug("%s: error handling imsg %d", __func__, type);
3800acf3e2dSflorian 			break;
3810acf3e2dSflorian 		}
3820acf3e2dSflorian 		imsg_free(&imsg);
3830acf3e2dSflorian 	}
3840acf3e2dSflorian 	if (!shut)
3850acf3e2dSflorian 		imsg_event_add(iev);
3860acf3e2dSflorian 	else {
3870acf3e2dSflorian 		/* This pipe is dead. Remove its event handler. */
3880acf3e2dSflorian 		event_del(&iev->ev);
3890acf3e2dSflorian 		event_loopexit(NULL);
3900acf3e2dSflorian 	}
3910acf3e2dSflorian }
3920acf3e2dSflorian 
3930acf3e2dSflorian void
3940acf3e2dSflorian frontend_dispatch_engine(int fd, short event, void *bula)
3950acf3e2dSflorian {
3960acf3e2dSflorian 	struct imsgev		*iev = bula;
3970acf3e2dSflorian 	struct imsgbuf		*ibuf = &iev->ibuf;
3980acf3e2dSflorian 	struct imsg		 imsg;
3990acf3e2dSflorian 	ssize_t			 n;
4000acf3e2dSflorian 	int			 shut = 0;
40133d2acb6Sflorian 	uint32_t		 if_index, type;
4020acf3e2dSflorian 
4030acf3e2dSflorian 	if (event & EV_READ) {
404668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
405dd7efffeSclaudio 			fatal("imsgbuf_read error");
4060acf3e2dSflorian 		if (n == 0)	/* Connection closed. */
4070acf3e2dSflorian 			shut = 1;
4080acf3e2dSflorian 	}
4090acf3e2dSflorian 	if (event & EV_WRITE) {
410dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
411e3b6409cSclaudio 			if (errno == EPIPE)	/* Connection closed. */
4120acf3e2dSflorian 				shut = 1;
413e3b6409cSclaudio 			else
414dd7efffeSclaudio 				fatal("imsgbuf_write");
415e3b6409cSclaudio 		}
4160acf3e2dSflorian 	}
4170acf3e2dSflorian 
4180acf3e2dSflorian 	for (;;) {
4190acf3e2dSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
4200acf3e2dSflorian 			fatal("%s: imsg_get error", __func__);
4210acf3e2dSflorian 		if (n == 0)	/* No more messages. */
4220acf3e2dSflorian 			break;
4230acf3e2dSflorian 
42433d2acb6Sflorian 		type = imsg_get_type(&imsg);
42533d2acb6Sflorian 
42633d2acb6Sflorian 		switch (type) {
427bf0ed931Sflorian #ifndef	SMALL
4280acf3e2dSflorian 		case IMSG_CTL_END:
4290acf3e2dSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO:
4300acf3e2dSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO_RA:
4310acf3e2dSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX:
4320acf3e2dSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS:
4330acf3e2dSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS:
4340acf3e2dSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL:
4350acf3e2dSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS:
4360acf3e2dSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL:
43734435150Sflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSALS:
43834435150Sflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSAL:
4390acf3e2dSflorian 			control_imsg_relay(&imsg);
4400acf3e2dSflorian 			break;
441bf0ed931Sflorian #endif	/* SMALL */
4420acf3e2dSflorian 		case IMSG_CTL_SEND_SOLICITATION:
44333d2acb6Sflorian 			if (imsg_get_data(&imsg, &if_index,
44433d2acb6Sflorian 			    sizeof(if_index)) == -1)
44533d2acb6Sflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
44633d2acb6Sflorian 
4470acf3e2dSflorian 			send_solicitation(if_index);
4480acf3e2dSflorian 			break;
4490acf3e2dSflorian 		default:
45033d2acb6Sflorian 			log_debug("%s: error handling imsg %d", __func__, type);
4510acf3e2dSflorian 			break;
4520acf3e2dSflorian 		}
4530acf3e2dSflorian 		imsg_free(&imsg);
4540acf3e2dSflorian 	}
4550acf3e2dSflorian 	if (!shut)
4560acf3e2dSflorian 		imsg_event_add(iev);
4570acf3e2dSflorian 	else {
4580acf3e2dSflorian 		/* This pipe is dead. Remove its event handler. */
4590acf3e2dSflorian 		event_del(&iev->ev);
4600acf3e2dSflorian 		event_loopexit(NULL);
4610acf3e2dSflorian 	}
4620acf3e2dSflorian }
4630acf3e2dSflorian 
4640acf3e2dSflorian int
4650acf3e2dSflorian get_flags(char *if_name)
4660acf3e2dSflorian {
4670acf3e2dSflorian 	struct ifreq		 ifr;
4680acf3e2dSflorian 
4699bdef370Sflorian 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
4708c4e2bd5Sflorian 	if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
4718c4e2bd5Sflorian 		log_warn("SIOCGIFFLAGS");
4728c4e2bd5Sflorian 		return -1;
4738c4e2bd5Sflorian 	}
4740acf3e2dSflorian 	return ifr.ifr_flags;
4750acf3e2dSflorian }
4760acf3e2dSflorian 
4770acf3e2dSflorian int
4780acf3e2dSflorian get_xflags(char *if_name)
4790acf3e2dSflorian {
4800acf3e2dSflorian 	struct ifreq		 ifr;
4810acf3e2dSflorian 
4829bdef370Sflorian 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
4838c4e2bd5Sflorian 	if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) {
4848c4e2bd5Sflorian 		log_warn("SIOCGIFXFLAGS");
4858c4e2bd5Sflorian 		return -1;
4868c4e2bd5Sflorian 	}
4870acf3e2dSflorian 	return ifr.ifr_flags;
4880acf3e2dSflorian }
4890acf3e2dSflorian 
490c966f6c0Sflorian int
491c966f6c0Sflorian get_ifrdomain(char *if_name)
492c966f6c0Sflorian {
493c966f6c0Sflorian 	struct ifreq		 ifr;
494c966f6c0Sflorian 
4959bdef370Sflorian 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
496c966f6c0Sflorian 	if (ioctl(ioctlsock, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1) {
497c966f6c0Sflorian 		log_warn("SIOCGIFRDOMAIN");
498c966f6c0Sflorian 		return -1;
499c966f6c0Sflorian 	}
500c966f6c0Sflorian 	return ifr.ifr_rdomainid;
501c966f6c0Sflorian }
502c966f6c0Sflorian 
5030acf3e2dSflorian void
5040acf3e2dSflorian update_iface(uint32_t if_index, char* if_name)
5050acf3e2dSflorian {
506dd19964dSflorian 	struct iface		*iface;
5078a8bcb8bSflorian 	struct ifaddrs		*ifap, *ifa;
5080acf3e2dSflorian 	struct imsg_ifinfo	 imsg_ifinfo;
5098a8bcb8bSflorian 	struct sockaddr_dl	*sdl;
5108a8bcb8bSflorian 	struct sockaddr_in6	*sin6;
51111cc0cd9Sflorian 	struct in6_ifreq	 ifr6;
512c966f6c0Sflorian 	int			 flags, xflags, ifrdomain;
5130acf3e2dSflorian 
5148c4e2bd5Sflorian 	if ((flags = get_flags(if_name)) == -1 || (xflags =
515dd19964dSflorian 	    get_xflags(if_name)) == -1)
5168c4e2bd5Sflorian 		return;
5170acf3e2dSflorian 
518c6384676Sflorian 	if (!(xflags & (IFXF_AUTOCONF6 | IFXF_AUTOCONF6TEMP)))
5190acf3e2dSflorian 		return;
5200acf3e2dSflorian 
521dd19964dSflorian 	if ((ifrdomain = get_ifrdomain(if_name)) == -1)
522dd19964dSflorian 		return;
523dd19964dSflorian 
524d4a6ab96Sflorian 	iface = get_iface_by_id(if_index);
525d4a6ab96Sflorian 
526d4a6ab96Sflorian 	if (iface != NULL) {
527d4a6ab96Sflorian 		if (iface->rdomain != ifrdomain) {
528eaf8d593Sflorian 			unref_icmp6ev(iface);
529d4a6ab96Sflorian 			iface->rdomain = ifrdomain;
530d4a6ab96Sflorian 			iface->icmp6ev = get_icmp6ev_by_rdomain(ifrdomain);
531d4a6ab96Sflorian 		}
532d4a6ab96Sflorian 	} else {
533dd19964dSflorian 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
534dd19964dSflorian 			fatal("calloc");
535dd19964dSflorian 		iface->if_index = if_index;
536dd19964dSflorian 		iface->rdomain = ifrdomain;
537d4a6ab96Sflorian 		iface->icmp6ev = get_icmp6ev_by_rdomain(ifrdomain);
53811cc0cd9Sflorian 		iface->ll_tentative = 1;
539dd19964dSflorian 
540dd19964dSflorian 		LIST_INSERT_HEAD(&interfaces, iface, entries);
541dd19964dSflorian 	}
542dd19964dSflorian 
5430acf3e2dSflorian 	memset(&imsg_ifinfo, 0, sizeof(imsg_ifinfo));
5440acf3e2dSflorian 
5450acf3e2dSflorian 	imsg_ifinfo.if_index = if_index;
546dd19964dSflorian 	imsg_ifinfo.rdomain = ifrdomain;
5470acf3e2dSflorian 	imsg_ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP |
5480acf3e2dSflorian 	    IFF_RUNNING);
549c6384676Sflorian 	imsg_ifinfo.autoconf = (xflags & IFXF_AUTOCONF6);
550804ba004Sflorian 	imsg_ifinfo.temporary = (xflags & IFXF_AUTOCONF6TEMP);
551b9b376fbSflorian 	imsg_ifinfo.soii = !(xflags & IFXF_INET6_NOSOII);
5520acf3e2dSflorian 
5538a8bcb8bSflorian 	if (getifaddrs(&ifap) != 0)
5548a8bcb8bSflorian 		fatal("getifaddrs");
5558a8bcb8bSflorian 
5568a8bcb8bSflorian 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
5578a8bcb8bSflorian 		if (strcmp(if_name, ifa->ifa_name) != 0)
5588a8bcb8bSflorian 			continue;
5598a8bcb8bSflorian 		if (ifa->ifa_addr == NULL)
5608a8bcb8bSflorian 			continue;
5618a8bcb8bSflorian 
5628a8bcb8bSflorian 		switch(ifa->ifa_addr->sa_family) {
5638a8bcb8bSflorian 		case AF_LINK:
5648a8bcb8bSflorian 			imsg_ifinfo.link_state =
5658a8bcb8bSflorian 			    ((struct if_data *)ifa->ifa_data)->ifi_link_state;
5668a8bcb8bSflorian 			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
5678a8bcb8bSflorian 			if (sdl->sdl_type != IFT_ETHER ||
5688a8bcb8bSflorian 			    sdl->sdl_alen != ETHER_ADDR_LEN)
5698a8bcb8bSflorian 				continue;
5708a8bcb8bSflorian 			memcpy(iface->hw_address.ether_addr_octet,
5718a8bcb8bSflorian 			    LLADDR(sdl), ETHER_ADDR_LEN);
5728a8bcb8bSflorian 			memcpy(imsg_ifinfo.hw_address.ether_addr_octet,
5738a8bcb8bSflorian 			    LLADDR(sdl), ETHER_ADDR_LEN);
5748a8bcb8bSflorian 		case AF_INET6:
5758a8bcb8bSflorian 			sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
5768a8bcb8bSflorian #ifdef __KAME__
5778a8bcb8bSflorian 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
5788a8bcb8bSflorian 			    sin6->sin6_scope_id == 0) {
5798a8bcb8bSflorian 				sin6->sin6_scope_id = ntohs(*(u_int16_t *)
5808a8bcb8bSflorian 				    &sin6->sin6_addr.s6_addr[2]);
5818a8bcb8bSflorian 				sin6->sin6_addr.s6_addr[2] =
5828a8bcb8bSflorian 				    sin6->sin6_addr.s6_addr[3] = 0;
5838a8bcb8bSflorian 			}
5848a8bcb8bSflorian #endif
58511cc0cd9Sflorian 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
5868a8bcb8bSflorian 				memcpy(&imsg_ifinfo.ll_address, sin6,
5878a8bcb8bSflorian 				    sizeof(imsg_ifinfo.ll_address));
58811cc0cd9Sflorian 
58911cc0cd9Sflorian 				if (!iface->ll_tentative)
59011cc0cd9Sflorian 					break;
59111cc0cd9Sflorian 
59211cc0cd9Sflorian 				memset(&ifr6, 0, sizeof(ifr6));
59311cc0cd9Sflorian 				strlcpy(ifr6.ifr_name, if_name,
59411cc0cd9Sflorian 				    sizeof(ifr6.ifr_name));
59511cc0cd9Sflorian 				memcpy(&ifr6.ifr_addr, sin6,
59611cc0cd9Sflorian 				    sizeof(ifr6.ifr_addr));
59711cc0cd9Sflorian 
59811cc0cd9Sflorian 				if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6,
59911cc0cd9Sflorian 				    (caddr_t)&ifr6) == -1) {
60011cc0cd9Sflorian 					log_warn("SIOCGIFAFLAG_IN6");
60111cc0cd9Sflorian 					break;
60211cc0cd9Sflorian 				}
60311cc0cd9Sflorian 
60411cc0cd9Sflorian 				if (!(ifr6.ifr_ifru.ifru_flags6 &
60511cc0cd9Sflorian 				    IN6_IFF_TENTATIVE)) {
60611cc0cd9Sflorian 					iface->ll_tentative = 0;
60711cc0cd9Sflorian 					if (iface->send_solicitation)
60811cc0cd9Sflorian 						send_solicitation(
60911cc0cd9Sflorian 						    iface->if_index);
61011cc0cd9Sflorian 				}
61111cc0cd9Sflorian 			}
6128a8bcb8bSflorian 			break;
6138a8bcb8bSflorian 		default:
6148a8bcb8bSflorian 			break;
6158a8bcb8bSflorian 		}
6168a8bcb8bSflorian 	}
6178a8bcb8bSflorian 
6188a8bcb8bSflorian 	freeifaddrs(ifap);
619dd19964dSflorian 
6208653da7cSflorian 	frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &imsg_ifinfo,
6210acf3e2dSflorian 	    sizeof(imsg_ifinfo));
6220acf3e2dSflorian }
6230acf3e2dSflorian 
624060a7308Sflorian #ifndef	SMALL
62505b87f88Sflorian const char*
62605b87f88Sflorian flags_to_str(int flags)
62705b87f88Sflorian {
62805b87f88Sflorian 	static char	buf[sizeof(" anycast tentative duplicated detached "
629804ba004Sflorian 			    "deprecated autoconf temporary")];
63005b87f88Sflorian 
63105b87f88Sflorian 	buf[0] = '\0';
63205b87f88Sflorian 	if (flags & IN6_IFF_ANYCAST)
6339bdef370Sflorian 		strlcat(buf, " anycast", sizeof(buf));
63405b87f88Sflorian 	if (flags & IN6_IFF_TENTATIVE)
6359bdef370Sflorian 		strlcat(buf, " tentative", sizeof(buf));
63605b87f88Sflorian 	if (flags & IN6_IFF_DUPLICATED)
6379bdef370Sflorian 		strlcat(buf, " duplicated", sizeof(buf));
63805b87f88Sflorian 	if (flags & IN6_IFF_DETACHED)
6399bdef370Sflorian 		strlcat(buf, " detached", sizeof(buf));
64005b87f88Sflorian 	if (flags & IN6_IFF_DEPRECATED)
6419bdef370Sflorian 		strlcat(buf, " deprecated", sizeof(buf));
64205b87f88Sflorian 	if (flags & IN6_IFF_AUTOCONF)
6439bdef370Sflorian 		strlcat(buf, " autoconf", sizeof(buf));
644c99d424eSflorian 	if (flags & IN6_IFF_TEMPORARY)
645804ba004Sflorian 		strlcat(buf, " temporary", sizeof(buf));
64605b87f88Sflorian 
64705b87f88Sflorian 	return (buf);
64805b87f88Sflorian }
649060a7308Sflorian #endif	/* SMALL */
650060a7308Sflorian 
6510acf3e2dSflorian void
6520acf3e2dSflorian frontend_startup(void)
6530acf3e2dSflorian {
6540acf3e2dSflorian 	struct if_nameindex	*ifnidxp, *ifnidx;
6550acf3e2dSflorian 
656f9631f15Sflorian 	if (!event_initialized(&ev_route))
657f9631f15Sflorian 		fatalx("%s: did not receive a route socket from the main "
658f9631f15Sflorian 		    "process", __func__);
659f9631f15Sflorian 
6600acf3e2dSflorian 	event_add(&ev_route, NULL);
661f9631f15Sflorian 
6620acf3e2dSflorian 	if ((ifnidxp = if_nameindex()) == NULL)
6630acf3e2dSflorian 		fatalx("if_nameindex");
6640acf3e2dSflorian 
6650acf3e2dSflorian 	for(ifnidx = ifnidxp; ifnidx->if_index !=0 && ifnidx->if_name != NULL;
6665a030e60Sflorian 	    ifnidx++)
6670acf3e2dSflorian 		update_iface(ifnidx->if_index, ifnidx->if_name);
6680acf3e2dSflorian 
6690acf3e2dSflorian 	if_freenameindex(ifnidxp);
6700acf3e2dSflorian }
6710acf3e2dSflorian 
6720acf3e2dSflorian void
6730acf3e2dSflorian route_receive(int fd, short events, void *arg)
6740acf3e2dSflorian {
675d18b479bSotto 	static uint8_t			 *buf;
6760acf3e2dSflorian 
677d18b479bSotto 	struct rt_msghdr		*rtm;
6780acf3e2dSflorian 	struct sockaddr			*sa, *rti_info[RTAX_MAX];
6790acf3e2dSflorian 	ssize_t				 n;
6800acf3e2dSflorian 
681d18b479bSotto 	if (buf == NULL) {
682d18b479bSotto 		buf = malloc(ROUTE_SOCKET_BUF_SIZE);
683d18b479bSotto 		if (buf == NULL)
684d18b479bSotto 			fatal("malloc");
685d18b479bSotto 	}
686d18b479bSotto 	rtm = (struct rt_msghdr *)buf;
687d18b479bSotto 	if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) {
6880acf3e2dSflorian 		if (errno == EAGAIN || errno == EINTR)
6890acf3e2dSflorian 			return;
6900acf3e2dSflorian 		log_warn("dispatch_rtmsg: read error");
6910acf3e2dSflorian 		return;
6920acf3e2dSflorian 	}
6930acf3e2dSflorian 
694be7530aaSflorian 	if (n == 0)
695be7530aaSflorian 		fatal("routing socket closed");
696be7530aaSflorian 
697128a03f8Sotto 	if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
698d18b479bSotto 		log_warnx("partial rtm of %zd in buffer", n);
6990acf3e2dSflorian 		return;
7000acf3e2dSflorian 	}
7010acf3e2dSflorian 
7020acf3e2dSflorian 	if (rtm->rtm_version != RTM_VERSION)
703be7530aaSflorian 		return;
7040acf3e2dSflorian 
705be7530aaSflorian 	sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen);
7060acf3e2dSflorian 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
7070acf3e2dSflorian 
7080acf3e2dSflorian 	handle_route_message(rtm, rti_info);
7090acf3e2dSflorian }
7100acf3e2dSflorian 
7110acf3e2dSflorian void
7120acf3e2dSflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
7130acf3e2dSflorian {
7140acf3e2dSflorian 	struct if_msghdr		*ifm;
715c932786dSflorian 	struct if_announcemsghdr	*ifan;
7160acf3e2dSflorian 	struct imsg_del_addr		 del_addr;
717d3ff3477Sflorian 	struct imsg_del_route		 del_route;
71805b87f88Sflorian 	struct imsg_dup_addr		 dup_addr;
7190acf3e2dSflorian 	struct sockaddr_rtlabel		*rl;
72005b87f88Sflorian 	struct sockaddr_in6		*sin6;
72105b87f88Sflorian 	struct in6_ifreq		 ifr6;
722d3ff3477Sflorian 	struct in6_addr			*in6;
7234b1b7c91Sflorian 	int				 xflags, if_index;
7240acf3e2dSflorian 	char				 ifnamebuf[IFNAMSIZ];
7250acf3e2dSflorian 	char				*if_name;
7260acf3e2dSflorian 
7270acf3e2dSflorian 	switch (rtm->rtm_type) {
7280acf3e2dSflorian 	case RTM_IFINFO:
7290acf3e2dSflorian 		ifm = (struct if_msghdr *)rtm;
7300acf3e2dSflorian 		if_index = ifm->ifm_index;
731d0c2b6e3Sflorian 		if_name = if_indextoname(if_index, ifnamebuf);
732d0c2b6e3Sflorian 		if (if_name == NULL) {
733d0c2b6e3Sflorian 			log_debug("RTM_IFINFO: lost if %d", if_index);
7340acf3e2dSflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
7350acf3e2dSflorian 			    &if_index, sizeof(if_index));
736c932786dSflorian 			remove_iface(if_index);
737d0c2b6e3Sflorian 			break;
738d0c2b6e3Sflorian 		}
7390acf3e2dSflorian 		xflags = get_xflags(if_name);
740c6384676Sflorian 		if (xflags == -1 || !(xflags & (IFXF_AUTOCONF6 |
741c6384676Sflorian 		    IFXF_AUTOCONF6TEMP))) {
7425a030e60Sflorian 			log_debug("RTM_IFINFO: %s(%d) no(longer) autoconf6",
7435a030e60Sflorian 			    if_name, if_index);
7440acf3e2dSflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0,
7450acf3e2dSflorian 			    0, &if_index, sizeof(if_index));
746dd19964dSflorian 			remove_iface(if_index);
747060a7308Sflorian 		} else {
748d0c2b6e3Sflorian 			update_iface(if_index, if_name);
749060a7308Sflorian 		}
7500acf3e2dSflorian 		break;
751c932786dSflorian 	case RTM_IFANNOUNCE:
752c932786dSflorian 		ifan = (struct if_announcemsghdr *)rtm;
753c932786dSflorian 		if_index = ifan->ifan_index;
754c932786dSflorian                 if (ifan->ifan_what == IFAN_DEPARTURE) {
755c932786dSflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
756c932786dSflorian 			    &if_index, sizeof(if_index));
757c932786dSflorian 			remove_iface(if_index);
758c932786dSflorian 		}
759c932786dSflorian 		break;
7600acf3e2dSflorian 	case RTM_NEWADDR:
7610acf3e2dSflorian 		ifm = (struct if_msghdr *)rtm;
762d0c2b6e3Sflorian 		if_index = ifm->ifm_index;
763d0c2b6e3Sflorian 		if_name = if_indextoname(if_index, ifnamebuf);
7647f8b5cc9Sflorian 		if (if_name == NULL) {
7657f8b5cc9Sflorian 			log_debug("RTM_NEWADDR: lost if %d", if_index);
7667f8b5cc9Sflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
7677f8b5cc9Sflorian 			    &if_index, sizeof(if_index));
7687f8b5cc9Sflorian 			remove_iface(if_index);
7697f8b5cc9Sflorian 			break;
7707f8b5cc9Sflorian 		}
7717f8b5cc9Sflorian 
772d0c2b6e3Sflorian 		log_debug("RTM_NEWADDR: %s[%u]", if_name, if_index);
773d0c2b6e3Sflorian 		update_iface(if_index, if_name);
7740acf3e2dSflorian 		break;
7750acf3e2dSflorian 	case RTM_DELADDR:
7760acf3e2dSflorian 		ifm = (struct if_msghdr *)rtm;
777d0c2b6e3Sflorian 		if_index = ifm->ifm_index;
778d0c2b6e3Sflorian 		if_name = if_indextoname(if_index, ifnamebuf);
7797f8b5cc9Sflorian 		if (if_name == NULL) {
7807f8b5cc9Sflorian 			log_debug("RTM_DELADDR: lost if %d", if_index);
7817f8b5cc9Sflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
7827f8b5cc9Sflorian 			    &if_index, sizeof(if_index));
7837f8b5cc9Sflorian 			remove_iface(if_index);
7847f8b5cc9Sflorian 			break;
7857f8b5cc9Sflorian 		}
7860acf3e2dSflorian 		if (rtm->rtm_addrs & RTA_IFA && rti_info[RTAX_IFA]->sa_family
7870acf3e2dSflorian 		    == AF_INET6) {
788d0c2b6e3Sflorian 			del_addr.if_index = if_index;
7890acf3e2dSflorian 			memcpy(&del_addr.addr, rti_info[RTAX_IFA], sizeof(
7900acf3e2dSflorian 			    del_addr.addr));
7910acf3e2dSflorian 			frontend_imsg_compose_engine(IMSG_DEL_ADDRESS,
7920acf3e2dSflorian 				    0, 0, &del_addr, sizeof(del_addr));
793d0c2b6e3Sflorian 			log_debug("RTM_DELADDR: %s[%u]", if_name, if_index);
7940acf3e2dSflorian 		}
7950acf3e2dSflorian 		break;
79605b87f88Sflorian 	case RTM_CHGADDRATTR:
79705b87f88Sflorian 		ifm = (struct if_msghdr *)rtm;
798d0c2b6e3Sflorian 		if_index = ifm->ifm_index;
799d0c2b6e3Sflorian 		if_name = if_indextoname(if_index, ifnamebuf);
8007f8b5cc9Sflorian 		if (if_name == NULL) {
8017f8b5cc9Sflorian 			log_debug("RTM_CHGADDRATTR: lost if %d", if_index);
8027f8b5cc9Sflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
8037f8b5cc9Sflorian 			    &if_index, sizeof(if_index));
8047f8b5cc9Sflorian 			remove_iface(if_index);
8057f8b5cc9Sflorian 			break;
8067f8b5cc9Sflorian 		}
80705b87f88Sflorian 		if (rtm->rtm_addrs & RTA_IFA && rti_info[RTAX_IFA]->sa_family
80805b87f88Sflorian 		    == AF_INET6) {
80905b87f88Sflorian 			sin6 = (struct sockaddr_in6 *) rti_info[RTAX_IFA];
81005b87f88Sflorian 
81111cc0cd9Sflorian 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
81211cc0cd9Sflorian 				update_iface(if_index, if_name);
81305b87f88Sflorian 				break;
81411cc0cd9Sflorian 			}
81505b87f88Sflorian 
81605b87f88Sflorian 			memset(&ifr6, 0, sizeof(ifr6));
8179bdef370Sflorian 			strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name));
81805b87f88Sflorian 			memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr));
81905b87f88Sflorian 
82005b87f88Sflorian 			if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6, (caddr_t)&ifr6)
821df69c215Sderaadt 			    == -1) {
82205b87f88Sflorian 				log_warn("SIOCGIFAFLAG_IN6");
82305b87f88Sflorian 				break;
82405b87f88Sflorian 			}
82505b87f88Sflorian 
82605b87f88Sflorian #ifndef	SMALL
82705b87f88Sflorian 			log_debug("RTM_CHGADDRATTR: %s - %s",
82805b87f88Sflorian 			    sin6_to_str(sin6),
82905b87f88Sflorian 			    flags_to_str(ifr6.ifr_ifru.ifru_flags6));
83005b87f88Sflorian #endif	/* SMALL */
83105b87f88Sflorian 
83205b87f88Sflorian 			if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED) {
833d0c2b6e3Sflorian 				dup_addr.if_index = if_index;
83405b87f88Sflorian 				dup_addr.addr = *sin6;
83505b87f88Sflorian 				frontend_imsg_compose_engine(IMSG_DUP_ADDRESS,
83605b87f88Sflorian 				    0, 0, &dup_addr, sizeof(dup_addr));
83705b87f88Sflorian 			}
83805b87f88Sflorian 
83905b87f88Sflorian 		}
84005b87f88Sflorian 		break;
841d3ff3477Sflorian 	case RTM_DELETE:
842d3ff3477Sflorian 		ifm = (struct if_msghdr *)rtm;
843d3ff3477Sflorian 		if ((rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY | RTA_LABEL)) !=
844d3ff3477Sflorian 		    (RTA_DST | RTA_GATEWAY | RTA_LABEL))
845d3ff3477Sflorian 			break;
846d3ff3477Sflorian 		if (rti_info[RTAX_DST]->sa_family != AF_INET6)
847d3ff3477Sflorian 			break;
848d3ff3477Sflorian 		if (!IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)
849d3ff3477Sflorian 		    rti_info[RTAX_DST])->sin6_addr))
850d3ff3477Sflorian 			break;
851d3ff3477Sflorian 		if (rti_info[RTAX_GATEWAY]->sa_family != AF_INET6)
852d3ff3477Sflorian 			break;
853d3ff3477Sflorian 		if (rti_info[RTAX_LABEL]->sa_len !=
854d3ff3477Sflorian 		    sizeof(struct sockaddr_rtlabel))
855d3ff3477Sflorian 			break;
856d3ff3477Sflorian 
857d3ff3477Sflorian 		rl = (struct sockaddr_rtlabel *)rti_info[RTAX_LABEL];
858d3ff3477Sflorian 		if (strcmp(rl->sr_label, SLAACD_RTA_LABEL) != 0)
859d3ff3477Sflorian 			break;
860d0c2b6e3Sflorian 		if_index = ifm->ifm_index;
861d0c2b6e3Sflorian 		if_name = if_indextoname(if_index, ifnamebuf);
8627f8b5cc9Sflorian 		if (if_name == NULL) {
8637f8b5cc9Sflorian 			log_debug("RTM_DELETE: lost if %d", if_index);
8647f8b5cc9Sflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
8657f8b5cc9Sflorian 			    &if_index, sizeof(if_index));
8667f8b5cc9Sflorian 			remove_iface(if_index);
8677f8b5cc9Sflorian 			break;
8687f8b5cc9Sflorian 		}
869d3ff3477Sflorian 
870d0c2b6e3Sflorian 		del_route.if_index = if_index;
871d3ff3477Sflorian 		memcpy(&del_route.gw, rti_info[RTAX_GATEWAY],
872d3ff3477Sflorian 		    sizeof(del_route.gw));
873d3ff3477Sflorian 		in6 = &del_route.gw.sin6_addr;
874939e6f5eSflorian #ifdef __KAME__
875d3ff3477Sflorian 		/* XXX from route(8) p_sockaddr() */
876a5c74c4bSflorian 		if ((IN6_IS_ADDR_LINKLOCAL(in6) ||
877d3ff3477Sflorian 		    IN6_IS_ADDR_MC_LINKLOCAL(in6) ||
878a5c74c4bSflorian 		    IN6_IS_ADDR_MC_INTFACELOCAL(in6)) &&
879a5c74c4bSflorian 		    del_route.gw.sin6_scope_id == 0) {
880d3ff3477Sflorian 			del_route.gw.sin6_scope_id =
881d3ff3477Sflorian 			    (u_int32_t)ntohs(*(u_short *) &in6->s6_addr[2]);
882d3ff3477Sflorian 			*(u_short *)&in6->s6_addr[2] = 0;
883d3ff3477Sflorian 		}
884939e6f5eSflorian #endif
885d3ff3477Sflorian 		frontend_imsg_compose_engine(IMSG_DEL_ROUTE,
886d3ff3477Sflorian 		    0, 0, &del_route, sizeof(del_route));
887d3ff3477Sflorian 		log_debug("RTM_DELETE: %s[%u]", if_name,
888d3ff3477Sflorian 		    ifm->ifm_index);
889d3ff3477Sflorian 
890d3ff3477Sflorian 		break;
89134435150Sflorian #ifndef	SMALL
89234435150Sflorian 	case RTM_PROPOSAL:
89334435150Sflorian 		if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) {
89434435150Sflorian 			log_debug("RTP_PROPOSAL_SOLICIT");
89534435150Sflorian 			frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS,
89634435150Sflorian 			    0, 0, NULL, 0);
89734435150Sflorian 		}
89834435150Sflorian 		break;
89934435150Sflorian #endif	/* SMALL */
9000acf3e2dSflorian 	default:
9010acf3e2dSflorian 		log_debug("unexpected RTM: %d", rtm->rtm_type);
9020acf3e2dSflorian 		break;
9030acf3e2dSflorian 	}
9040acf3e2dSflorian 
9050acf3e2dSflorian }
9060acf3e2dSflorian 
9070acf3e2dSflorian #define ROUNDUP(a) \
908064707deSfriehm 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
9090acf3e2dSflorian 
9100acf3e2dSflorian void
9110acf3e2dSflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
9120acf3e2dSflorian {
9130acf3e2dSflorian 	int	i;
9140acf3e2dSflorian 
9150acf3e2dSflorian 	for (i = 0; i < RTAX_MAX; i++) {
9160acf3e2dSflorian 		if (addrs & (1 << i)) {
9170acf3e2dSflorian 			rti_info[i] = sa;
9180acf3e2dSflorian 			sa = (struct sockaddr *)((char *)(sa) +
9190acf3e2dSflorian 			    ROUNDUP(sa->sa_len));
9200acf3e2dSflorian 		} else
9210acf3e2dSflorian 			rti_info[i] = NULL;
9220acf3e2dSflorian 	}
9230acf3e2dSflorian }
9240acf3e2dSflorian 
9250acf3e2dSflorian void
9260acf3e2dSflorian icmp6_receive(int fd, short events, void *arg)
9270acf3e2dSflorian {
9280acf3e2dSflorian 	struct imsg_ra		 ra;
929dd19964dSflorian 	struct icmp6_hdr	*icmp6_hdr;
930dd19964dSflorian 	struct icmp6_ev		*icmp6ev;
9310acf3e2dSflorian 	struct in6_pktinfo	*pi = NULL;
9320acf3e2dSflorian 	struct cmsghdr		*cm;
9330acf3e2dSflorian 	ssize_t			 len;
9340acf3e2dSflorian 	int			 if_index = 0, *hlimp = NULL;
935e56e9441Skn 	char			 ntopbuf[INET6_ADDRSTRLEN];
936e56e9441Skn #ifndef SMALL
937e56e9441Skn 	char			 ifnamebuf[IFNAMSIZ];
938e56e9441Skn #endif	/* SMALL */
9390acf3e2dSflorian 
940dd19964dSflorian 	icmp6ev = arg;
941dd19964dSflorian 	if ((len = recvmsg(fd, &icmp6ev->rcvmhdr, 0)) == -1) {
9420acf3e2dSflorian 		log_warn("recvmsg");
9430acf3e2dSflorian 		return;
9440acf3e2dSflorian 	}
9450acf3e2dSflorian 
94612ce2480Ssemarie 	if ((size_t)len < sizeof(struct icmp6_hdr))
94712ce2480Ssemarie 		return;
94812ce2480Ssemarie 
94912ce2480Ssemarie 	icmp6_hdr = (struct icmp6_hdr *)icmp6ev->answer;
95012ce2480Ssemarie 	if (icmp6_hdr->icmp6_type != ND_ROUTER_ADVERT)
95112ce2480Ssemarie 		return;
95212ce2480Ssemarie 
9530acf3e2dSflorian 	/* extract optional information via Advanced API */
954dd19964dSflorian 	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&icmp6ev->rcvmhdr); cm;
955dd19964dSflorian 	    cm = (struct cmsghdr *)CMSG_NXTHDR(&icmp6ev->rcvmhdr, cm)) {
9560acf3e2dSflorian 		if (cm->cmsg_level == IPPROTO_IPV6 &&
9570acf3e2dSflorian 		    cm->cmsg_type == IPV6_PKTINFO &&
9580acf3e2dSflorian 		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
9590acf3e2dSflorian 			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
9600acf3e2dSflorian 			if_index = pi->ipi6_ifindex;
9610acf3e2dSflorian 		}
9620acf3e2dSflorian 		if (cm->cmsg_level == IPPROTO_IPV6 &&
9630acf3e2dSflorian 		    cm->cmsg_type == IPV6_HOPLIMIT &&
9640acf3e2dSflorian 		    cm->cmsg_len == CMSG_LEN(sizeof(int)))
9650acf3e2dSflorian 			hlimp = (int *)CMSG_DATA(cm);
9660acf3e2dSflorian 	}
9670acf3e2dSflorian 
9680acf3e2dSflorian 	if (if_index == 0) {
9690acf3e2dSflorian 		log_warnx("failed to get receiving interface");
9700acf3e2dSflorian 		return;
9710acf3e2dSflorian 	}
9720acf3e2dSflorian 
9730acf3e2dSflorian 	if (hlimp == NULL) {
9740acf3e2dSflorian 		log_warnx("failed to get receiving hop limit");
9750acf3e2dSflorian 		return;
9760acf3e2dSflorian 	}
9770acf3e2dSflorian 
9780acf3e2dSflorian 	if (*hlimp != 255) {
9792ac62a75Sflorian 		log_warnx("invalid RA with hop limit of %d from %s on %s",
980dd19964dSflorian 		    *hlimp, inet_ntop(AF_INET6, &icmp6ev->from.sin6_addr,
9810acf3e2dSflorian 		    ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
9820acf3e2dSflorian 		    ifnamebuf));
9830acf3e2dSflorian 		return;
9840acf3e2dSflorian 	}
9850acf3e2dSflorian 
9860acf3e2dSflorian 	if ((size_t)len > sizeof(ra.packet)) {
9872ac62a75Sflorian 		log_warnx("invalid RA with size %ld from %s on %s",
988dd19964dSflorian 		    len, inet_ntop(AF_INET6, &icmp6ev->from.sin6_addr,
9890acf3e2dSflorian 		    ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
9900acf3e2dSflorian 		    ifnamebuf));
9910acf3e2dSflorian 		return;
9920acf3e2dSflorian 	}
9930acf3e2dSflorian 	ra.if_index = if_index;
994dd19964dSflorian 	memcpy(&ra.from,  &icmp6ev->from, sizeof(ra.from));
9950acf3e2dSflorian 	ra.len = len;
996dd19964dSflorian 	memcpy(ra.packet, icmp6ev->answer, len);
9970acf3e2dSflorian 
9980acf3e2dSflorian 	frontend_imsg_compose_engine(IMSG_RA, 0, 0, &ra, sizeof(ra));
9990acf3e2dSflorian }
10000acf3e2dSflorian 
10010acf3e2dSflorian void
10020acf3e2dSflorian send_solicitation(uint32_t if_index)
10030acf3e2dSflorian {
10040acf3e2dSflorian 	struct in6_pktinfo	*pi;
10050acf3e2dSflorian 	struct cmsghdr		*cm;
1006dd19964dSflorian 	struct iface		*iface;
10070acf3e2dSflorian 
10080acf3e2dSflorian 	log_debug("%s(%u)", __func__, if_index);
10090acf3e2dSflorian 
1010dd19964dSflorian 	if ((iface = get_iface_by_id(if_index)) == NULL)
1011411eeab6Sflorian 		return;
1012411eeab6Sflorian 
1013dd19964dSflorian 	if (!event_initialized(&iface->icmp6ev->ev)) {
1014dd19964dSflorian 		iface->send_solicitation = 1;
1015dd19964dSflorian 		return;
101611cc0cd9Sflorian 	} else if (iface->ll_tentative) {
101711cc0cd9Sflorian 		iface->send_solicitation = 1;
101811cc0cd9Sflorian 		return;
1019dd19964dSflorian 	}
1020411eeab6Sflorian 
102111cc0cd9Sflorian 	iface->send_solicitation = 0;
102211cc0cd9Sflorian 
10230acf3e2dSflorian 	dst.sin6_scope_id = if_index;
10240acf3e2dSflorian 
10250acf3e2dSflorian 	cm = CMSG_FIRSTHDR(&sndmhdr);
10260acf3e2dSflorian 	pi = (struct in6_pktinfo *)CMSG_DATA(cm);
10270acf3e2dSflorian 	pi->ipi6_ifindex = if_index;
10280acf3e2dSflorian 
1029dd19964dSflorian 	memcpy(&nd_opt_source_link_addr, &iface->hw_address,
1030dd19964dSflorian 	    sizeof(nd_opt_source_link_addr));
1031dd19964dSflorian 
1032dd19964dSflorian 	if (sendmsg(EVENT_FD(&iface->icmp6ev->ev), &sndmhdr, 0) != sizeof(rs) +
10330acf3e2dSflorian 	    sizeof(nd_opt_hdr) + sizeof(nd_opt_source_link_addr))
10340acf3e2dSflorian 		log_warn("sendmsg");
10350acf3e2dSflorian }
1036dd19964dSflorian 
1037dd19964dSflorian struct iface*
1038dd19964dSflorian get_iface_by_id(uint32_t if_index)
1039dd19964dSflorian {
1040dd19964dSflorian 	struct iface	*iface;
1041dd19964dSflorian 
1042dd19964dSflorian 	LIST_FOREACH (iface, &interfaces, entries) {
1043dd19964dSflorian 		if (iface->if_index == if_index)
1044dd19964dSflorian 			return (iface);
1045dd19964dSflorian 	}
1046dd19964dSflorian 
1047dd19964dSflorian 	return (NULL);
1048dd19964dSflorian }
1049dd19964dSflorian 
1050dd19964dSflorian void
1051dd19964dSflorian remove_iface(uint32_t if_index)
1052dd19964dSflorian {
1053dd19964dSflorian 	struct iface	*iface;
1054dd19964dSflorian 
1055dd19964dSflorian 	iface = get_iface_by_id(if_index);
1056dd19964dSflorian 
1057dd19964dSflorian 	if (iface == NULL)
1058dd19964dSflorian 		return;
1059dd19964dSflorian 
1060dd19964dSflorian 	LIST_REMOVE(iface, entries);
1061dd19964dSflorian 
1062eaf8d593Sflorian 	unref_icmp6ev(iface);
1063dd19964dSflorian 	free(iface);
1064dd19964dSflorian }
1065dd19964dSflorian 
1066dd19964dSflorian struct icmp6_ev*
1067dd19964dSflorian get_icmp6ev_by_rdomain(int rdomain)
1068dd19964dSflorian {
1069dd19964dSflorian 	struct iface	*iface;
1070d4a6ab96Sflorian 	struct icmp6_ev	*icmp6ev = NULL;
1071dd19964dSflorian 
1072dd19964dSflorian 	LIST_FOREACH (iface, &interfaces, entries) {
1073d4a6ab96Sflorian 		if (iface->rdomain == rdomain) {
1074d4a6ab96Sflorian 			icmp6ev = iface->icmp6ev;
1075d4a6ab96Sflorian 			break;
1076d4a6ab96Sflorian 		}
1077dd19964dSflorian 	}
1078dd19964dSflorian 
1079d4a6ab96Sflorian 	if (icmp6ev == NULL) {
1080d4a6ab96Sflorian 		if ((icmp6ev = calloc(1, sizeof(*icmp6ev))) == NULL)
1081d4a6ab96Sflorian 			fatal("calloc");
1082d4a6ab96Sflorian 		icmp6ev->rcviov[0].iov_base = (caddr_t)icmp6ev->answer;
1083d4a6ab96Sflorian 		icmp6ev->rcviov[0].iov_len = sizeof(icmp6ev->answer);
1084d4a6ab96Sflorian 		icmp6ev->rcvmhdr.msg_name = (caddr_t)&icmp6ev->from;
1085d4a6ab96Sflorian 		icmp6ev->rcvmhdr.msg_namelen = sizeof(icmp6ev->from);
1086d4a6ab96Sflorian 		icmp6ev->rcvmhdr.msg_iov = icmp6ev->rcviov;
1087d4a6ab96Sflorian 		icmp6ev->rcvmhdr.msg_iovlen = 1;
1088d4a6ab96Sflorian 		icmp6ev->rcvmhdr.msg_controllen =
1089d4a6ab96Sflorian 		    CMSG_SPACE(sizeof(struct in6_pktinfo)) +
1090d4a6ab96Sflorian 		    CMSG_SPACE(sizeof(int));
1091d4a6ab96Sflorian 		if ((icmp6ev->rcvmhdr.msg_control = malloc(icmp6ev->
1092d4a6ab96Sflorian 		    rcvmhdr.msg_controllen)) == NULL)
1093d4a6ab96Sflorian 			fatal("malloc");
1094d4a6ab96Sflorian 		frontend_imsg_compose_main(IMSG_OPEN_ICMP6SOCK, 0,
1095d4a6ab96Sflorian 		    &rdomain, sizeof(rdomain));
1096d4a6ab96Sflorian 	}
1097d4a6ab96Sflorian 	icmp6ev->refcnt++;
1098d4a6ab96Sflorian 	return (icmp6ev);
1099dd19964dSflorian }
1100dd19964dSflorian 
1101dd19964dSflorian void
1102eaf8d593Sflorian unref_icmp6ev(struct iface *iface)
1103eaf8d593Sflorian {
11049a9a88f5Snaddy 	struct icmp6_ev *icmp6ev = iface->icmp6ev;
11059a9a88f5Snaddy 
1106c552023eSflorian 	iface->icmp6ev = NULL;
1107c552023eSflorian 
11089a9a88f5Snaddy 	if (icmp6ev != NULL) {
11099a9a88f5Snaddy 		icmp6ev->refcnt--;
11109a9a88f5Snaddy 		if (icmp6ev->refcnt == 0) {
11119a9a88f5Snaddy 			event_del(&icmp6ev->ev);
11129a9a88f5Snaddy 			close(EVENT_FD(&icmp6ev->ev));
11139a9a88f5Snaddy 			free(icmp6ev);
1114eaf8d593Sflorian 		}
1115eaf8d593Sflorian 	}
1116eaf8d593Sflorian }
1117eaf8d593Sflorian 
1118eaf8d593Sflorian void
1119dd19964dSflorian set_icmp6sock(int icmp6sock, int rdomain)
1120dd19964dSflorian {
1121dd19964dSflorian 	struct iface	*iface;
1122dd19964dSflorian 
1123dd19964dSflorian 	LIST_FOREACH (iface, &interfaces, entries) {
1124dd19964dSflorian 		if (!event_initialized(&iface->icmp6ev->ev) && iface->rdomain
1125dd19964dSflorian 		    == rdomain) {
1126dd19964dSflorian 			event_set(&iface->icmp6ev->ev, icmp6sock, EV_READ |
1127dd19964dSflorian 			    EV_PERSIST, icmp6_receive, iface->icmp6ev);
1128dd19964dSflorian 			event_add(&iface->icmp6ev->ev, NULL);
1129dd19964dSflorian 			icmp6sock = -1;
1130dd19964dSflorian 			break;
1131dd19964dSflorian 		}
1132dd19964dSflorian 	}
1133dd19964dSflorian 
1134909dc701Sflorian 	if (icmp6sock != -1) {
1135909dc701Sflorian 		/*
1136909dc701Sflorian 		 * The interface disappeared or changed rdomain while we were
1137909dc701Sflorian 		 * waiting for the parent process to open the raw socket.
1138909dc701Sflorian 		 */
1139909dc701Sflorian 		close(icmp6sock);
1140909dc701Sflorian 		return;
1141909dc701Sflorian 	}
1142dd19964dSflorian 
1143dd19964dSflorian 	LIST_FOREACH (iface, &interfaces, entries) {
1144dd19964dSflorian 		if (event_initialized(&iface->icmp6ev->ev) &&
114511cc0cd9Sflorian 		    iface->send_solicitation)
1146dd19964dSflorian 			send_solicitation(iface->if_index);
1147dd19964dSflorian 	}
1148dd19964dSflorian }
1149