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