xref: /openbsd/usr.sbin/rad/engine.c (revision 0c40990e)
1*0c40990eSflorian /*	$OpenBSD: engine.c,v 1.4 2018/07/11 14:03:13 florian Exp $	*/
253293e44Sflorian 
353293e44Sflorian /*
453293e44Sflorian  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
553293e44Sflorian  * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
653293e44Sflorian  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
753293e44Sflorian  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
853293e44Sflorian  *
953293e44Sflorian  * Permission to use, copy, modify, and distribute this software for any
1053293e44Sflorian  * purpose with or without fee is hereby granted, provided that the above
1153293e44Sflorian  * copyright notice and this permission notice appear in all copies.
1253293e44Sflorian  *
1353293e44Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1453293e44Sflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1553293e44Sflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1653293e44Sflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1753293e44Sflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1853293e44Sflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1953293e44Sflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2053293e44Sflorian  */
2153293e44Sflorian 
2253293e44Sflorian #include <sys/types.h>
2353293e44Sflorian #include <sys/queue.h>
2453293e44Sflorian #include <sys/socket.h>
2553293e44Sflorian #include <sys/syslog.h>
2653293e44Sflorian #include <sys/uio.h>
2753293e44Sflorian 
2853293e44Sflorian #include <netinet/in.h>
2953293e44Sflorian #include <net/if.h>
3053293e44Sflorian #include <arpa/inet.h>
3153293e44Sflorian #include <netinet/icmp6.h>
3253293e44Sflorian 
3353293e44Sflorian #include <errno.h>
3453293e44Sflorian #include <event.h>
3553293e44Sflorian #include <imsg.h>
3653293e44Sflorian #include <signal.h>
3753293e44Sflorian #include <stdlib.h>
3853293e44Sflorian #include <string.h>
3953293e44Sflorian #include <pwd.h>
4053293e44Sflorian #include <unistd.h>
4153293e44Sflorian 
4253293e44Sflorian #include "log.h"
4353293e44Sflorian #include "rad.h"
4453293e44Sflorian #include "engine.h"
4553293e44Sflorian 
46*0c40990eSflorian #define	MAX_RTR_ADV_INTERVAL	600
47*0c40990eSflorian #define	MIN_RTR_ADV_INTERVAL	200
48*0c40990eSflorian 
49*0c40990eSflorian struct engine_iface {
50*0c40990eSflorian 	TAILQ_ENTRY(engine_iface)	entry;
51*0c40990eSflorian 	struct event			timer;
52*0c40990eSflorian 	uint32_t			if_index;
53*0c40990eSflorian };
54*0c40990eSflorian 
55*0c40990eSflorian TAILQ_HEAD(, engine_iface)	engine_interfaces;
56*0c40990eSflorian 
57*0c40990eSflorian 
5853293e44Sflorian __dead void		 engine_shutdown(void);
5953293e44Sflorian void			 engine_sig_handler(int sig, short, void *);
6053293e44Sflorian void			 engine_dispatch_frontend(int, short, void *);
6153293e44Sflorian void			 engine_dispatch_main(int, short, void *);
6253293e44Sflorian void			 parse_ra_rs(struct imsg_ra_rs *);
6353293e44Sflorian void			 parse_ra(struct imsg_ra_rs *);
6453293e44Sflorian void			 parse_rs(struct imsg_ra_rs *);
65*0c40990eSflorian void			 update_iface(uint32_t);
66*0c40990eSflorian struct engine_iface	*find_engine_iface_by_id(uint32_t);
67*0c40990eSflorian void			 iface_timeout(int, short, void *);
6853293e44Sflorian 
6953293e44Sflorian struct rad_conf	*engine_conf;
7053293e44Sflorian struct imsgev		*iev_frontend;
7153293e44Sflorian struct imsgev		*iev_main;
7253293e44Sflorian struct sockaddr_in6	 all_nodes;
7353293e44Sflorian 
7453293e44Sflorian void
7553293e44Sflorian engine_sig_handler(int sig, short event, void *arg)
7653293e44Sflorian {
7753293e44Sflorian 	/*
7853293e44Sflorian 	 * Normal signal handler rules don't apply because libevent
7953293e44Sflorian 	 * decouples for us.
8053293e44Sflorian 	 */
8153293e44Sflorian 
8253293e44Sflorian 	switch (sig) {
8353293e44Sflorian 	case SIGINT:
8453293e44Sflorian 	case SIGTERM:
8553293e44Sflorian 		engine_shutdown();
8653293e44Sflorian 	default:
8753293e44Sflorian 		fatalx("unexpected signal");
8853293e44Sflorian 	}
8953293e44Sflorian }
9053293e44Sflorian 
9153293e44Sflorian void
9253293e44Sflorian engine(int debug, int verbose)
9353293e44Sflorian {
9453293e44Sflorian 	struct event		 ev_sigint, ev_sigterm;
9553293e44Sflorian 	struct passwd		*pw;
9653293e44Sflorian 
9753293e44Sflorian 	engine_conf = config_new_empty();
9853293e44Sflorian 
9953293e44Sflorian 	log_init(debug, LOG_DAEMON);
10053293e44Sflorian 	log_setverbose(verbose);
10153293e44Sflorian 
10253293e44Sflorian 	if ((pw = getpwnam(RAD_USER)) == NULL)
10353293e44Sflorian 		fatal("getpwnam");
10453293e44Sflorian 
10553293e44Sflorian 	if (chroot(pw->pw_dir) == -1)
10653293e44Sflorian 		fatal("chroot");
10753293e44Sflorian 	if (chdir("/") == -1)
10853293e44Sflorian 		fatal("chdir(\"/\")");
10953293e44Sflorian 
11053293e44Sflorian 	rad_process = PROC_ENGINE;
11153293e44Sflorian 	setproctitle("%s", log_procnames[rad_process]);
11253293e44Sflorian 	log_procinit(log_procnames[rad_process]);
11353293e44Sflorian 
11453293e44Sflorian 	if (setgroups(1, &pw->pw_gid) ||
11553293e44Sflorian 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
11653293e44Sflorian 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
11753293e44Sflorian 		fatal("can't drop privileges");
11853293e44Sflorian 
11953293e44Sflorian 	if (pledge("stdio recvfd", NULL) == -1)
12053293e44Sflorian 		fatal("pledge");
12153293e44Sflorian 
12253293e44Sflorian 	event_init();
12353293e44Sflorian 
12453293e44Sflorian 	/* Setup signal handler(s). */
12553293e44Sflorian 	signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL);
12653293e44Sflorian 	signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL);
12753293e44Sflorian 	signal_add(&ev_sigint, NULL);
12853293e44Sflorian 	signal_add(&ev_sigterm, NULL);
12953293e44Sflorian 	signal(SIGPIPE, SIG_IGN);
13053293e44Sflorian 	signal(SIGHUP, SIG_IGN);
13153293e44Sflorian 
13253293e44Sflorian 	/* Setup pipe and event handler to the main process. */
13353293e44Sflorian 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
13453293e44Sflorian 		fatal(NULL);
13553293e44Sflorian 
13653293e44Sflorian 	imsg_init(&iev_main->ibuf, 3);
13753293e44Sflorian 	iev_main->handler = engine_dispatch_main;
13853293e44Sflorian 
13953293e44Sflorian 	/* Setup event handlers. */
14053293e44Sflorian 	iev_main->events = EV_READ;
14153293e44Sflorian 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
14253293e44Sflorian 	    iev_main->handler, iev_main);
14353293e44Sflorian 	event_add(&iev_main->ev, NULL);
14453293e44Sflorian 
14553293e44Sflorian 	all_nodes.sin6_len = sizeof(all_nodes);
14653293e44Sflorian 	all_nodes.sin6_family = AF_INET6;
14753293e44Sflorian 	if (inet_pton(AF_INET6, "ff02::1", &all_nodes.sin6_addr) != 1)
14853293e44Sflorian 		fatal("inet_pton");
14953293e44Sflorian 
15053293e44Sflorian 	event_dispatch();
15153293e44Sflorian 
15253293e44Sflorian 	engine_shutdown();
15353293e44Sflorian }
15453293e44Sflorian 
15553293e44Sflorian __dead void
15653293e44Sflorian engine_shutdown(void)
15753293e44Sflorian {
15853293e44Sflorian 	/* Close pipes. */
15953293e44Sflorian 	msgbuf_clear(&iev_frontend->ibuf.w);
16053293e44Sflorian 	close(iev_frontend->ibuf.fd);
16153293e44Sflorian 	msgbuf_clear(&iev_main->ibuf.w);
16253293e44Sflorian 	close(iev_main->ibuf.fd);
16353293e44Sflorian 
16453293e44Sflorian 	config_clear(engine_conf);
16553293e44Sflorian 
16653293e44Sflorian 	free(iev_frontend);
16753293e44Sflorian 	free(iev_main);
16853293e44Sflorian 
16953293e44Sflorian 	log_info("engine exiting");
17053293e44Sflorian 	exit(0);
17153293e44Sflorian }
17253293e44Sflorian 
17353293e44Sflorian int
17453293e44Sflorian engine_imsg_compose_frontend(int type, pid_t pid, void *data, uint16_t datalen)
17553293e44Sflorian {
17653293e44Sflorian 	return (imsg_compose_event(iev_frontend, type, 0, pid, -1,
17753293e44Sflorian 	    data, datalen));
17853293e44Sflorian }
17953293e44Sflorian 
18053293e44Sflorian void
18153293e44Sflorian engine_dispatch_frontend(int fd, short event, void *bula)
18253293e44Sflorian {
18353293e44Sflorian 	struct imsgev		*iev = bula;
18453293e44Sflorian 	struct imsgbuf		*ibuf;
18553293e44Sflorian 	struct imsg		 imsg;
18653293e44Sflorian 	struct imsg_ra_rs	 ra_rs;
18753293e44Sflorian 	ssize_t			 n;
188*0c40990eSflorian 	uint32_t		 if_index;
18953293e44Sflorian 	int			 shut = 0, verbose;
19053293e44Sflorian 
19153293e44Sflorian 	ibuf = &iev->ibuf;
19253293e44Sflorian 
19353293e44Sflorian 	if (event & EV_READ) {
19453293e44Sflorian 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
19553293e44Sflorian 			fatal("imsg_read error");
19653293e44Sflorian 		if (n == 0)	/* Connection closed. */
19753293e44Sflorian 			shut = 1;
19853293e44Sflorian 	}
19953293e44Sflorian 	if (event & EV_WRITE) {
20053293e44Sflorian 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
20153293e44Sflorian 			fatal("msgbuf_write");
20253293e44Sflorian 		if (n == 0)	/* Connection closed. */
20353293e44Sflorian 			shut = 1;
20453293e44Sflorian 	}
20553293e44Sflorian 
20653293e44Sflorian 	for (;;) {
20753293e44Sflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
20853293e44Sflorian 			fatal("%s: imsg_get error", __func__);
20953293e44Sflorian 		if (n == 0)	/* No more messages. */
21053293e44Sflorian 			break;
21153293e44Sflorian 
21253293e44Sflorian 		switch (imsg.hdr.type) {
21353293e44Sflorian 		case IMSG_RA_RS:
21453293e44Sflorian 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(ra_rs))
21553293e44Sflorian 				fatal("%s: IMSG_RA_RS wrong length: %d",
21653293e44Sflorian 				    __func__, imsg.hdr.len);
21753293e44Sflorian 			memcpy(&ra_rs, imsg.data, sizeof(ra_rs));
21853293e44Sflorian 			parse_ra_rs(&ra_rs);
21953293e44Sflorian 			break;
220*0c40990eSflorian 		case IMSG_UPDATE_IF:
221*0c40990eSflorian 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index))
222*0c40990eSflorian 				fatal("%s: IMSG_UPDATE_IF wrong length: %d",
223*0c40990eSflorian 				    __func__, imsg.hdr.len);
224*0c40990eSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
225*0c40990eSflorian 			update_iface(if_index);
226*0c40990eSflorian 			break;
22753293e44Sflorian 		case IMSG_CTL_LOG_VERBOSE:
22853293e44Sflorian 			/* Already checked by frontend. */
22953293e44Sflorian 			memcpy(&verbose, imsg.data, sizeof(verbose));
23053293e44Sflorian 			log_setverbose(verbose);
23153293e44Sflorian 			break;
23253293e44Sflorian 		default:
23353293e44Sflorian 			log_debug("%s: unexpected imsg %d", __func__,
23453293e44Sflorian 			    imsg.hdr.type);
23553293e44Sflorian 			break;
23653293e44Sflorian 		}
23753293e44Sflorian 		imsg_free(&imsg);
23853293e44Sflorian 	}
23953293e44Sflorian 	if (!shut)
24053293e44Sflorian 		imsg_event_add(iev);
24153293e44Sflorian 	else {
24253293e44Sflorian 		/* This pipe is dead. Remove its event handler. */
24353293e44Sflorian 		event_del(&iev->ev);
24453293e44Sflorian 		event_loopexit(NULL);
24553293e44Sflorian 	}
24653293e44Sflorian }
24753293e44Sflorian 
24853293e44Sflorian void
24953293e44Sflorian engine_dispatch_main(int fd, short event, void *bula)
25053293e44Sflorian {
25153293e44Sflorian 	static struct rad_conf		*nconf;
25253293e44Sflorian 	static struct ra_iface_conf	*ra_iface_conf;
25353293e44Sflorian 	struct imsg			 imsg;
25453293e44Sflorian 	struct imsgev			*iev = bula;
25553293e44Sflorian 	struct imsgbuf			*ibuf;
25653293e44Sflorian 	struct ra_prefix_conf		*ra_prefix_conf;
25753293e44Sflorian 	ssize_t				 n;
25853293e44Sflorian 	int				 shut = 0;
25953293e44Sflorian 
26053293e44Sflorian 	ibuf = &iev->ibuf;
26153293e44Sflorian 
26253293e44Sflorian 	if (event & EV_READ) {
26353293e44Sflorian 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
26453293e44Sflorian 			fatal("imsg_read error");
26553293e44Sflorian 		if (n == 0)	/* Connection closed. */
26653293e44Sflorian 			shut = 1;
26753293e44Sflorian 	}
26853293e44Sflorian 	if (event & EV_WRITE) {
26953293e44Sflorian 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
27053293e44Sflorian 			fatal("msgbuf_write");
27153293e44Sflorian 		if (n == 0)	/* Connection closed. */
27253293e44Sflorian 			shut = 1;
27353293e44Sflorian 	}
27453293e44Sflorian 
27553293e44Sflorian 	for (;;) {
27653293e44Sflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
27753293e44Sflorian 			fatal("%s: imsg_get error", __func__);
27853293e44Sflorian 		if (n == 0)	/* No more messages. */
27953293e44Sflorian 			break;
28053293e44Sflorian 
28153293e44Sflorian 		switch (imsg.hdr.type) {
28253293e44Sflorian 		case IMSG_SOCKET_IPC:
28353293e44Sflorian 			/*
28453293e44Sflorian 			 * Setup pipe and event handler to the frontend
28553293e44Sflorian 			 * process.
28653293e44Sflorian 			 */
28753293e44Sflorian 			if (iev_frontend) {
28853293e44Sflorian 				log_warnx("%s: received unexpected imsg fd "
28953293e44Sflorian 				    "to engine", __func__);
29053293e44Sflorian 				break;
29153293e44Sflorian 			}
29253293e44Sflorian 			if ((fd = imsg.fd) == -1) {
29353293e44Sflorian 				log_warnx("%s: expected to receive imsg fd to "
29453293e44Sflorian 				   "engine but didn't receive any", __func__);
29553293e44Sflorian 				break;
29653293e44Sflorian 			}
29753293e44Sflorian 
29853293e44Sflorian 			iev_frontend = malloc(sizeof(struct imsgev));
29953293e44Sflorian 			if (iev_frontend == NULL)
30053293e44Sflorian 				fatal(NULL);
30153293e44Sflorian 
30253293e44Sflorian 			imsg_init(&iev_frontend->ibuf, fd);
30353293e44Sflorian 			iev_frontend->handler = engine_dispatch_frontend;
30453293e44Sflorian 			iev_frontend->events = EV_READ;
30553293e44Sflorian 
30653293e44Sflorian 			event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
30753293e44Sflorian 			iev_frontend->events, iev_frontend->handler,
30853293e44Sflorian 			    iev_frontend);
30953293e44Sflorian 			event_add(&iev_frontend->ev, NULL);
31053293e44Sflorian 			break;
31153293e44Sflorian 		case IMSG_RECONF_CONF:
31253293e44Sflorian 			if ((nconf = malloc(sizeof(struct rad_conf))) == NULL)
31353293e44Sflorian 				fatal(NULL);
31453293e44Sflorian 			memcpy(nconf, imsg.data, sizeof(struct rad_conf));
31553293e44Sflorian 			SIMPLEQ_INIT(&nconf->ra_iface_list);
31653293e44Sflorian 			break;
31753293e44Sflorian 		case IMSG_RECONF_RA_IFACE:
31853293e44Sflorian 			if ((ra_iface_conf = malloc(sizeof(struct
31953293e44Sflorian 			    ra_iface_conf))) == NULL)
32053293e44Sflorian 				fatal(NULL);
32153293e44Sflorian 			memcpy(ra_iface_conf, imsg.data,
32253293e44Sflorian 			    sizeof(struct ra_iface_conf));
32353293e44Sflorian 			ra_iface_conf->autoprefix = NULL;
32453293e44Sflorian 			SIMPLEQ_INIT(&ra_iface_conf->ra_prefix_list);
32553293e44Sflorian 			SIMPLEQ_INSERT_TAIL(&nconf->ra_iface_list,
32653293e44Sflorian 			    ra_iface_conf, entry);
32753293e44Sflorian 			break;
32853293e44Sflorian 		case IMSG_RECONF_RA_AUTOPREFIX:
32953293e44Sflorian 			if ((ra_iface_conf->autoprefix = malloc(sizeof(struct
33053293e44Sflorian 			    ra_prefix_conf))) == NULL)
33153293e44Sflorian 				fatal(NULL);
33253293e44Sflorian 			memcpy(ra_iface_conf->autoprefix, imsg.data,
33353293e44Sflorian 			    sizeof(struct ra_prefix_conf));
33453293e44Sflorian 			break;
33553293e44Sflorian 		case IMSG_RECONF_RA_PREFIX:
33653293e44Sflorian 			if ((ra_prefix_conf = malloc(sizeof(struct
33753293e44Sflorian 			    ra_prefix_conf))) == NULL)
33853293e44Sflorian 				fatal(NULL);
33953293e44Sflorian 			memcpy(ra_prefix_conf, imsg.data, sizeof(struct
34053293e44Sflorian 			    ra_prefix_conf));
34153293e44Sflorian 			SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_prefix_list,
34253293e44Sflorian 			    ra_prefix_conf, entry);
34353293e44Sflorian 			break;
34453293e44Sflorian 		case IMSG_RECONF_END:
34553293e44Sflorian 			merge_config(engine_conf, nconf);
34653293e44Sflorian 			nconf = NULL;
34753293e44Sflorian 			break;
34853293e44Sflorian 		default:
34953293e44Sflorian 			log_debug("%s: unexpected imsg %d", __func__,
35053293e44Sflorian 			    imsg.hdr.type);
35153293e44Sflorian 			break;
35253293e44Sflorian 		}
35353293e44Sflorian 		imsg_free(&imsg);
35453293e44Sflorian 	}
35553293e44Sflorian 	if (!shut)
35653293e44Sflorian 		imsg_event_add(iev);
35753293e44Sflorian 	else {
35853293e44Sflorian 		/* This pipe is dead. Remove its event handler. */
35953293e44Sflorian 		event_del(&iev->ev);
36053293e44Sflorian 		event_loopexit(NULL);
36153293e44Sflorian 	}
36253293e44Sflorian }
36353293e44Sflorian 
36453293e44Sflorian 
36553293e44Sflorian void
36653293e44Sflorian parse_ra_rs(struct imsg_ra_rs *ra_rs)
36753293e44Sflorian {
36853293e44Sflorian 	char			 ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
36953293e44Sflorian 	struct icmp6_hdr	*hdr;
37053293e44Sflorian 
37153293e44Sflorian 	hdr = (struct icmp6_hdr *) ra_rs->packet;
37253293e44Sflorian 
37353293e44Sflorian 	switch (hdr->icmp6_type) {
37453293e44Sflorian 	case ND_ROUTER_ADVERT:
37553293e44Sflorian 		parse_ra(ra_rs);
37653293e44Sflorian 		break;
37753293e44Sflorian 	case ND_ROUTER_SOLICIT:
37853293e44Sflorian 		parse_rs(ra_rs);
37953293e44Sflorian 		break;
38053293e44Sflorian 	default:
38153293e44Sflorian 		log_warnx("unexpected icmp6_type: %d from %s on %s",
38253293e44Sflorian 		    hdr->icmp6_type, inet_ntop(AF_INET6, &ra_rs->from.sin6_addr,
38353293e44Sflorian 		    ntopbuf, INET6_ADDRSTRLEN), if_indextoname(ra_rs->if_index,
38453293e44Sflorian 		    ifnamebuf));
38553293e44Sflorian 		break;
38653293e44Sflorian 	}
38753293e44Sflorian }
38853293e44Sflorian 
38953293e44Sflorian void
39053293e44Sflorian parse_ra(struct imsg_ra_rs *ra)
39153293e44Sflorian {
39253293e44Sflorian 	char			 ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
39353293e44Sflorian 	log_debug("got RA from %s on %s",
39453293e44Sflorian 	    inet_ntop(AF_INET6, &ra->from.sin6_addr, ntopbuf,
39553293e44Sflorian 	    INET6_ADDRSTRLEN), if_indextoname(ra->if_index,
39653293e44Sflorian 	    ifnamebuf));
39753293e44Sflorian 	/* XXX not yet */
39853293e44Sflorian }
39953293e44Sflorian 
40053293e44Sflorian void
40153293e44Sflorian parse_rs(struct imsg_ra_rs *rs)
40253293e44Sflorian {
40353293e44Sflorian 	struct nd_router_solicit	*nd_rs;
40453293e44Sflorian 	struct imsg_send_ra		 send_ra;
40553293e44Sflorian 	ssize_t				 len;
40653293e44Sflorian 	const char			*hbuf;
40753293e44Sflorian 	char				 ifnamebuf[IFNAMSIZ];
40853293e44Sflorian 	uint8_t				*p;
40953293e44Sflorian 
41053293e44Sflorian 	hbuf = sin6_to_str(&rs->from);
41153293e44Sflorian 
41253293e44Sflorian 	send_ra.if_index = rs->if_index;
41353293e44Sflorian 	memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
41453293e44Sflorian 
41553293e44Sflorian 	log_debug("got RS from %s on %s", hbuf, if_indextoname(rs->if_index,
41653293e44Sflorian 	    ifnamebuf));
41753293e44Sflorian 
41853293e44Sflorian 	len = rs->len;
41953293e44Sflorian 
42053293e44Sflorian 	if (!IN6_IS_ADDR_LINKLOCAL(&rs->from.sin6_addr)) {
42153293e44Sflorian 		log_warnx("RA from non link local address %s", hbuf);
42253293e44Sflorian 		return;
42353293e44Sflorian 	}
42453293e44Sflorian 
42553293e44Sflorian 	if ((size_t)len < sizeof(struct nd_router_solicit)) {
42653293e44Sflorian 		log_warnx("received too short message (%ld) from %s", len,
42753293e44Sflorian 		    hbuf);
42853293e44Sflorian 		return;
42953293e44Sflorian 	}
43053293e44Sflorian 
43153293e44Sflorian 	p = rs->packet;
43253293e44Sflorian 	nd_rs = (struct nd_router_solicit *)p;
43353293e44Sflorian 	len -= sizeof(struct nd_router_solicit);
43453293e44Sflorian 	p += sizeof(struct nd_router_solicit);
43553293e44Sflorian 
43653293e44Sflorian 	if (nd_rs->nd_rs_code != 0) {
43753293e44Sflorian 		log_warnx("invalid ICMPv6 code (%d) from %s", nd_rs->nd_rs_code,
43853293e44Sflorian 		    hbuf);
43953293e44Sflorian 		return;
44053293e44Sflorian 	}
44153293e44Sflorian 	while ((size_t)len >= sizeof(struct nd_opt_hdr)) {
44253293e44Sflorian 		struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p;
44353293e44Sflorian 
44453293e44Sflorian 		len -= sizeof(struct nd_opt_hdr);
44553293e44Sflorian 		p += sizeof(struct nd_opt_hdr);
44653293e44Sflorian 
44753293e44Sflorian 		if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) {
44853293e44Sflorian 			log_warnx("invalid option len: %u > %ld",
44953293e44Sflorian 			    nd_opt_hdr->nd_opt_len, len);
45053293e44Sflorian 			return;
45153293e44Sflorian 		}
45253293e44Sflorian 		switch (nd_opt_hdr->nd_opt_type) {
45353293e44Sflorian 		case ND_OPT_SOURCE_LINKADDR:
45453293e44Sflorian 			log_debug("got RS with source linkaddr option");
45553293e44Sflorian 			memcpy(&send_ra.to, &rs->from, sizeof(send_ra.to));
45653293e44Sflorian 			break;
45753293e44Sflorian 		default:
45853293e44Sflorian 			log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type);
45953293e44Sflorian 			break;
46053293e44Sflorian 		}
46153293e44Sflorian 		len -= nd_opt_hdr->nd_opt_len * 8 - 2;
46253293e44Sflorian 		p += nd_opt_hdr->nd_opt_len * 8 - 2;
46353293e44Sflorian 	}
46453293e44Sflorian 	engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
46553293e44Sflorian 	    sizeof(send_ra));
46653293e44Sflorian }
467*0c40990eSflorian 
468*0c40990eSflorian struct engine_iface*
469*0c40990eSflorian find_engine_iface_by_id(uint32_t if_index)
470*0c40990eSflorian {
471*0c40990eSflorian 	struct engine_iface	*engine_iface;
472*0c40990eSflorian 
473*0c40990eSflorian 	TAILQ_FOREACH(engine_iface, &engine_interfaces, entry) {
474*0c40990eSflorian 		if (engine_iface->if_index == if_index)
475*0c40990eSflorian 			return engine_iface;
476*0c40990eSflorian 	}
477*0c40990eSflorian 	return (NULL);
478*0c40990eSflorian }
479*0c40990eSflorian 
480*0c40990eSflorian void
481*0c40990eSflorian update_iface(uint32_t if_index)
482*0c40990eSflorian {
483*0c40990eSflorian 	struct engine_iface	*engine_iface;
484*0c40990eSflorian 	struct timeval		 tv;
485*0c40990eSflorian 
486*0c40990eSflorian 	if ((engine_iface = find_engine_iface_by_id(if_index)) == NULL) {
487*0c40990eSflorian 		engine_iface = calloc(1, sizeof(*engine_iface));
488*0c40990eSflorian 		engine_iface->if_index = if_index;
489*0c40990eSflorian 		evtimer_set(&engine_iface->timer, iface_timeout, engine_iface);
490*0c40990eSflorian 	}
491*0c40990eSflorian 
492*0c40990eSflorian 	tv.tv_sec = 0;
493*0c40990eSflorian 	tv.tv_usec = arc4random_uniform(1000000);
494*0c40990eSflorian 	evtimer_add(&engine_iface->timer, &tv);
495*0c40990eSflorian }
496*0c40990eSflorian 
497*0c40990eSflorian void
498*0c40990eSflorian iface_timeout(int fd, short events, void *arg)
499*0c40990eSflorian {
500*0c40990eSflorian 	struct engine_iface	*engine_iface = (struct engine_iface *)arg;
501*0c40990eSflorian 	struct imsg_send_ra	 send_ra;
502*0c40990eSflorian 	struct timeval		 tv;
503*0c40990eSflorian 
504*0c40990eSflorian 
505*0c40990eSflorian 	tv.tv_sec = MIN_RTR_ADV_INTERVAL +
506*0c40990eSflorian 	    arc4random_uniform(MAX_RTR_ADV_INTERVAL - MIN_RTR_ADV_INTERVAL);
507*0c40990eSflorian 	tv.tv_usec = arc4random_uniform(1000000);
508*0c40990eSflorian 
509*0c40990eSflorian 	log_debug("%s new timeout in %lld", __func__, tv.tv_sec);
510*0c40990eSflorian 
511*0c40990eSflorian 	evtimer_add(&engine_iface->timer, &tv);
512*0c40990eSflorian 
513*0c40990eSflorian 	send_ra.if_index = engine_iface->if_index;
514*0c40990eSflorian 	memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
515*0c40990eSflorian 	engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
516*0c40990eSflorian 	    sizeof(send_ra));
517*0c40990eSflorian }
518