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