xref: /openbsd-src/sbin/slaacd/frontend.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: frontend.c,v 1.43 2020/12/29 19:51:15 benno Exp $	*/
2 
3 /*
4  * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
5  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
6  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
7  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #include <sys/queue.h>
24 #include <sys/socket.h>
25 #include <sys/syslog.h>
26 #include <sys/uio.h>
27 
28 #include <net/if.h>
29 #include <net/if_dl.h>
30 #include <net/if_types.h>
31 #include <net/route.h>
32 
33 #include <arpa/inet.h>
34 
35 #include <netinet/in.h>
36 #include <netinet/if_ether.h>
37 #include <netinet6/nd6.h>
38 #include <netinet6/in6_var.h>
39 #include <netinet/ip6.h>
40 #include <netinet6/ip6_var.h>
41 #include <netinet/icmp6.h>
42 
43 #include <errno.h>
44 #include <event.h>
45 #include <ifaddrs.h>
46 #include <imsg.h>
47 #include <pwd.h>
48 #include <signal.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 
54 #include "log.h"
55 #include "slaacd.h"
56 #include "frontend.h"
57 #include "control.h"
58 
59 #define	ROUTE_SOCKET_BUF_SIZE	16384
60 #define	ALLROUTER		"ff02::2"
61 
62 struct icmp6_ev {
63 	struct event		 ev;
64 	uint8_t			 answer[1500];
65 	struct msghdr		 rcvmhdr;
66 	struct iovec		 rcviov[1];
67 	struct sockaddr_in6	 from;
68 	int			 refcnt;
69 };
70 
71 struct iface		{
72 	LIST_ENTRY(iface)	 entries;
73 	struct icmp6_ev		*icmp6ev;
74 	struct ether_addr	 hw_address;
75 	uint32_t		 if_index;
76 	int			 rdomain;
77 	int			 send_solicitation;
78 };
79 
80 __dead void	 frontend_shutdown(void);
81 void		 frontend_sig_handler(int, short, void *);
82 void		 update_iface(uint32_t, char*);
83 void		 frontend_startup(void);
84 void		 route_receive(int, short, void *);
85 void		 handle_route_message(struct rt_msghdr *, struct sockaddr **);
86 void		 get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
87 void		 icmp6_receive(int, short, void *);
88 int		 get_flags(char *);
89 int		 get_xflags(char *);
90 int		 get_ifrdomain(char *);
91 void		 get_lladdr(char *, struct ether_addr *, struct sockaddr_in6 *);
92 struct iface	*get_iface_by_id(uint32_t);
93 void		 remove_iface(uint32_t);
94 struct icmp6_ev	*get_icmp6ev_by_rdomain(int);
95 void		 unref_icmp6ev(struct iface *);
96 void		 set_icmp6sock(int, int);
97 void		 send_solicitation(uint32_t);
98 #ifndef	SMALL
99 void		 update_autoconf_addresses(uint32_t, char*);
100 const char	*flags_to_str(int);
101 #endif	/* SMALL */
102 
103 LIST_HEAD(, iface)		 interfaces;
104 struct imsgev			*iev_main;
105 struct imsgev			*iev_engine;
106 struct event			 ev_route;
107 struct msghdr			 sndmhdr;
108 struct iovec			 sndiov[4];
109 struct nd_router_solicit	 rs;
110 struct nd_opt_hdr		 nd_opt_hdr;
111 struct ether_addr		 nd_opt_source_link_addr;
112 struct sockaddr_in6		 dst;
113 int				 ioctlsock;
114 
115 void
116 frontend_sig_handler(int sig, short event, void *bula)
117 {
118 	/*
119 	 * Normal signal handler rules don't apply because libevent
120 	 * decouples for us.
121 	 */
122 
123 	switch (sig) {
124 	case SIGINT:
125 	case SIGTERM:
126 		frontend_shutdown();
127 	default:
128 		fatalx("unexpected signal");
129 	}
130 }
131 
132 void
133 frontend(int debug, int verbose)
134 {
135 	struct event		 ev_sigint, ev_sigterm;
136 	struct passwd		*pw;
137 	struct in6_pktinfo	*pi;
138 	struct cmsghdr		*cm;
139 	size_t			 sndcmsglen;
140 	int			 hoplimit = 255;
141 	uint8_t			*sndcmsgbuf;
142 
143 	log_init(debug, LOG_DAEMON);
144 	log_setverbose(verbose);
145 
146 #ifndef	SMALL
147 	control_state.fd = -1;
148 #endif	/* SMALL */
149 
150 	if ((pw = getpwnam(SLAACD_USER)) == NULL)
151 		fatal("getpwnam");
152 
153 	if (chroot(pw->pw_dir) == -1)
154 		fatal("chroot");
155 	if (chdir("/") == -1)
156 		fatal("chdir(\"/\")");
157 
158 	slaacd_process = PROC_FRONTEND;
159 	setproctitle("%s", log_procnames[slaacd_process]);
160 	log_procinit(log_procnames[slaacd_process]);
161 
162 	if ((ioctlsock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
163 		fatal("socket");
164 
165 	if (setgroups(1, &pw->pw_gid) ||
166 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
167 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
168 		fatal("can't drop privileges");
169 
170 	if (pledge("stdio unix recvfd route", NULL) == -1)
171 		fatal("pledge");
172 
173 	event_init();
174 
175 	/* Setup signal handler. */
176 	signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
177 	signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
178 	signal_add(&ev_sigint, NULL);
179 	signal_add(&ev_sigterm, NULL);
180 	signal(SIGPIPE, SIG_IGN);
181 	signal(SIGHUP, SIG_IGN);
182 
183 	/* Setup pipe and event handler to the parent process. */
184 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
185 		fatal(NULL);
186 	imsg_init(&iev_main->ibuf, 3);
187 	iev_main->handler = frontend_dispatch_main;
188 	iev_main->events = EV_READ;
189 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
190 	    iev_main->handler, iev_main);
191 	event_add(&iev_main->ev, NULL);
192 
193 	sndcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
194 	    CMSG_SPACE(sizeof(int));
195 
196 	if ((sndcmsgbuf = malloc(sndcmsglen)) == NULL)
197 		fatal("malloc");
198 
199 	rs.nd_rs_type = ND_ROUTER_SOLICIT;
200 	rs.nd_rs_code = 0;
201 	rs.nd_rs_cksum = 0;
202 	rs.nd_rs_reserved = 0;
203 
204 	nd_opt_hdr.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
205 	nd_opt_hdr.nd_opt_len = 1;
206 
207 	memset(&dst, 0, sizeof(dst));
208 	dst.sin6_family = AF_INET6;
209 	if (inet_pton(AF_INET6, ALLROUTER, &dst.sin6_addr.s6_addr) != 1)
210 		fatal("inet_pton");
211 
212 	sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
213 	sndmhdr.msg_iov = sndiov;
214 	sndmhdr.msg_iovlen = 3;
215 	sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
216 	sndmhdr.msg_controllen = sndcmsglen;
217 
218 	sndmhdr.msg_name = (caddr_t)&dst;
219 	sndmhdr.msg_iov[0].iov_base = (caddr_t)&rs;
220 	sndmhdr.msg_iov[0].iov_len = sizeof(rs);
221 	sndmhdr.msg_iov[1].iov_base = (caddr_t)&nd_opt_hdr;
222 	sndmhdr.msg_iov[1].iov_len = sizeof(nd_opt_hdr);
223 	sndmhdr.msg_iov[2].iov_base = (caddr_t)&nd_opt_source_link_addr;
224 	sndmhdr.msg_iov[2].iov_len = sizeof(nd_opt_source_link_addr);
225 
226 	cm = CMSG_FIRSTHDR(&sndmhdr);
227 
228 	cm->cmsg_level = IPPROTO_IPV6;
229 	cm->cmsg_type = IPV6_PKTINFO;
230 	cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
231 	pi = (struct in6_pktinfo *)CMSG_DATA(cm);
232 	memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));
233 	pi->ipi6_ifindex = 0;
234 
235 	cm = CMSG_NXTHDR(&sndmhdr, cm);
236 	cm->cmsg_level = IPPROTO_IPV6;
237 	cm->cmsg_type = IPV6_HOPLIMIT;
238 	cm->cmsg_len = CMSG_LEN(sizeof(int));
239 	memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
240 
241 	LIST_INIT(&interfaces);
242 
243 	event_dispatch();
244 
245 	frontend_shutdown();
246 }
247 
248 __dead void
249 frontend_shutdown(void)
250 {
251 	/* Close pipes. */
252 	msgbuf_write(&iev_engine->ibuf.w);
253 	msgbuf_clear(&iev_engine->ibuf.w);
254 	close(iev_engine->ibuf.fd);
255 	msgbuf_write(&iev_main->ibuf.w);
256 	msgbuf_clear(&iev_main->ibuf.w);
257 	close(iev_main->ibuf.fd);
258 
259 	free(iev_engine);
260 	free(iev_main);
261 
262 	log_info("frontend exiting");
263 	exit(0);
264 }
265 
266 int
267 frontend_imsg_compose_main(int type, pid_t pid, void *data,
268     uint16_t datalen)
269 {
270 	return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
271 	    datalen));
272 }
273 
274 int
275 frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid,
276     void *data, uint16_t datalen)
277 {
278 	return (imsg_compose_event(iev_engine, type, peerid, pid, -1,
279 	    data, datalen));
280 }
281 
282 void
283 frontend_dispatch_main(int fd, short event, void *bula)
284 {
285 	struct imsg		 imsg;
286 	struct imsgev		*iev = bula;
287 	struct imsgbuf		*ibuf = &iev->ibuf;
288 	ssize_t			 n;
289 	int			 shut = 0, icmp6sock, rdomain;
290 
291 	if (event & EV_READ) {
292 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
293 			fatal("imsg_read error");
294 		if (n == 0)	/* Connection closed. */
295 			shut = 1;
296 	}
297 	if (event & EV_WRITE) {
298 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
299 			fatal("msgbuf_write");
300 		if (n == 0)	/* Connection closed. */
301 			shut = 1;
302 	}
303 
304 	for (;;) {
305 		if ((n = imsg_get(ibuf, &imsg)) == -1)
306 			fatal("%s: imsg_get error", __func__);
307 		if (n == 0)	/* No more messages. */
308 			break;
309 
310 		switch (imsg.hdr.type) {
311 		case IMSG_SOCKET_IPC:
312 			/*
313 			 * Setup pipe and event handler to the engine
314 			 * process.
315 			 */
316 			if (iev_engine)
317 				fatalx("%s: received unexpected imsg fd "
318 				    "to frontend", __func__);
319 
320 			if ((fd = imsg.fd) == -1)
321 				fatalx("%s: expected to receive imsg fd to "
322 				   "frontend but didn't receive any",
323 				   __func__);
324 
325 			iev_engine = malloc(sizeof(struct imsgev));
326 			if (iev_engine == NULL)
327 				fatal(NULL);
328 
329 			imsg_init(&iev_engine->ibuf, fd);
330 			iev_engine->handler = frontend_dispatch_engine;
331 			iev_engine->events = EV_READ;
332 
333 			event_set(&iev_engine->ev, iev_engine->ibuf.fd,
334 			iev_engine->events, iev_engine->handler, iev_engine);
335 			event_add(&iev_engine->ev, NULL);
336 			break;
337 		case IMSG_ICMP6SOCK:
338 			if ((icmp6sock = imsg.fd) == -1)
339 				fatalx("%s: expected to receive imsg "
340 				    "ICMPv6 fd but didn't receive any",
341 				    __func__);
342 			if (IMSG_DATA_SIZE(imsg) != sizeof(rdomain))
343 				fatalx("%s: IMSG_ICMP6SOCK wrong length: "
344 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
345 			memcpy(&rdomain, imsg.data, sizeof(rdomain));
346 			set_icmp6sock(icmp6sock, rdomain);
347 			break;
348 		case IMSG_ROUTESOCK:
349 			if ((fd = imsg.fd) == -1)
350 				fatalx("%s: expected to receive imsg "
351 				    "routesocket fd but didn't receive any",
352 				    __func__);
353 			event_set(&ev_route, fd, EV_READ | EV_PERSIST,
354 			    route_receive, NULL);
355 			break;
356 		case IMSG_STARTUP:
357 			frontend_startup();
358 			break;
359 #ifndef	SMALL
360 		case IMSG_CONTROLFD:
361 			if (control_state.fd != -1)
362 				fatalx("%s: received unexpected controlsock",
363 				    __func__);
364 			if ((fd = imsg.fd) == -1)
365 				fatalx("%s: expected to receive imsg "
366 				    "control fd but didn't receive any",
367 				    __func__);
368 			control_state.fd = fd;
369 			/* Listen on control socket. */
370 			TAILQ_INIT(&ctl_conns);
371 			control_listen();
372 			break;
373 		case IMSG_CTL_END:
374 			control_imsg_relay(&imsg);
375 			break;
376 #endif	/* SMALL */
377 		default:
378 			log_debug("%s: error handling imsg %d", __func__,
379 			    imsg.hdr.type);
380 			break;
381 		}
382 		imsg_free(&imsg);
383 	}
384 	if (!shut)
385 		imsg_event_add(iev);
386 	else {
387 		/* This pipe is dead. Remove its event handler. */
388 		event_del(&iev->ev);
389 		event_loopexit(NULL);
390 	}
391 }
392 
393 void
394 frontend_dispatch_engine(int fd, short event, void *bula)
395 {
396 	struct imsgev		*iev = bula;
397 	struct imsgbuf		*ibuf = &iev->ibuf;
398 	struct imsg		 imsg;
399 	ssize_t			 n;
400 	int			 shut = 0;
401 	uint32_t		 if_index;
402 
403 	if (event & EV_READ) {
404 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
405 			fatal("imsg_read error");
406 		if (n == 0)	/* Connection closed. */
407 			shut = 1;
408 	}
409 	if (event & EV_WRITE) {
410 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
411 			fatal("msgbuf_write");
412 		if (n == 0)	/* Connection closed. */
413 			shut = 1;
414 	}
415 
416 	for (;;) {
417 		if ((n = imsg_get(ibuf, &imsg)) == -1)
418 			fatal("%s: imsg_get error", __func__);
419 		if (n == 0)	/* No more messages. */
420 			break;
421 
422 		switch (imsg.hdr.type) {
423 #ifndef	SMALL
424 		case IMSG_CTL_END:
425 		case IMSG_CTL_SHOW_INTERFACE_INFO:
426 		case IMSG_CTL_SHOW_INTERFACE_INFO_RA:
427 		case IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX:
428 		case IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS:
429 		case IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL:
430 		case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS:
431 		case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL:
432 		case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS:
433 		case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL:
434 		case IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSALS:
435 		case IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSAL:
436 			control_imsg_relay(&imsg);
437 			break;
438 #endif	/* SMALL */
439 		case IMSG_CTL_SEND_SOLICITATION:
440 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
441 				fatalx("%s: IMSG_CTL_SEND_SOLICITATION wrong "
442 				    "length: %lu", __func__,
443 				    IMSG_DATA_SIZE(imsg));
444 			if_index = *((uint32_t *)imsg.data);
445 			send_solicitation(if_index);
446 			break;
447 		default:
448 			log_debug("%s: error handling imsg %d", __func__,
449 			    imsg.hdr.type);
450 			break;
451 		}
452 		imsg_free(&imsg);
453 	}
454 	if (!shut)
455 		imsg_event_add(iev);
456 	else {
457 		/* This pipe is dead. Remove its event handler. */
458 		event_del(&iev->ev);
459 		event_loopexit(NULL);
460 	}
461 }
462 
463 int
464 get_flags(char *if_name)
465 {
466 	struct ifreq		 ifr;
467 
468 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
469 	if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
470 		log_warn("SIOCGIFFLAGS");
471 		return -1;
472 	}
473 	return ifr.ifr_flags;
474 }
475 
476 int
477 get_xflags(char *if_name)
478 {
479 	struct ifreq		 ifr;
480 
481 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
482 	if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) {
483 		log_warn("SIOCGIFXFLAGS");
484 		return -1;
485 	}
486 	return ifr.ifr_flags;
487 }
488 
489 int
490 get_ifrdomain(char *if_name)
491 {
492 	struct ifreq		 ifr;
493 
494 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
495 	if (ioctl(ioctlsock, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1) {
496 		log_warn("SIOCGIFRDOMAIN");
497 		return -1;
498 	}
499 	return ifr.ifr_rdomainid;
500 }
501 
502 void
503 update_iface(uint32_t if_index, char* if_name)
504 {
505 	struct iface		*iface;
506 	struct imsg_ifinfo	 imsg_ifinfo;
507 	int			 flags, xflags, ifrdomain;
508 
509 	if ((flags = get_flags(if_name)) == -1 || (xflags =
510 	    get_xflags(if_name)) == -1)
511 		return;
512 
513 	if (!(xflags & IFXF_AUTOCONF6))
514 		return;
515 
516 	if((ifrdomain = get_ifrdomain(if_name)) == -1)
517 		return;
518 
519 	iface = get_iface_by_id(if_index);
520 
521 	if (iface != NULL) {
522 		if (iface->rdomain != ifrdomain) {
523 			unref_icmp6ev(iface);
524 			iface->rdomain = ifrdomain;
525 			iface->icmp6ev = get_icmp6ev_by_rdomain(ifrdomain);
526 		}
527 	} else {
528 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
529 			fatal("calloc");
530 		iface->if_index = if_index;
531 		iface->rdomain = ifrdomain;
532 		iface->icmp6ev = get_icmp6ev_by_rdomain(ifrdomain);
533 
534 		LIST_INSERT_HEAD(&interfaces, iface, entries);
535 	}
536 
537 	memset(&imsg_ifinfo, 0, sizeof(imsg_ifinfo));
538 
539 	imsg_ifinfo.if_index = if_index;
540 	imsg_ifinfo.rdomain = ifrdomain;
541 	imsg_ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP |
542 	    IFF_RUNNING);
543 	imsg_ifinfo.autoconfprivacy = !(xflags & IFXF_INET6_NOPRIVACY);
544 	imsg_ifinfo.soii = !(xflags & IFXF_INET6_NOSOII);
545 	get_lladdr(if_name, &imsg_ifinfo.hw_address, &imsg_ifinfo.ll_address);
546 
547 	memcpy(&iface->hw_address, &imsg_ifinfo.hw_address,
548 	    sizeof(iface->hw_address));
549 
550 	frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &imsg_ifinfo,
551 	    sizeof(imsg_ifinfo));
552 }
553 
554 #ifndef	SMALL
555 void
556 update_autoconf_addresses(uint32_t if_index, char* if_name)
557 {
558 	struct in6_ifreq	 ifr6;
559 	struct imsg_addrinfo	 imsg_addrinfo;
560 	struct ifaddrs		*ifap, *ifa;
561 	struct in6_addrlifetime *lifetime;
562 	struct sockaddr_in6	*sin6;
563 	struct imsg_link_state	 imsg_link_state;
564 	time_t			 t;
565 	int			 xflags;
566 
567 	if ((xflags = get_xflags(if_name)) == -1)
568 		return;
569 
570 	if (!(xflags & IFXF_AUTOCONF6))
571 		return;
572 
573 	memset(&imsg_addrinfo, 0, sizeof(imsg_addrinfo));
574 	imsg_addrinfo.if_index = if_index;
575 	get_lladdr(if_name, &imsg_addrinfo.hw_address,
576 	    &imsg_addrinfo.ll_address);
577 
578 	memset(&imsg_link_state, 0, sizeof(imsg_link_state));
579 	imsg_link_state.if_index = if_index;
580 
581 	if (getifaddrs(&ifap) != 0)
582 		fatal("getifaddrs");
583 
584 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
585 		if (strcmp(if_name, ifa->ifa_name) != 0)
586 			continue;
587 		if (ifa->ifa_addr == NULL)
588 			continue;
589 
590 		if (ifa->ifa_addr->sa_family == AF_LINK)
591 			imsg_link_state.link_state =
592 			    ((struct if_data *)ifa->ifa_data)->ifi_link_state;
593 
594 		if (ifa->ifa_addr->sa_family != AF_INET6)
595 			continue;
596 		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
597 		if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
598 			continue;
599 
600 		log_debug("%s: IP: %s", __func__, sin6_to_str(sin6));
601 		imsg_addrinfo.addr = *sin6;
602 
603 		memset(&ifr6, 0, sizeof(ifr6));
604 		strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name));
605 		memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr));
606 
607 		if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6, (caddr_t)&ifr6) == -1) {
608 			log_warn("SIOCGIFAFLAG_IN6");
609 			continue;
610 		}
611 
612 		if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF |
613 		    IN6_IFF_TEMPORARY)))
614 			continue;
615 
616 		imsg_addrinfo.privacy = ifr6.ifr_ifru.ifru_flags6 &
617 		    IN6_IFF_TEMPORARY ? 1 : 0;
618 
619 		memset(&ifr6, 0, sizeof(ifr6));
620 		strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name));
621 		memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr));
622 
623 		if (ioctl(ioctlsock, SIOCGIFNETMASK_IN6, (caddr_t)&ifr6) ==
624 		    -1) {
625 			log_warn("SIOCGIFNETMASK_IN6");
626 			continue;
627 		}
628 
629 		imsg_addrinfo.mask = ((struct sockaddr_in6 *)&ifr6.ifr_addr)
630 		    ->sin6_addr;
631 
632 		memset(&ifr6, 0, sizeof(ifr6));
633 		strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name));
634 		memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr));
635 		lifetime = &ifr6.ifr_ifru.ifru_lifetime;
636 
637 		if (ioctl(ioctlsock, SIOCGIFALIFETIME_IN6, (caddr_t)&ifr6) ==
638 		    -1) {
639 			log_warn("SIOCGIFALIFETIME_IN6");
640 			continue;
641 		}
642 
643 		imsg_addrinfo.vltime = ND6_INFINITE_LIFETIME;
644 		imsg_addrinfo.pltime = ND6_INFINITE_LIFETIME;
645 		t = time(NULL);
646 
647 		if (lifetime->ia6t_preferred)
648 			imsg_addrinfo.pltime = lifetime->ia6t_preferred < t ? 0
649 			    : lifetime->ia6t_preferred - t;
650 
651 		if (lifetime->ia6t_expire)
652 			imsg_addrinfo.vltime = lifetime->ia6t_expire < t ? 0 :
653 			    lifetime->ia6t_expire - t;
654 
655 		frontend_imsg_compose_main(IMSG_UPDATE_ADDRESS, 0,
656 		    &imsg_addrinfo, sizeof(imsg_addrinfo));
657 
658 	}
659 	freeifaddrs(ifap);
660 
661 	log_debug("%s: %s link state down? %s", __func__, if_name,
662 	    imsg_link_state.link_state == LINK_STATE_DOWN ? "yes" : "no");
663 
664 	frontend_imsg_compose_main(IMSG_UPDATE_LINK_STATE, 0,
665 	    &imsg_link_state, sizeof(imsg_link_state));
666 }
667 
668 const char*
669 flags_to_str(int flags)
670 {
671 	static char	buf[sizeof(" anycast tentative duplicated detached "
672 			    "deprecated autoconf autoconfprivacy")];
673 
674 	buf[0] = '\0';
675 	if (flags & IN6_IFF_ANYCAST)
676 		strlcat(buf, " anycast", sizeof(buf));
677 	if (flags & IN6_IFF_TENTATIVE)
678 		strlcat(buf, " tentative", sizeof(buf));
679 	if (flags & IN6_IFF_DUPLICATED)
680 		strlcat(buf, " duplicated", sizeof(buf));
681 	if (flags & IN6_IFF_DETACHED)
682 		strlcat(buf, " detached", sizeof(buf));
683 	if (flags & IN6_IFF_DEPRECATED)
684 		strlcat(buf, " deprecated", sizeof(buf));
685 	if (flags & IN6_IFF_AUTOCONF)
686 		strlcat(buf, " autoconf", sizeof(buf));
687 	if (flags & IN6_IFF_TEMPORARY)
688 		strlcat(buf, " autoconfprivacy", sizeof(buf));
689 
690 	return (buf);
691 }
692 #endif	/* SMALL */
693 
694 void
695 frontend_startup(void)
696 {
697 	struct if_nameindex	*ifnidxp, *ifnidx;
698 
699 	if (!event_initialized(&ev_route))
700 		fatalx("%s: did not receive a route socket from the main "
701 		    "process", __func__);
702 
703 	event_add(&ev_route, NULL);
704 
705 	if ((ifnidxp = if_nameindex()) == NULL)
706 		fatalx("if_nameindex");
707 
708 	for(ifnidx = ifnidxp; ifnidx->if_index !=0 && ifnidx->if_name != NULL;
709 	    ifnidx++) {
710 		update_iface(ifnidx->if_index, ifnidx->if_name);
711 #ifndef	SMALL
712 		update_autoconf_addresses(ifnidx->if_index, ifnidx->if_name);
713 #endif	/* SMALL */
714 	}
715 
716 	if_freenameindex(ifnidxp);
717 }
718 
719 void
720 route_receive(int fd, short events, void *arg)
721 {
722 	static uint8_t			 *buf;
723 
724 	struct rt_msghdr		*rtm;
725 	struct sockaddr			*sa, *rti_info[RTAX_MAX];
726 	ssize_t				 n;
727 
728 	if (buf == NULL) {
729 		buf = malloc(ROUTE_SOCKET_BUF_SIZE);
730 		if (buf == NULL)
731 			fatal("malloc");
732 	}
733 	rtm = (struct rt_msghdr *)buf;
734 	if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) {
735 		if (errno == EAGAIN || errno == EINTR)
736 			return;
737 		log_warn("dispatch_rtmsg: read error");
738 		return;
739 	}
740 
741 	if (n == 0)
742 		fatal("routing socket closed");
743 
744 	if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
745 		log_warnx("partial rtm of %zd in buffer", n);
746 		return;
747 	}
748 
749 	if (rtm->rtm_version != RTM_VERSION)
750 		return;
751 
752 	sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen);
753 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
754 
755 	handle_route_message(rtm, rti_info);
756 }
757 
758 void
759 handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
760 {
761 	struct if_msghdr		*ifm;
762 	struct imsg_del_addr		 del_addr;
763 	struct imsg_del_route		 del_route;
764 	struct imsg_dup_addr		 dup_addr;
765 	struct sockaddr_rtlabel		*rl;
766 	struct sockaddr_in6		*sin6;
767 	struct in6_ifreq		 ifr6;
768 	struct in6_addr			*in6;
769 	int				 xflags, if_index;
770 	char				 ifnamebuf[IFNAMSIZ];
771 	char				*if_name;
772 
773 	switch (rtm->rtm_type) {
774 	case RTM_IFINFO:
775 		ifm = (struct if_msghdr *)rtm;
776 		if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
777 		if (if_name == NULL) {
778 			log_debug("RTM_IFINFO: lost if %d", ifm->ifm_index);
779 			if_index = ifm->ifm_index;
780 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
781 			    &if_index, sizeof(if_index));
782 		} else {
783 			xflags = get_xflags(if_name);
784 			if (xflags == -1 || !(xflags & IFXF_AUTOCONF6)) {
785 				log_debug("RTM_IFINFO: %s(%d) no(longer) "
786 				   "autoconf6", if_name, ifm->ifm_index);
787 				if_index = ifm->ifm_index;
788 				frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0,
789 				    0, &if_index, sizeof(if_index));
790 				remove_iface(if_index);
791 			} else {
792 				update_iface(ifm->ifm_index, if_name);
793 #ifndef	SMALL
794 				update_autoconf_addresses(ifm->ifm_index,
795 				    if_name);
796 #endif	/* SMALL */
797 			}
798 		}
799 		break;
800 	case RTM_NEWADDR:
801 		ifm = (struct if_msghdr *)rtm;
802 		if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
803 		log_debug("RTM_NEWADDR: %s[%u]", if_name, ifm->ifm_index);
804 		update_iface(ifm->ifm_index, if_name);
805 		break;
806 	case RTM_DELADDR:
807 		ifm = (struct if_msghdr *)rtm;
808 		if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
809 		if (rtm->rtm_addrs & RTA_IFA && rti_info[RTAX_IFA]->sa_family
810 		    == AF_INET6) {
811 			del_addr.if_index = ifm->ifm_index;
812 			memcpy(&del_addr.addr, rti_info[RTAX_IFA], sizeof(
813 			    del_addr.addr));
814 			frontend_imsg_compose_engine(IMSG_DEL_ADDRESS,
815 				    0, 0, &del_addr, sizeof(del_addr));
816 			log_debug("RTM_DELADDR: %s[%u]", if_name,
817 			    ifm->ifm_index);
818 		}
819 		break;
820 	case RTM_CHGADDRATTR:
821 		ifm = (struct if_msghdr *)rtm;
822 		if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
823 		if (rtm->rtm_addrs & RTA_IFA && rti_info[RTAX_IFA]->sa_family
824 		    == AF_INET6) {
825 			sin6 = (struct sockaddr_in6 *) rti_info[RTAX_IFA];
826 
827 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
828 				break;
829 
830 			memset(&ifr6, 0, sizeof(ifr6));
831 			strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name));
832 			memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr));
833 
834 			if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6, (caddr_t)&ifr6)
835 			    == -1) {
836 				log_warn("SIOCGIFAFLAG_IN6");
837 				break;
838 			}
839 
840 #ifndef	SMALL
841 			log_debug("RTM_CHGADDRATTR: %s -%s",
842 			    sin6_to_str(sin6),
843 			    flags_to_str(ifr6.ifr_ifru.ifru_flags6));
844 #endif	/* SMALL */
845 
846 			if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED) {
847 				dup_addr.if_index = ifm->ifm_index;
848 				dup_addr.addr = *sin6;
849 				frontend_imsg_compose_engine(IMSG_DUP_ADDRESS,
850 				    0, 0, &dup_addr, sizeof(dup_addr));
851 			}
852 
853 		}
854 		break;
855 	case RTM_DELETE:
856 		ifm = (struct if_msghdr *)rtm;
857 		if ((rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY | RTA_LABEL)) !=
858 		    (RTA_DST | RTA_GATEWAY | RTA_LABEL))
859 			break;
860 		if (rti_info[RTAX_DST]->sa_family != AF_INET6)
861 			break;
862 		if (!IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)
863 		    rti_info[RTAX_DST])->sin6_addr))
864 			break;
865 		if (rti_info[RTAX_GATEWAY]->sa_family != AF_INET6)
866 			break;
867 		if (rti_info[RTAX_LABEL]->sa_len !=
868 		    sizeof(struct sockaddr_rtlabel))
869 			break;
870 
871 		rl = (struct sockaddr_rtlabel *)rti_info[RTAX_LABEL];
872 		if (strcmp(rl->sr_label, SLAACD_RTA_LABEL) != 0)
873 			break;
874 
875 		if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
876 
877 		del_route.if_index = ifm->ifm_index;
878 		memcpy(&del_route.gw, rti_info[RTAX_GATEWAY],
879 		    sizeof(del_route.gw));
880 		in6 = &del_route.gw.sin6_addr;
881 		/* XXX from route(8) p_sockaddr() */
882 		if (IN6_IS_ADDR_LINKLOCAL(in6) ||
883 		    IN6_IS_ADDR_MC_LINKLOCAL(in6) ||
884 		    IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
885 			del_route.gw.sin6_scope_id =
886 			    (u_int32_t)ntohs(*(u_short *) &in6->s6_addr[2]);
887 			*(u_short *)&in6->s6_addr[2] = 0;
888 		}
889 		frontend_imsg_compose_engine(IMSG_DEL_ROUTE,
890 		    0, 0, &del_route, sizeof(del_route));
891 		log_debug("RTM_DELETE: %s[%u]", if_name,
892 		    ifm->ifm_index);
893 
894 		break;
895 #ifndef	SMALL
896 	case RTM_PROPOSAL:
897 		if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) {
898 			log_debug("RTP_PROPOSAL_SOLICIT");
899 			frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS,
900 			    0, 0, NULL, 0);
901 		}
902 		break;
903 #endif	/* SMALL */
904 	default:
905 		log_debug("unexpected RTM: %d", rtm->rtm_type);
906 		break;
907 	}
908 
909 }
910 
911 #define ROUNDUP(a) \
912 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
913 
914 void
915 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
916 {
917 	int	i;
918 
919 	for (i = 0; i < RTAX_MAX; i++) {
920 		if (addrs & (1 << i)) {
921 			rti_info[i] = sa;
922 			sa = (struct sockaddr *)((char *)(sa) +
923 			    ROUNDUP(sa->sa_len));
924 		} else
925 			rti_info[i] = NULL;
926 	}
927 }
928 
929 void
930 get_lladdr(char *if_name, struct ether_addr *mac, struct sockaddr_in6 *ll)
931 {
932 	struct ifaddrs		*ifap, *ifa;
933 	struct sockaddr_dl	*sdl;
934 	struct sockaddr_in6	*sin6;
935 
936 	if (getifaddrs(&ifap) != 0)
937 		fatal("getifaddrs");
938 
939 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
940 		if (strcmp(if_name, ifa->ifa_name) != 0)
941 			continue;
942 		if (ifa->ifa_addr == NULL)
943 			continue;
944 
945 		switch(ifa->ifa_addr->sa_family) {
946 		case AF_LINK:
947 			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
948 			if (sdl->sdl_type != IFT_ETHER ||
949 			    sdl->sdl_alen != ETHER_ADDR_LEN)
950 				continue;
951 
952 			memcpy(mac->ether_addr_octet, LLADDR(sdl),
953 			    ETHER_ADDR_LEN);
954 			break;
955 		case AF_INET6:
956 			sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
957 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
958 				sin6->sin6_scope_id = ntohs(*(u_int16_t *)
959 				    &sin6->sin6_addr.s6_addr[2]);
960 				sin6->sin6_addr.s6_addr[2] =
961 				    sin6->sin6_addr.s6_addr[3] = 0;
962 				memcpy(ll, sin6, sizeof(*ll));
963 			}
964 			break;
965 		default:
966 			break;
967 		}
968 	}
969 	freeifaddrs(ifap);
970 }
971 
972 void
973 icmp6_receive(int fd, short events, void *arg)
974 {
975 	struct imsg_ra		 ra;
976 	struct icmp6_hdr	*icmp6_hdr;
977 	struct icmp6_ev		*icmp6ev;
978 	struct in6_pktinfo	*pi = NULL;
979 	struct cmsghdr		*cm;
980 	ssize_t			 len;
981 	int			 if_index = 0, *hlimp = NULL;
982 	char			 ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
983 
984 	icmp6ev = arg;
985 	if ((len = recvmsg(fd, &icmp6ev->rcvmhdr, 0)) == -1) {
986 		log_warn("recvmsg");
987 		return;
988 	}
989 
990 	if ((size_t)len < sizeof(struct icmp6_hdr))
991 		return;
992 
993 	icmp6_hdr = (struct icmp6_hdr *)icmp6ev->answer;
994 	if (icmp6_hdr->icmp6_type != ND_ROUTER_ADVERT)
995 		return;
996 
997 	/* extract optional information via Advanced API */
998 	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&icmp6ev->rcvmhdr); cm;
999 	    cm = (struct cmsghdr *)CMSG_NXTHDR(&icmp6ev->rcvmhdr, cm)) {
1000 		if (cm->cmsg_level == IPPROTO_IPV6 &&
1001 		    cm->cmsg_type == IPV6_PKTINFO &&
1002 		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
1003 			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
1004 			if_index = pi->ipi6_ifindex;
1005 		}
1006 		if (cm->cmsg_level == IPPROTO_IPV6 &&
1007 		    cm->cmsg_type == IPV6_HOPLIMIT &&
1008 		    cm->cmsg_len == CMSG_LEN(sizeof(int)))
1009 			hlimp = (int *)CMSG_DATA(cm);
1010 	}
1011 
1012 	if (if_index == 0) {
1013 		log_warnx("failed to get receiving interface");
1014 		return;
1015 	}
1016 
1017 	if (hlimp == NULL) {
1018 		log_warnx("failed to get receiving hop limit");
1019 		return;
1020 	}
1021 
1022 	if (*hlimp != 255) {
1023 		log_warnx("invalid RA with hop limit of %d from %s on %s",
1024 		    *hlimp, inet_ntop(AF_INET6, &icmp6ev->from.sin6_addr,
1025 		    ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
1026 		    ifnamebuf));
1027 		return;
1028 	}
1029 
1030 	if ((size_t)len > sizeof(ra.packet)) {
1031 		log_warnx("invalid RA with size %ld from %s on %s",
1032 		    len, inet_ntop(AF_INET6, &icmp6ev->from.sin6_addr,
1033 		    ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
1034 		    ifnamebuf));
1035 		return;
1036 	}
1037 	ra.if_index = if_index;
1038 	memcpy(&ra.from,  &icmp6ev->from, sizeof(ra.from));
1039 	ra.len = len;
1040 	memcpy(ra.packet, icmp6ev->answer, len);
1041 
1042 	frontend_imsg_compose_engine(IMSG_RA, 0, 0, &ra, sizeof(ra));
1043 }
1044 
1045 void
1046 send_solicitation(uint32_t if_index)
1047 {
1048 	struct in6_pktinfo	*pi;
1049 	struct cmsghdr		*cm;
1050 	struct iface		*iface;
1051 
1052 	log_debug("%s(%u)", __func__, if_index);
1053 
1054 	if ((iface = get_iface_by_id(if_index)) == NULL)
1055 		return;
1056 
1057 	if (!event_initialized(&iface->icmp6ev->ev)) {
1058 		iface->send_solicitation = 1;
1059 		return;
1060 	}
1061 
1062 	dst.sin6_scope_id = if_index;
1063 
1064 	cm = CMSG_FIRSTHDR(&sndmhdr);
1065 	pi = (struct in6_pktinfo *)CMSG_DATA(cm);
1066 	pi->ipi6_ifindex = if_index;
1067 
1068 	memcpy(&nd_opt_source_link_addr, &iface->hw_address,
1069 	    sizeof(nd_opt_source_link_addr));
1070 
1071 	if (sendmsg(EVENT_FD(&iface->icmp6ev->ev), &sndmhdr, 0) != sizeof(rs) +
1072 	    sizeof(nd_opt_hdr) + sizeof(nd_opt_source_link_addr))
1073 		log_warn("sendmsg");
1074 }
1075 
1076 struct iface*
1077 get_iface_by_id(uint32_t if_index)
1078 {
1079 	struct iface	*iface;
1080 
1081 	LIST_FOREACH (iface, &interfaces, entries) {
1082 		if (iface->if_index == if_index)
1083 			return (iface);
1084 	}
1085 
1086 	return (NULL);
1087 }
1088 
1089 void
1090 remove_iface(uint32_t if_index)
1091 {
1092 	struct iface	*iface;
1093 
1094 	iface = get_iface_by_id(if_index);
1095 
1096 	if (iface == NULL)
1097 		return;
1098 
1099 	LIST_REMOVE(iface, entries);
1100 
1101 	unref_icmp6ev(iface);
1102 	free(iface);
1103 }
1104 
1105 struct icmp6_ev*
1106 get_icmp6ev_by_rdomain(int rdomain)
1107 {
1108 	struct iface	*iface;
1109 	struct icmp6_ev	*icmp6ev = NULL;
1110 
1111 	LIST_FOREACH (iface, &interfaces, entries) {
1112 		if (iface->rdomain == rdomain) {
1113 			icmp6ev = iface->icmp6ev;
1114 			break;
1115 		}
1116 	}
1117 
1118 	if (icmp6ev == NULL) {
1119 		if ((icmp6ev = calloc(1, sizeof(*icmp6ev))) == NULL)
1120 			fatal("calloc");
1121 		icmp6ev->rcviov[0].iov_base = (caddr_t)icmp6ev->answer;
1122 		icmp6ev->rcviov[0].iov_len = sizeof(icmp6ev->answer);
1123 		icmp6ev->rcvmhdr.msg_name = (caddr_t)&icmp6ev->from;
1124 		icmp6ev->rcvmhdr.msg_namelen = sizeof(icmp6ev->from);
1125 		icmp6ev->rcvmhdr.msg_iov = icmp6ev->rcviov;
1126 		icmp6ev->rcvmhdr.msg_iovlen = 1;
1127 		icmp6ev->rcvmhdr.msg_controllen =
1128 		    CMSG_SPACE(sizeof(struct in6_pktinfo)) +
1129 		    CMSG_SPACE(sizeof(int));
1130 		if ((icmp6ev->rcvmhdr.msg_control = malloc(icmp6ev->
1131 		    rcvmhdr.msg_controllen)) == NULL)
1132 			fatal("malloc");
1133 		frontend_imsg_compose_main(IMSG_OPEN_ICMP6SOCK, 0,
1134 		    &rdomain, sizeof(rdomain));
1135 	}
1136 	icmp6ev->refcnt++;
1137 	return (icmp6ev);
1138 }
1139 
1140 void
1141 unref_icmp6ev(struct iface *iface)
1142 {
1143 	struct icmp6_ev *icmp6ev = iface->icmp6ev;
1144 
1145 	iface->icmp6ev = NULL;
1146 
1147 	if (icmp6ev != NULL) {
1148 		icmp6ev->refcnt--;
1149 		if (icmp6ev->refcnt == 0) {
1150 			event_del(&icmp6ev->ev);
1151 			close(EVENT_FD(&icmp6ev->ev));
1152 			free(icmp6ev);
1153 		}
1154 	}
1155 }
1156 
1157 void
1158 set_icmp6sock(int icmp6sock, int rdomain)
1159 {
1160 	struct iface	*iface;
1161 
1162 	LIST_FOREACH (iface, &interfaces, entries) {
1163 		if (!event_initialized(&iface->icmp6ev->ev) && iface->rdomain
1164 		    == rdomain) {
1165 			event_set(&iface->icmp6ev->ev, icmp6sock, EV_READ |
1166 			    EV_PERSIST, icmp6_receive, iface->icmp6ev);
1167 			event_add(&iface->icmp6ev->ev, NULL);
1168 			icmp6sock = -1;
1169 			break;
1170 		}
1171 	}
1172 
1173 	if (icmp6sock != -1) {
1174 		/*
1175 		 * The interface disappeared or changed rdomain while we were
1176 		 * waiting for the parent process to open the raw socket.
1177 		 */
1178 		close(icmp6sock);
1179 		return;
1180 	}
1181 
1182 	LIST_FOREACH (iface, &interfaces, entries) {
1183 		if (event_initialized(&iface->icmp6ev->ev) &&
1184 		    iface->send_solicitation) {
1185 			iface->send_solicitation = 0;
1186 			send_solicitation(iface->if_index);
1187 		}
1188 	}
1189 }
1190