xref: /openbsd-src/usr.sbin/rad/engine.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1*f1b790a5Sclaudio /*	$OpenBSD: engine.c,v 1.28 2024/11/21 13:38:15 claudio 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>
26a14293eaSflorian #include <sys/time.h>
2753293e44Sflorian #include <sys/uio.h>
2853293e44Sflorian 
2953293e44Sflorian #include <netinet/in.h>
3053293e44Sflorian #include <net/if.h>
3153293e44Sflorian #include <arpa/inet.h>
3253293e44Sflorian #include <netinet/icmp6.h>
3353293e44Sflorian 
3453293e44Sflorian #include <errno.h>
3553293e44Sflorian #include <event.h>
3653293e44Sflorian #include <imsg.h>
37a14293eaSflorian #include <pwd.h>
3853293e44Sflorian #include <signal.h>
3953293e44Sflorian #include <stdlib.h>
4053293e44Sflorian #include <string.h>
41a14293eaSflorian #include <time.h>
4253293e44Sflorian #include <unistd.h>
4353293e44Sflorian 
4453293e44Sflorian #include "log.h"
4553293e44Sflorian #include "rad.h"
4653293e44Sflorian #include "engine.h"
4753293e44Sflorian 
480c40990eSflorian struct engine_iface {
490c40990eSflorian 	TAILQ_ENTRY(engine_iface)	entry;
500c40990eSflorian 	struct event			timer;
51a14293eaSflorian 	struct timespec			last_ra;
520c40990eSflorian 	uint32_t			if_index;
53a14293eaSflorian 	int				ras_delayed;
540c40990eSflorian };
550c40990eSflorian 
560c40990eSflorian TAILQ_HEAD(, engine_iface)	engine_interfaces;
570c40990eSflorian 
580c40990eSflorian 
5953293e44Sflorian __dead void		 engine_shutdown(void);
6053293e44Sflorian void			 engine_sig_handler(int sig, short, void *);
6153293e44Sflorian void			 engine_dispatch_frontend(int, short, void *);
6253293e44Sflorian void			 engine_dispatch_main(int, short, void *);
6353293e44Sflorian void			 parse_ra_rs(struct imsg_ra_rs *);
6453293e44Sflorian void			 parse_ra(struct imsg_ra_rs *);
6553293e44Sflorian void			 parse_rs(struct imsg_ra_rs *);
660c40990eSflorian void			 update_iface(uint32_t);
674a78c7cfSflorian void			 remove_iface(uint32_t);
680c40990eSflorian struct engine_iface	*find_engine_iface_by_id(uint32_t);
690c40990eSflorian void			 iface_timeout(int, short, void *);
7053293e44Sflorian 
7153293e44Sflorian struct rad_conf		*engine_conf;
72032fa683Sflorian static struct imsgev	*iev_frontend;
73032fa683Sflorian static struct imsgev	*iev_main;
7453293e44Sflorian struct sockaddr_in6	 all_nodes;
7553293e44Sflorian 
7653293e44Sflorian void
7753293e44Sflorian engine_sig_handler(int sig, short event, void *arg)
7853293e44Sflorian {
7953293e44Sflorian 	/*
8053293e44Sflorian 	 * Normal signal handler rules don't apply because libevent
8153293e44Sflorian 	 * decouples for us.
8253293e44Sflorian 	 */
8353293e44Sflorian 
8453293e44Sflorian 	switch (sig) {
8553293e44Sflorian 	case SIGINT:
8653293e44Sflorian 	case SIGTERM:
8753293e44Sflorian 		engine_shutdown();
8853293e44Sflorian 	default:
8953293e44Sflorian 		fatalx("unexpected signal");
9053293e44Sflorian 	}
9153293e44Sflorian }
9253293e44Sflorian 
9353293e44Sflorian void
9453293e44Sflorian engine(int debug, int verbose)
9553293e44Sflorian {
9653293e44Sflorian 	struct event		 ev_sigint, ev_sigterm;
9753293e44Sflorian 	struct passwd		*pw;
9853293e44Sflorian 
9953293e44Sflorian 	engine_conf = config_new_empty();
10053293e44Sflorian 
10153293e44Sflorian 	log_init(debug, LOG_DAEMON);
10253293e44Sflorian 	log_setverbose(verbose);
10353293e44Sflorian 
10453293e44Sflorian 	if ((pw = getpwnam(RAD_USER)) == NULL)
10553293e44Sflorian 		fatal("getpwnam");
10653293e44Sflorian 
10753293e44Sflorian 	if (chroot(pw->pw_dir) == -1)
10853293e44Sflorian 		fatal("chroot");
10953293e44Sflorian 	if (chdir("/") == -1)
11053293e44Sflorian 		fatal("chdir(\"/\")");
11153293e44Sflorian 
1123c6be478Sflorian 	setproctitle("%s", "engine");
1133c6be478Sflorian 	log_procinit("engine");
11453293e44Sflorian 
11553293e44Sflorian 	if (setgroups(1, &pw->pw_gid) ||
11653293e44Sflorian 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
11753293e44Sflorian 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
11853293e44Sflorian 		fatal("can't drop privileges");
11953293e44Sflorian 
12053293e44Sflorian 	if (pledge("stdio recvfd", NULL) == -1)
12153293e44Sflorian 		fatal("pledge");
12253293e44Sflorian 
12353293e44Sflorian 	event_init();
12453293e44Sflorian 
12553293e44Sflorian 	/* Setup signal handler(s). */
12653293e44Sflorian 	signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL);
12753293e44Sflorian 	signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL);
12853293e44Sflorian 	signal_add(&ev_sigint, NULL);
12953293e44Sflorian 	signal_add(&ev_sigterm, NULL);
13053293e44Sflorian 	signal(SIGPIPE, SIG_IGN);
13153293e44Sflorian 	signal(SIGHUP, SIG_IGN);
13253293e44Sflorian 
13353293e44Sflorian 	/* Setup pipe and event handler to the main process. */
13453293e44Sflorian 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
13553293e44Sflorian 		fatal(NULL);
13653293e44Sflorian 
137*f1b790a5Sclaudio 	if (imsgbuf_init(&iev_main->ibuf, 3) == -1)
138*f1b790a5Sclaudio 		fatal(NULL);
139*f1b790a5Sclaudio 	imsgbuf_allow_fdpass(&iev_main->ibuf);
14053293e44Sflorian 	iev_main->handler = engine_dispatch_main;
14153293e44Sflorian 
14253293e44Sflorian 	/* Setup event handlers. */
14353293e44Sflorian 	iev_main->events = EV_READ;
14453293e44Sflorian 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
14553293e44Sflorian 	    iev_main->handler, iev_main);
14653293e44Sflorian 	event_add(&iev_main->ev, NULL);
14753293e44Sflorian 
14853293e44Sflorian 	all_nodes.sin6_len = sizeof(all_nodes);
14953293e44Sflorian 	all_nodes.sin6_family = AF_INET6;
15053293e44Sflorian 	if (inet_pton(AF_INET6, "ff02::1", &all_nodes.sin6_addr) != 1)
15153293e44Sflorian 		fatal("inet_pton");
15253293e44Sflorian 
1534a78c7cfSflorian 	TAILQ_INIT(&engine_interfaces);
1544a78c7cfSflorian 
15553293e44Sflorian 	event_dispatch();
15653293e44Sflorian 
15753293e44Sflorian 	engine_shutdown();
15853293e44Sflorian }
15953293e44Sflorian 
16053293e44Sflorian __dead void
16153293e44Sflorian engine_shutdown(void)
16253293e44Sflorian {
16353293e44Sflorian 	/* Close pipes. */
1649cbf9e90Sclaudio 	imsgbuf_clear(&iev_frontend->ibuf);
16553293e44Sflorian 	close(iev_frontend->ibuf.fd);
1669cbf9e90Sclaudio 	imsgbuf_clear(&iev_main->ibuf);
16753293e44Sflorian 	close(iev_main->ibuf.fd);
16853293e44Sflorian 
16953293e44Sflorian 	config_clear(engine_conf);
17053293e44Sflorian 
17153293e44Sflorian 	free(iev_frontend);
17253293e44Sflorian 	free(iev_main);
17353293e44Sflorian 
17453293e44Sflorian 	log_info("engine exiting");
17553293e44Sflorian 	exit(0);
17653293e44Sflorian }
17753293e44Sflorian 
17853293e44Sflorian int
17953293e44Sflorian engine_imsg_compose_frontend(int type, pid_t pid, void *data, uint16_t datalen)
18053293e44Sflorian {
18153293e44Sflorian 	return (imsg_compose_event(iev_frontend, type, 0, pid, -1,
18253293e44Sflorian 	    data, datalen));
18353293e44Sflorian }
18453293e44Sflorian 
18553293e44Sflorian void
18653293e44Sflorian engine_dispatch_frontend(int fd, short event, void *bula)
18753293e44Sflorian {
18853293e44Sflorian 	struct imsgev		*iev = bula;
18953293e44Sflorian 	struct imsgbuf		*ibuf;
19053293e44Sflorian 	struct imsg		 imsg;
19153293e44Sflorian 	struct imsg_ra_rs	 ra_rs;
19253293e44Sflorian 	ssize_t			 n;
1930c40990eSflorian 	uint32_t		 if_index;
19453293e44Sflorian 	int			 shut = 0, verbose;
19553293e44Sflorian 
19653293e44Sflorian 	ibuf = &iev->ibuf;
19753293e44Sflorian 
19853293e44Sflorian 	if (event & EV_READ) {
199668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
200dd7efffeSclaudio 			fatal("imsgbuf_read error");
20153293e44Sflorian 		if (n == 0)	/* Connection closed. */
20253293e44Sflorian 			shut = 1;
20353293e44Sflorian 	}
20453293e44Sflorian 	if (event & EV_WRITE) {
205dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
206c1aa9554Sclaudio 			if (errno == EPIPE)	/* connection closed */
20753293e44Sflorian 				shut = 1;
208c1aa9554Sclaudio 			else
209dd7efffeSclaudio 				fatal("imsgbuf_write");
210c1aa9554Sclaudio 		}
21153293e44Sflorian 	}
21253293e44Sflorian 
21353293e44Sflorian 	for (;;) {
21453293e44Sflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
21553293e44Sflorian 			fatal("%s: imsg_get error", __func__);
21653293e44Sflorian 		if (n == 0)	/* No more messages. */
21753293e44Sflorian 			break;
21853293e44Sflorian 
21953293e44Sflorian 		switch (imsg.hdr.type) {
22053293e44Sflorian 		case IMSG_RA_RS:
221b17c900dSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(ra_rs))
22251a61992Spamela 				fatalx("%s: IMSG_RA_RS wrong length: %lu",
223b17c900dSpamela 				    __func__, IMSG_DATA_SIZE(imsg));
22453293e44Sflorian 			memcpy(&ra_rs, imsg.data, sizeof(ra_rs));
22553293e44Sflorian 			parse_ra_rs(&ra_rs);
22653293e44Sflorian 			break;
2270c40990eSflorian 		case IMSG_UPDATE_IF:
228b17c900dSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
22951a61992Spamela 				fatalx("%s: IMSG_UPDATE_IF wrong length: %lu",
230b17c900dSpamela 				    __func__, IMSG_DATA_SIZE(imsg));
2310c40990eSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
2320c40990eSflorian 			update_iface(if_index);
2330c40990eSflorian 			break;
2344a78c7cfSflorian 		case IMSG_REMOVE_IF:
235b17c900dSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
23651a61992Spamela 				fatalx("%s: IMSG_REMOVE_IF wrong length: %lu",
237b17c900dSpamela 				    __func__, IMSG_DATA_SIZE(imsg));
2384a78c7cfSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
2394a78c7cfSflorian 			remove_iface(if_index);
2404a78c7cfSflorian 			break;
24153293e44Sflorian 		case IMSG_CTL_LOG_VERBOSE:
2420eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(verbose))
2430eb5c43bSpamela 				fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: "
2440eb5c43bSpamela 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
24553293e44Sflorian 			memcpy(&verbose, imsg.data, sizeof(verbose));
24653293e44Sflorian 			log_setverbose(verbose);
24753293e44Sflorian 			break;
24853293e44Sflorian 		default:
24953293e44Sflorian 			log_debug("%s: unexpected imsg %d", __func__,
25053293e44Sflorian 			    imsg.hdr.type);
25153293e44Sflorian 			break;
25253293e44Sflorian 		}
25353293e44Sflorian 		imsg_free(&imsg);
25453293e44Sflorian 	}
25553293e44Sflorian 	if (!shut)
25653293e44Sflorian 		imsg_event_add(iev);
25753293e44Sflorian 	else {
25853293e44Sflorian 		/* This pipe is dead. Remove its event handler. */
25953293e44Sflorian 		event_del(&iev->ev);
26053293e44Sflorian 		event_loopexit(NULL);
26153293e44Sflorian 	}
26253293e44Sflorian }
26353293e44Sflorian 
26453293e44Sflorian void
26553293e44Sflorian engine_dispatch_main(int fd, short event, void *bula)
26653293e44Sflorian {
26753293e44Sflorian 	static struct rad_conf		*nconf;
26853293e44Sflorian 	static struct ra_iface_conf	*ra_iface_conf;
2698815eebdSflorian 	static struct ra_options_conf	*ra_options;
27053293e44Sflorian 	struct imsg			 imsg;
27153293e44Sflorian 	struct imsgev			*iev = bula;
27253293e44Sflorian 	struct imsgbuf			*ibuf;
27353293e44Sflorian 	struct ra_prefix_conf		*ra_prefix_conf;
2744c40b7e8Sflorian 	struct ra_rdnss_conf		*ra_rdnss_conf;
2754c40b7e8Sflorian 	struct ra_dnssl_conf		*ra_dnssl_conf;
2765207bb19Sflorian 	struct ra_pref64_conf		*pref64;
27753293e44Sflorian 	ssize_t				 n;
27853293e44Sflorian 	int				 shut = 0;
27953293e44Sflorian 
28053293e44Sflorian 	ibuf = &iev->ibuf;
28153293e44Sflorian 
28253293e44Sflorian 	if (event & EV_READ) {
283668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
284dd7efffeSclaudio 			fatal("imsgbuf_read error");
28553293e44Sflorian 		if (n == 0)	/* Connection closed. */
28653293e44Sflorian 			shut = 1;
28753293e44Sflorian 	}
28853293e44Sflorian 	if (event & EV_WRITE) {
289dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
290c1aa9554Sclaudio 			if (errno == EPIPE)	/* connection closed */
29153293e44Sflorian 				shut = 1;
292c1aa9554Sclaudio 			else
293dd7efffeSclaudio 				fatal("imsgbuf_write");
294c1aa9554Sclaudio 		}
29553293e44Sflorian 	}
29653293e44Sflorian 
29753293e44Sflorian 	for (;;) {
29853293e44Sflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
29953293e44Sflorian 			fatal("%s: imsg_get error", __func__);
30053293e44Sflorian 		if (n == 0)	/* No more messages. */
30153293e44Sflorian 			break;
30253293e44Sflorian 
30353293e44Sflorian 		switch (imsg.hdr.type) {
30453293e44Sflorian 		case IMSG_SOCKET_IPC:
30553293e44Sflorian 			/*
30653293e44Sflorian 			 * Setup pipe and event handler to the frontend
30753293e44Sflorian 			 * process.
30853293e44Sflorian 			 */
3090eb5c43bSpamela 			if (iev_frontend)
3100eb5c43bSpamela 				fatalx("%s: received unexpected imsg fd "
31153293e44Sflorian 				    "to engine", __func__);
3120eb5c43bSpamela 
313d9be77f0Sclaudio 			if ((fd = imsg_get_fd(&imsg)) == -1)
3140eb5c43bSpamela 				fatalx("%s: expected to receive imsg fd to "
31553293e44Sflorian 				   "engine but didn't receive any", __func__);
31653293e44Sflorian 
31753293e44Sflorian 			iev_frontend = malloc(sizeof(struct imsgev));
31853293e44Sflorian 			if (iev_frontend == NULL)
31953293e44Sflorian 				fatal(NULL);
32053293e44Sflorian 
321*f1b790a5Sclaudio 			if (imsgbuf_init(&iev_frontend->ibuf, fd) == -1)
322*f1b790a5Sclaudio 				fatal(NULL);
32353293e44Sflorian 			iev_frontend->handler = engine_dispatch_frontend;
32453293e44Sflorian 			iev_frontend->events = EV_READ;
32553293e44Sflorian 
32653293e44Sflorian 			event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
32753293e44Sflorian 			iev_frontend->events, iev_frontend->handler,
32853293e44Sflorian 			    iev_frontend);
32953293e44Sflorian 			event_add(&iev_frontend->ev, NULL);
33053293e44Sflorian 			break;
33153293e44Sflorian 		case IMSG_RECONF_CONF:
3322b2996d8Sflorian 			if (nconf != NULL)
3332b2996d8Sflorian 				fatalx("%s: IMSG_RECONF_CONF already in "
3342b2996d8Sflorian 				    "progress", __func__);
3350eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct rad_conf))
336ce1003a9Spamela 				fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
337ce1003a9Spamela 				    __func__, IMSG_DATA_SIZE(imsg));
33853293e44Sflorian 			if ((nconf = malloc(sizeof(struct rad_conf))) == NULL)
33953293e44Sflorian 				fatal(NULL);
34053293e44Sflorian 			memcpy(nconf, imsg.data, sizeof(struct rad_conf));
34153293e44Sflorian 			SIMPLEQ_INIT(&nconf->ra_iface_list);
3428815eebdSflorian 			SIMPLEQ_INIT(&nconf->ra_options.ra_rdnss_list);
3438815eebdSflorian 			SIMPLEQ_INIT(&nconf->ra_options.ra_dnssl_list);
3445207bb19Sflorian 			SIMPLEQ_INIT(&nconf->ra_options.ra_pref64_list);
3458815eebdSflorian 			ra_options = &nconf->ra_options;
34653293e44Sflorian 			break;
34753293e44Sflorian 		case IMSG_RECONF_RA_IFACE:
3480eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
3490eb5c43bSpamela 			    ra_iface_conf))
3500eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_RA_IFACE wrong length: "
3510eb5c43bSpamela 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
35253293e44Sflorian 			if ((ra_iface_conf = malloc(sizeof(struct
35353293e44Sflorian 			    ra_iface_conf))) == NULL)
35453293e44Sflorian 				fatal(NULL);
35553293e44Sflorian 			memcpy(ra_iface_conf, imsg.data,
35653293e44Sflorian 			    sizeof(struct ra_iface_conf));
35753293e44Sflorian 			ra_iface_conf->autoprefix = NULL;
35853293e44Sflorian 			SIMPLEQ_INIT(&ra_iface_conf->ra_prefix_list);
3598815eebdSflorian 			SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_rdnss_list);
3608815eebdSflorian 			SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_dnssl_list);
3615207bb19Sflorian 			SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_pref64_list);
36253293e44Sflorian 			SIMPLEQ_INSERT_TAIL(&nconf->ra_iface_list,
36353293e44Sflorian 			    ra_iface_conf, entry);
3648815eebdSflorian 			ra_options = &ra_iface_conf->ra_options;
36553293e44Sflorian 			break;
36653293e44Sflorian 		case IMSG_RECONF_RA_AUTOPREFIX:
3670eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
3680eb5c43bSpamela 			    ra_prefix_conf))
3690eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_RA_AUTOPREFIX wrong "
3700eb5c43bSpamela 				    "length: %lu", __func__,
3710eb5c43bSpamela 				    IMSG_DATA_SIZE(imsg));
37253293e44Sflorian 			if ((ra_iface_conf->autoprefix = malloc(sizeof(struct
37353293e44Sflorian 			    ra_prefix_conf))) == NULL)
37453293e44Sflorian 				fatal(NULL);
37553293e44Sflorian 			memcpy(ra_iface_conf->autoprefix, imsg.data,
37653293e44Sflorian 			    sizeof(struct ra_prefix_conf));
37753293e44Sflorian 			break;
37853293e44Sflorian 		case IMSG_RECONF_RA_PREFIX:
3790eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
3800eb5c43bSpamela 			    ra_prefix_conf))
3810eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_RA_PREFIX wrong "
3820eb5c43bSpamela 				    "length: %lu", __func__,
3830eb5c43bSpamela 				    IMSG_DATA_SIZE(imsg));
38453293e44Sflorian 			if ((ra_prefix_conf = malloc(sizeof(struct
38553293e44Sflorian 			    ra_prefix_conf))) == NULL)
38653293e44Sflorian 				fatal(NULL);
38753293e44Sflorian 			memcpy(ra_prefix_conf, imsg.data, sizeof(struct
38853293e44Sflorian 			    ra_prefix_conf));
38953293e44Sflorian 			SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_prefix_list,
39053293e44Sflorian 			    ra_prefix_conf, entry);
39153293e44Sflorian 			break;
3924c40b7e8Sflorian 		case IMSG_RECONF_RA_RDNSS:
3930eb5c43bSpamela 			if(IMSG_DATA_SIZE(imsg) != sizeof(struct
3940eb5c43bSpamela 			    ra_rdnss_conf))
3950eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_RA_RDNSS wrong length: "
3960eb5c43bSpamela 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
3974c40b7e8Sflorian 			if ((ra_rdnss_conf = malloc(sizeof(struct
3984c40b7e8Sflorian 			    ra_rdnss_conf))) == NULL)
3994c40b7e8Sflorian 				fatal(NULL);
4004c40b7e8Sflorian 			memcpy(ra_rdnss_conf, imsg.data, sizeof(struct
4014c40b7e8Sflorian 			    ra_rdnss_conf));
4028815eebdSflorian 			SIMPLEQ_INSERT_TAIL(&ra_options->ra_rdnss_list,
4034c40b7e8Sflorian 			    ra_rdnss_conf, entry);
4044c40b7e8Sflorian 			break;
4054c40b7e8Sflorian 		case IMSG_RECONF_RA_DNSSL:
4060eb5c43bSpamela 			if(IMSG_DATA_SIZE(imsg) != sizeof(struct
4070eb5c43bSpamela 			    ra_dnssl_conf))
4080eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_RA_DNSSL wrong length: "
4090eb5c43bSpamela 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
4104c40b7e8Sflorian 			if ((ra_dnssl_conf = malloc(sizeof(struct
4114c40b7e8Sflorian 			    ra_dnssl_conf))) == NULL)
4124c40b7e8Sflorian 				fatal(NULL);
4134c40b7e8Sflorian 			memcpy(ra_dnssl_conf, imsg.data, sizeof(struct
4144c40b7e8Sflorian 			    ra_dnssl_conf));
4158815eebdSflorian 			SIMPLEQ_INSERT_TAIL(&ra_options->ra_dnssl_list,
4164c40b7e8Sflorian 			    ra_dnssl_conf, entry);
4174c40b7e8Sflorian 			break;
4185207bb19Sflorian 		case IMSG_RECONF_RA_PREF64:
4195207bb19Sflorian 			if(IMSG_DATA_SIZE(imsg) != sizeof(struct
4205207bb19Sflorian 			    ra_pref64_conf))
4215207bb19Sflorian 				fatalx("%s: IMSG_RECONF_RA_PREF64 wrong length: "
4225207bb19Sflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
4235207bb19Sflorian 			if ((pref64 = malloc(sizeof(struct ra_pref64_conf))) ==
4245207bb19Sflorian 			    NULL)
4255207bb19Sflorian 				fatal(NULL);
4265207bb19Sflorian 			memcpy(pref64, imsg.data, sizeof(struct ra_pref64_conf));
4275207bb19Sflorian 			SIMPLEQ_INSERT_TAIL(&ra_options->ra_pref64_list, pref64,
4285207bb19Sflorian 			    entry);
4295207bb19Sflorian 			break;
43053293e44Sflorian 		case IMSG_RECONF_END:
4312b2996d8Sflorian 			if (nconf == NULL)
4322b2996d8Sflorian 				fatalx("%s: IMSG_RECONF_END without "
4332b2996d8Sflorian 				    "IMSG_RECONF_CONF", __func__);
43453293e44Sflorian 			merge_config(engine_conf, nconf);
43553293e44Sflorian 			nconf = NULL;
43653293e44Sflorian 			break;
43753293e44Sflorian 		default:
43853293e44Sflorian 			log_debug("%s: unexpected imsg %d", __func__,
43953293e44Sflorian 			    imsg.hdr.type);
44053293e44Sflorian 			break;
44153293e44Sflorian 		}
44253293e44Sflorian 		imsg_free(&imsg);
44353293e44Sflorian 	}
44453293e44Sflorian 	if (!shut)
44553293e44Sflorian 		imsg_event_add(iev);
44653293e44Sflorian 	else {
44753293e44Sflorian 		/* This pipe is dead. Remove its event handler. */
44853293e44Sflorian 		event_del(&iev->ev);
44953293e44Sflorian 		event_loopexit(NULL);
45053293e44Sflorian 	}
45153293e44Sflorian }
45253293e44Sflorian 
45353293e44Sflorian 
45453293e44Sflorian void
45553293e44Sflorian parse_ra_rs(struct imsg_ra_rs *ra_rs)
45653293e44Sflorian {
45753293e44Sflorian 	char			 ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
45853293e44Sflorian 	struct icmp6_hdr	*hdr;
45953293e44Sflorian 
46053293e44Sflorian 	hdr = (struct icmp6_hdr *) ra_rs->packet;
46153293e44Sflorian 
46253293e44Sflorian 	switch (hdr->icmp6_type) {
46353293e44Sflorian 	case ND_ROUTER_ADVERT:
46453293e44Sflorian 		parse_ra(ra_rs);
46553293e44Sflorian 		break;
46653293e44Sflorian 	case ND_ROUTER_SOLICIT:
46753293e44Sflorian 		parse_rs(ra_rs);
46853293e44Sflorian 		break;
46953293e44Sflorian 	default:
47053293e44Sflorian 		log_warnx("unexpected icmp6_type: %d from %s on %s",
47153293e44Sflorian 		    hdr->icmp6_type, inet_ntop(AF_INET6, &ra_rs->from.sin6_addr,
47253293e44Sflorian 		    ntopbuf, INET6_ADDRSTRLEN), if_indextoname(ra_rs->if_index,
47353293e44Sflorian 		    ifnamebuf));
47453293e44Sflorian 		break;
47553293e44Sflorian 	}
47653293e44Sflorian }
47753293e44Sflorian 
47853293e44Sflorian void
47953293e44Sflorian parse_ra(struct imsg_ra_rs *ra)
48053293e44Sflorian {
48153293e44Sflorian 	char			 ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
48253293e44Sflorian 	log_debug("got RA from %s on %s",
48353293e44Sflorian 	    inet_ntop(AF_INET6, &ra->from.sin6_addr, ntopbuf,
48453293e44Sflorian 	    INET6_ADDRSTRLEN), if_indextoname(ra->if_index,
48553293e44Sflorian 	    ifnamebuf));
48653293e44Sflorian 	/* XXX not yet */
48753293e44Sflorian }
48853293e44Sflorian 
48953293e44Sflorian void
49053293e44Sflorian parse_rs(struct imsg_ra_rs *rs)
49153293e44Sflorian {
49253293e44Sflorian 	struct nd_router_solicit	*nd_rs;
493a14293eaSflorian 	struct engine_iface		*engine_iface;
49453293e44Sflorian 	ssize_t				 len;
495a14293eaSflorian 	int				 unicast_ra = 0;
49653293e44Sflorian 	const char			*hbuf;
49753293e44Sflorian 	char				 ifnamebuf[IFNAMSIZ];
49853293e44Sflorian 	uint8_t				*p;
49953293e44Sflorian 
50053293e44Sflorian 	hbuf = sin6_to_str(&rs->from);
50153293e44Sflorian 
50253293e44Sflorian 	log_debug("got RS from %s on %s", hbuf, if_indextoname(rs->if_index,
50353293e44Sflorian 	    ifnamebuf));
50453293e44Sflorian 
505a14293eaSflorian 	if ((engine_iface = find_engine_iface_by_id(rs->if_index)) == NULL)
506a14293eaSflorian 		return;
507a14293eaSflorian 
50853293e44Sflorian 	len = rs->len;
50953293e44Sflorian 
51018ef43d7Sflorian 	if (!(IN6_IS_ADDR_LINKLOCAL(&rs->from.sin6_addr) ||
51118ef43d7Sflorian 	    IN6_IS_ADDR_UNSPECIFIED(&rs->from.sin6_addr))) {
51218ef43d7Sflorian 		log_warnx("RA from invalid address %s on %s", hbuf,
5134e3dc766Sclaudio 		    if_indextoname(rs->if_index, ifnamebuf));
51453293e44Sflorian 		return;
51553293e44Sflorian 	}
51653293e44Sflorian 
51753293e44Sflorian 	if ((size_t)len < sizeof(struct nd_router_solicit)) {
51853293e44Sflorian 		log_warnx("received too short message (%ld) from %s", len,
51953293e44Sflorian 		    hbuf);
52053293e44Sflorian 		return;
52153293e44Sflorian 	}
52253293e44Sflorian 
52353293e44Sflorian 	p = rs->packet;
52453293e44Sflorian 	nd_rs = (struct nd_router_solicit *)p;
52553293e44Sflorian 	len -= sizeof(struct nd_router_solicit);
52653293e44Sflorian 	p += sizeof(struct nd_router_solicit);
52753293e44Sflorian 
52853293e44Sflorian 	if (nd_rs->nd_rs_code != 0) {
52953293e44Sflorian 		log_warnx("invalid ICMPv6 code (%d) from %s", nd_rs->nd_rs_code,
53053293e44Sflorian 		    hbuf);
53153293e44Sflorian 		return;
53253293e44Sflorian 	}
53353293e44Sflorian 	while ((size_t)len >= sizeof(struct nd_opt_hdr)) {
53453293e44Sflorian 		struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p;
53553293e44Sflorian 
53653293e44Sflorian 		len -= sizeof(struct nd_opt_hdr);
53753293e44Sflorian 		p += sizeof(struct nd_opt_hdr);
53853293e44Sflorian 
53953293e44Sflorian 		if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) {
54053293e44Sflorian 			log_warnx("invalid option len: %u > %ld",
54153293e44Sflorian 			    nd_opt_hdr->nd_opt_len, len);
54253293e44Sflorian 			return;
54353293e44Sflorian 		}
54453293e44Sflorian 		switch (nd_opt_hdr->nd_opt_type) {
54553293e44Sflorian 		case ND_OPT_SOURCE_LINKADDR:
54653293e44Sflorian 			log_debug("got RS with source linkaddr option");
547a14293eaSflorian 			unicast_ra = 1;
54853293e44Sflorian 			break;
54953293e44Sflorian 		default:
55053293e44Sflorian 			log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type);
55153293e44Sflorian 			break;
55253293e44Sflorian 		}
55353293e44Sflorian 		len -= nd_opt_hdr->nd_opt_len * 8 - 2;
55453293e44Sflorian 		p += nd_opt_hdr->nd_opt_len * 8 - 2;
55553293e44Sflorian 	}
556a14293eaSflorian 
557a14293eaSflorian 	if (unicast_ra) {
558a14293eaSflorian 		struct imsg_send_ra	 send_ra;
559a14293eaSflorian 
560a14293eaSflorian 		send_ra.if_index = rs->if_index;
561a14293eaSflorian 		memcpy(&send_ra.to, &rs->from, sizeof(send_ra.to));
56253293e44Sflorian 		engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
56353293e44Sflorian 		    sizeof(send_ra));
564a14293eaSflorian 	} else {
565a14293eaSflorian 		struct timespec	 now, diff, ra_delay = {MIN_DELAY_BETWEEN_RAS, 0};
566a14293eaSflorian 		struct timeval	 tv = {0, 0};
567a14293eaSflorian 
568a14293eaSflorian 		/* a multicast RA is already scheduled within the next 3 seconds */
569a14293eaSflorian 		if (engine_iface->ras_delayed)
570a14293eaSflorian 			return;
571a14293eaSflorian 
572a14293eaSflorian 		engine_iface->ras_delayed = 1;
573a14293eaSflorian 		clock_gettime(CLOCK_MONOTONIC, &now);
574a14293eaSflorian 		timespecsub(&now, &engine_iface->last_ra, &diff);
575a14293eaSflorian 
576a14293eaSflorian 		if (timespeccmp(&diff, &ra_delay, <)) {
577a14293eaSflorian 			timespecsub(&ra_delay, &diff, &ra_delay);
578a14293eaSflorian 			TIMESPEC_TO_TIMEVAL(&tv, &ra_delay);
579a14293eaSflorian 		}
580a14293eaSflorian 
581a14293eaSflorian 		tv.tv_usec = arc4random_uniform(MAX_RA_DELAY_TIME * 1000);
582a14293eaSflorian 		evtimer_add(&engine_iface->timer, &tv);
583a14293eaSflorian 	}
58453293e44Sflorian }
5850c40990eSflorian 
5860c40990eSflorian struct engine_iface*
5870c40990eSflorian find_engine_iface_by_id(uint32_t if_index)
5880c40990eSflorian {
5890c40990eSflorian 	struct engine_iface	*engine_iface;
5900c40990eSflorian 
5910c40990eSflorian 	TAILQ_FOREACH(engine_iface, &engine_interfaces, entry) {
5920c40990eSflorian 		if (engine_iface->if_index == if_index)
5930c40990eSflorian 			return engine_iface;
5940c40990eSflorian 	}
5950c40990eSflorian 	return (NULL);
5960c40990eSflorian }
5970c40990eSflorian 
5980c40990eSflorian void
5990c40990eSflorian update_iface(uint32_t if_index)
6000c40990eSflorian {
6010c40990eSflorian 	struct engine_iface	*engine_iface;
6020c40990eSflorian 	struct timeval		 tv;
6030c40990eSflorian 
6040c40990eSflorian 	if ((engine_iface = find_engine_iface_by_id(if_index)) == NULL) {
6050c40990eSflorian 		engine_iface = calloc(1, sizeof(*engine_iface));
6060c40990eSflorian 		engine_iface->if_index = if_index;
6070c40990eSflorian 		evtimer_set(&engine_iface->timer, iface_timeout, engine_iface);
6084a78c7cfSflorian 		TAILQ_INSERT_TAIL(&engine_interfaces, engine_iface, entry);
6090c40990eSflorian 	}
6100c40990eSflorian 
6110c40990eSflorian 	tv.tv_sec = 0;
6120c40990eSflorian 	tv.tv_usec = arc4random_uniform(1000000);
6130c40990eSflorian 	evtimer_add(&engine_iface->timer, &tv);
6140c40990eSflorian }
6150c40990eSflorian 
6160c40990eSflorian void
6174a78c7cfSflorian remove_iface(uint32_t if_index)
6184a78c7cfSflorian {
6194a78c7cfSflorian 	struct engine_iface	*engine_iface;
6204a78c7cfSflorian 	struct imsg_send_ra	 send_ra;
6214a78c7cfSflorian 	char			 if_name[IF_NAMESIZE];
6224a78c7cfSflorian 
6234a78c7cfSflorian 	if ((engine_iface = find_engine_iface_by_id(if_index)) == NULL) {
6244a78c7cfSflorian 		/* we don't know this interface, frontend can delete it */
6254a78c7cfSflorian 		engine_imsg_compose_frontend(IMSG_REMOVE_IF, 0,
6264a78c7cfSflorian 		    &if_index, sizeof(if_index));
6274a78c7cfSflorian 		return;
6284a78c7cfSflorian 	}
6294a78c7cfSflorian 
6304a78c7cfSflorian 	send_ra.if_index = engine_iface->if_index;
6314a78c7cfSflorian 	memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
6324a78c7cfSflorian 
6334a78c7cfSflorian 	TAILQ_REMOVE(&engine_interfaces, engine_iface, entry);
6344a78c7cfSflorian 	evtimer_del(&engine_iface->timer);
6354a78c7cfSflorian 
6364a78c7cfSflorian 	if (if_indextoname(if_index, if_name) != NULL)
6374a78c7cfSflorian 		engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
6384a78c7cfSflorian 		    sizeof(send_ra));
6394a78c7cfSflorian 	engine_imsg_compose_frontend(IMSG_REMOVE_IF, 0,
6404a78c7cfSflorian 	    &engine_iface->if_index, sizeof(engine_iface->if_index));
6414a78c7cfSflorian 	free(engine_iface);
6424a78c7cfSflorian }
6434a78c7cfSflorian 
6444a78c7cfSflorian void
6450c40990eSflorian iface_timeout(int fd, short events, void *arg)
6460c40990eSflorian {
6470c40990eSflorian 	struct engine_iface	*engine_iface = (struct engine_iface *)arg;
6480c40990eSflorian 	struct imsg_send_ra	 send_ra;
6490c40990eSflorian 	struct timeval		 tv;
6500c40990eSflorian 
6510c40990eSflorian 	tv.tv_sec = MIN_RTR_ADV_INTERVAL +
6520c40990eSflorian 	    arc4random_uniform(MAX_RTR_ADV_INTERVAL - MIN_RTR_ADV_INTERVAL);
6530c40990eSflorian 	tv.tv_usec = arc4random_uniform(1000000);
6540c40990eSflorian 
6550c40990eSflorian 	log_debug("%s new timeout in %lld", __func__, tv.tv_sec);
6560c40990eSflorian 
6570c40990eSflorian 	evtimer_add(&engine_iface->timer, &tv);
6580c40990eSflorian 
6590c40990eSflorian 	send_ra.if_index = engine_iface->if_index;
6600c40990eSflorian 	memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
6610c40990eSflorian 	engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
6620c40990eSflorian 	    sizeof(send_ra));
663a14293eaSflorian 	clock_gettime(CLOCK_MONOTONIC, &engine_iface->last_ra);
664a14293eaSflorian 	engine_iface->ras_delayed = 0;
6650c40990eSflorian }
666