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