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