1*d9be77f0Sclaudio /* $OpenBSD: engine.c,v 1.22 2023/12/14 11:09:34 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
engine_sig_handler(int sig,short event,void * arg)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
engine(int debug,int verbose)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
13753293e44Sflorian imsg_init(&iev_main->ibuf, 3);
13853293e44Sflorian iev_main->handler = engine_dispatch_main;
13953293e44Sflorian
14053293e44Sflorian /* Setup event handlers. */
14153293e44Sflorian iev_main->events = EV_READ;
14253293e44Sflorian event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
14353293e44Sflorian iev_main->handler, iev_main);
14453293e44Sflorian event_add(&iev_main->ev, NULL);
14553293e44Sflorian
14653293e44Sflorian all_nodes.sin6_len = sizeof(all_nodes);
14753293e44Sflorian all_nodes.sin6_family = AF_INET6;
14853293e44Sflorian if (inet_pton(AF_INET6, "ff02::1", &all_nodes.sin6_addr) != 1)
14953293e44Sflorian fatal("inet_pton");
15053293e44Sflorian
1514a78c7cfSflorian TAILQ_INIT(&engine_interfaces);
1524a78c7cfSflorian
15353293e44Sflorian event_dispatch();
15453293e44Sflorian
15553293e44Sflorian engine_shutdown();
15653293e44Sflorian }
15753293e44Sflorian
15853293e44Sflorian __dead void
engine_shutdown(void)15953293e44Sflorian engine_shutdown(void)
16053293e44Sflorian {
16153293e44Sflorian /* Close pipes. */
16253293e44Sflorian msgbuf_clear(&iev_frontend->ibuf.w);
16353293e44Sflorian close(iev_frontend->ibuf.fd);
16453293e44Sflorian msgbuf_clear(&iev_main->ibuf.w);
16553293e44Sflorian close(iev_main->ibuf.fd);
16653293e44Sflorian
16753293e44Sflorian config_clear(engine_conf);
16853293e44Sflorian
16953293e44Sflorian free(iev_frontend);
17053293e44Sflorian free(iev_main);
17153293e44Sflorian
17253293e44Sflorian log_info("engine exiting");
17353293e44Sflorian exit(0);
17453293e44Sflorian }
17553293e44Sflorian
17653293e44Sflorian int
engine_imsg_compose_frontend(int type,pid_t pid,void * data,uint16_t datalen)17753293e44Sflorian engine_imsg_compose_frontend(int type, pid_t pid, void *data, uint16_t datalen)
17853293e44Sflorian {
17953293e44Sflorian return (imsg_compose_event(iev_frontend, type, 0, pid, -1,
18053293e44Sflorian data, datalen));
18153293e44Sflorian }
18253293e44Sflorian
18353293e44Sflorian void
engine_dispatch_frontend(int fd,short event,void * bula)18453293e44Sflorian engine_dispatch_frontend(int fd, short event, void *bula)
18553293e44Sflorian {
18653293e44Sflorian struct imsgev *iev = bula;
18753293e44Sflorian struct imsgbuf *ibuf;
18853293e44Sflorian struct imsg imsg;
18953293e44Sflorian struct imsg_ra_rs ra_rs;
19053293e44Sflorian ssize_t n;
1910c40990eSflorian uint32_t if_index;
19253293e44Sflorian int shut = 0, verbose;
19353293e44Sflorian
19453293e44Sflorian ibuf = &iev->ibuf;
19553293e44Sflorian
19653293e44Sflorian if (event & EV_READ) {
19753293e44Sflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
19853293e44Sflorian fatal("imsg_read error");
19953293e44Sflorian if (n == 0) /* Connection closed. */
20053293e44Sflorian shut = 1;
20153293e44Sflorian }
20253293e44Sflorian if (event & EV_WRITE) {
20353293e44Sflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
20453293e44Sflorian fatal("msgbuf_write");
20553293e44Sflorian if (n == 0) /* Connection closed. */
20653293e44Sflorian shut = 1;
20753293e44Sflorian }
20853293e44Sflorian
20953293e44Sflorian for (;;) {
21053293e44Sflorian if ((n = imsg_get(ibuf, &imsg)) == -1)
21153293e44Sflorian fatal("%s: imsg_get error", __func__);
21253293e44Sflorian if (n == 0) /* No more messages. */
21353293e44Sflorian break;
21453293e44Sflorian
21553293e44Sflorian switch (imsg.hdr.type) {
21653293e44Sflorian case IMSG_RA_RS:
217b17c900dSpamela if (IMSG_DATA_SIZE(imsg) != sizeof(ra_rs))
21851a61992Spamela fatalx("%s: IMSG_RA_RS wrong length: %lu",
219b17c900dSpamela __func__, IMSG_DATA_SIZE(imsg));
22053293e44Sflorian memcpy(&ra_rs, imsg.data, sizeof(ra_rs));
22153293e44Sflorian parse_ra_rs(&ra_rs);
22253293e44Sflorian break;
2230c40990eSflorian case IMSG_UPDATE_IF:
224b17c900dSpamela if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
22551a61992Spamela fatalx("%s: IMSG_UPDATE_IF wrong length: %lu",
226b17c900dSpamela __func__, IMSG_DATA_SIZE(imsg));
2270c40990eSflorian memcpy(&if_index, imsg.data, sizeof(if_index));
2280c40990eSflorian update_iface(if_index);
2290c40990eSflorian break;
2304a78c7cfSflorian case IMSG_REMOVE_IF:
231b17c900dSpamela if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
23251a61992Spamela fatalx("%s: IMSG_REMOVE_IF wrong length: %lu",
233b17c900dSpamela __func__, IMSG_DATA_SIZE(imsg));
2344a78c7cfSflorian memcpy(&if_index, imsg.data, sizeof(if_index));
2354a78c7cfSflorian remove_iface(if_index);
2364a78c7cfSflorian break;
23753293e44Sflorian case IMSG_CTL_LOG_VERBOSE:
2380eb5c43bSpamela if (IMSG_DATA_SIZE(imsg) != sizeof(verbose))
2390eb5c43bSpamela fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: "
2400eb5c43bSpamela "%lu", __func__, IMSG_DATA_SIZE(imsg));
24153293e44Sflorian memcpy(&verbose, imsg.data, sizeof(verbose));
24253293e44Sflorian log_setverbose(verbose);
24353293e44Sflorian break;
24453293e44Sflorian default:
24553293e44Sflorian log_debug("%s: unexpected imsg %d", __func__,
24653293e44Sflorian imsg.hdr.type);
24753293e44Sflorian break;
24853293e44Sflorian }
24953293e44Sflorian imsg_free(&imsg);
25053293e44Sflorian }
25153293e44Sflorian if (!shut)
25253293e44Sflorian imsg_event_add(iev);
25353293e44Sflorian else {
25453293e44Sflorian /* This pipe is dead. Remove its event handler. */
25553293e44Sflorian event_del(&iev->ev);
25653293e44Sflorian event_loopexit(NULL);
25753293e44Sflorian }
25853293e44Sflorian }
25953293e44Sflorian
26053293e44Sflorian void
engine_dispatch_main(int fd,short event,void * bula)26153293e44Sflorian engine_dispatch_main(int fd, short event, void *bula)
26253293e44Sflorian {
26353293e44Sflorian static struct rad_conf *nconf;
26453293e44Sflorian static struct ra_iface_conf *ra_iface_conf;
2658815eebdSflorian static struct ra_options_conf *ra_options;
26653293e44Sflorian struct imsg imsg;
26753293e44Sflorian struct imsgev *iev = bula;
26853293e44Sflorian struct imsgbuf *ibuf;
26953293e44Sflorian struct ra_prefix_conf *ra_prefix_conf;
2704c40b7e8Sflorian struct ra_rdnss_conf *ra_rdnss_conf;
2714c40b7e8Sflorian struct ra_dnssl_conf *ra_dnssl_conf;
2725207bb19Sflorian struct ra_pref64_conf *pref64;
27353293e44Sflorian ssize_t n;
27453293e44Sflorian int shut = 0;
27553293e44Sflorian
27653293e44Sflorian ibuf = &iev->ibuf;
27753293e44Sflorian
27853293e44Sflorian if (event & EV_READ) {
27953293e44Sflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
28053293e44Sflorian fatal("imsg_read error");
28153293e44Sflorian if (n == 0) /* Connection closed. */
28253293e44Sflorian shut = 1;
28353293e44Sflorian }
28453293e44Sflorian if (event & EV_WRITE) {
28553293e44Sflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
28653293e44Sflorian fatal("msgbuf_write");
28753293e44Sflorian if (n == 0) /* Connection closed. */
28853293e44Sflorian shut = 1;
28953293e44Sflorian }
29053293e44Sflorian
29153293e44Sflorian for (;;) {
29253293e44Sflorian if ((n = imsg_get(ibuf, &imsg)) == -1)
29353293e44Sflorian fatal("%s: imsg_get error", __func__);
29453293e44Sflorian if (n == 0) /* No more messages. */
29553293e44Sflorian break;
29653293e44Sflorian
29753293e44Sflorian switch (imsg.hdr.type) {
29853293e44Sflorian case IMSG_SOCKET_IPC:
29953293e44Sflorian /*
30053293e44Sflorian * Setup pipe and event handler to the frontend
30153293e44Sflorian * process.
30253293e44Sflorian */
3030eb5c43bSpamela if (iev_frontend)
3040eb5c43bSpamela fatalx("%s: received unexpected imsg fd "
30553293e44Sflorian "to engine", __func__);
3060eb5c43bSpamela
307*d9be77f0Sclaudio if ((fd = imsg_get_fd(&imsg)) == -1)
3080eb5c43bSpamela fatalx("%s: expected to receive imsg fd to "
30953293e44Sflorian "engine but didn't receive any", __func__);
31053293e44Sflorian
31153293e44Sflorian iev_frontend = malloc(sizeof(struct imsgev));
31253293e44Sflorian if (iev_frontend == NULL)
31353293e44Sflorian fatal(NULL);
31453293e44Sflorian
31553293e44Sflorian imsg_init(&iev_frontend->ibuf, fd);
31653293e44Sflorian iev_frontend->handler = engine_dispatch_frontend;
31753293e44Sflorian iev_frontend->events = EV_READ;
31853293e44Sflorian
31953293e44Sflorian event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
32053293e44Sflorian iev_frontend->events, iev_frontend->handler,
32153293e44Sflorian iev_frontend);
32253293e44Sflorian event_add(&iev_frontend->ev, NULL);
32353293e44Sflorian break;
32453293e44Sflorian case IMSG_RECONF_CONF:
3252b2996d8Sflorian if (nconf != NULL)
3262b2996d8Sflorian fatalx("%s: IMSG_RECONF_CONF already in "
3272b2996d8Sflorian "progress", __func__);
3280eb5c43bSpamela if (IMSG_DATA_SIZE(imsg) != sizeof(struct rad_conf))
329ce1003a9Spamela fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
330ce1003a9Spamela __func__, IMSG_DATA_SIZE(imsg));
33153293e44Sflorian if ((nconf = malloc(sizeof(struct rad_conf))) == NULL)
33253293e44Sflorian fatal(NULL);
33353293e44Sflorian memcpy(nconf, imsg.data, sizeof(struct rad_conf));
33453293e44Sflorian SIMPLEQ_INIT(&nconf->ra_iface_list);
3358815eebdSflorian SIMPLEQ_INIT(&nconf->ra_options.ra_rdnss_list);
3368815eebdSflorian SIMPLEQ_INIT(&nconf->ra_options.ra_dnssl_list);
3375207bb19Sflorian SIMPLEQ_INIT(&nconf->ra_options.ra_pref64_list);
3388815eebdSflorian ra_options = &nconf->ra_options;
33953293e44Sflorian break;
34053293e44Sflorian case IMSG_RECONF_RA_IFACE:
3410eb5c43bSpamela if (IMSG_DATA_SIZE(imsg) != sizeof(struct
3420eb5c43bSpamela ra_iface_conf))
3430eb5c43bSpamela fatalx("%s: IMSG_RECONF_RA_IFACE wrong length: "
3440eb5c43bSpamela "%lu", __func__, IMSG_DATA_SIZE(imsg));
34553293e44Sflorian if ((ra_iface_conf = malloc(sizeof(struct
34653293e44Sflorian ra_iface_conf))) == NULL)
34753293e44Sflorian fatal(NULL);
34853293e44Sflorian memcpy(ra_iface_conf, imsg.data,
34953293e44Sflorian sizeof(struct ra_iface_conf));
35053293e44Sflorian ra_iface_conf->autoprefix = NULL;
35153293e44Sflorian SIMPLEQ_INIT(&ra_iface_conf->ra_prefix_list);
3528815eebdSflorian SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_rdnss_list);
3538815eebdSflorian SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_dnssl_list);
3545207bb19Sflorian SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_pref64_list);
35553293e44Sflorian SIMPLEQ_INSERT_TAIL(&nconf->ra_iface_list,
35653293e44Sflorian ra_iface_conf, entry);
3578815eebdSflorian ra_options = &ra_iface_conf->ra_options;
35853293e44Sflorian break;
35953293e44Sflorian case IMSG_RECONF_RA_AUTOPREFIX:
3600eb5c43bSpamela if (IMSG_DATA_SIZE(imsg) != sizeof(struct
3610eb5c43bSpamela ra_prefix_conf))
3620eb5c43bSpamela fatalx("%s: IMSG_RECONF_RA_AUTOPREFIX wrong "
3630eb5c43bSpamela "length: %lu", __func__,
3640eb5c43bSpamela IMSG_DATA_SIZE(imsg));
36553293e44Sflorian if ((ra_iface_conf->autoprefix = malloc(sizeof(struct
36653293e44Sflorian ra_prefix_conf))) == NULL)
36753293e44Sflorian fatal(NULL);
36853293e44Sflorian memcpy(ra_iface_conf->autoprefix, imsg.data,
36953293e44Sflorian sizeof(struct ra_prefix_conf));
37053293e44Sflorian break;
37153293e44Sflorian case IMSG_RECONF_RA_PREFIX:
3720eb5c43bSpamela if (IMSG_DATA_SIZE(imsg) != sizeof(struct
3730eb5c43bSpamela ra_prefix_conf))
3740eb5c43bSpamela fatalx("%s: IMSG_RECONF_RA_PREFIX wrong "
3750eb5c43bSpamela "length: %lu", __func__,
3760eb5c43bSpamela IMSG_DATA_SIZE(imsg));
37753293e44Sflorian if ((ra_prefix_conf = malloc(sizeof(struct
37853293e44Sflorian ra_prefix_conf))) == NULL)
37953293e44Sflorian fatal(NULL);
38053293e44Sflorian memcpy(ra_prefix_conf, imsg.data, sizeof(struct
38153293e44Sflorian ra_prefix_conf));
38253293e44Sflorian SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_prefix_list,
38353293e44Sflorian ra_prefix_conf, entry);
38453293e44Sflorian break;
3854c40b7e8Sflorian case IMSG_RECONF_RA_RDNSS:
3860eb5c43bSpamela if(IMSG_DATA_SIZE(imsg) != sizeof(struct
3870eb5c43bSpamela ra_rdnss_conf))
3880eb5c43bSpamela fatalx("%s: IMSG_RECONF_RA_RDNSS wrong length: "
3890eb5c43bSpamela "%lu", __func__, IMSG_DATA_SIZE(imsg));
3904c40b7e8Sflorian if ((ra_rdnss_conf = malloc(sizeof(struct
3914c40b7e8Sflorian ra_rdnss_conf))) == NULL)
3924c40b7e8Sflorian fatal(NULL);
3934c40b7e8Sflorian memcpy(ra_rdnss_conf, imsg.data, sizeof(struct
3944c40b7e8Sflorian ra_rdnss_conf));
3958815eebdSflorian SIMPLEQ_INSERT_TAIL(&ra_options->ra_rdnss_list,
3964c40b7e8Sflorian ra_rdnss_conf, entry);
3974c40b7e8Sflorian break;
3984c40b7e8Sflorian case IMSG_RECONF_RA_DNSSL:
3990eb5c43bSpamela if(IMSG_DATA_SIZE(imsg) != sizeof(struct
4000eb5c43bSpamela ra_dnssl_conf))
4010eb5c43bSpamela fatalx("%s: IMSG_RECONF_RA_DNSSL wrong length: "
4020eb5c43bSpamela "%lu", __func__, IMSG_DATA_SIZE(imsg));
4034c40b7e8Sflorian if ((ra_dnssl_conf = malloc(sizeof(struct
4044c40b7e8Sflorian ra_dnssl_conf))) == NULL)
4054c40b7e8Sflorian fatal(NULL);
4064c40b7e8Sflorian memcpy(ra_dnssl_conf, imsg.data, sizeof(struct
4074c40b7e8Sflorian ra_dnssl_conf));
4088815eebdSflorian SIMPLEQ_INSERT_TAIL(&ra_options->ra_dnssl_list,
4094c40b7e8Sflorian ra_dnssl_conf, entry);
4104c40b7e8Sflorian break;
4115207bb19Sflorian case IMSG_RECONF_RA_PREF64:
4125207bb19Sflorian if(IMSG_DATA_SIZE(imsg) != sizeof(struct
4135207bb19Sflorian ra_pref64_conf))
4145207bb19Sflorian fatalx("%s: IMSG_RECONF_RA_PREF64 wrong length: "
4155207bb19Sflorian "%lu", __func__, IMSG_DATA_SIZE(imsg));
4165207bb19Sflorian if ((pref64 = malloc(sizeof(struct ra_pref64_conf))) ==
4175207bb19Sflorian NULL)
4185207bb19Sflorian fatal(NULL);
4195207bb19Sflorian memcpy(pref64, imsg.data, sizeof(struct ra_pref64_conf));
4205207bb19Sflorian SIMPLEQ_INSERT_TAIL(&ra_options->ra_pref64_list, pref64,
4215207bb19Sflorian entry);
4225207bb19Sflorian break;
42353293e44Sflorian case IMSG_RECONF_END:
4242b2996d8Sflorian if (nconf == NULL)
4252b2996d8Sflorian fatalx("%s: IMSG_RECONF_END without "
4262b2996d8Sflorian "IMSG_RECONF_CONF", __func__);
42753293e44Sflorian merge_config(engine_conf, nconf);
42853293e44Sflorian nconf = NULL;
42953293e44Sflorian break;
43053293e44Sflorian default:
43153293e44Sflorian log_debug("%s: unexpected imsg %d", __func__,
43253293e44Sflorian imsg.hdr.type);
43353293e44Sflorian break;
43453293e44Sflorian }
43553293e44Sflorian imsg_free(&imsg);
43653293e44Sflorian }
43753293e44Sflorian if (!shut)
43853293e44Sflorian imsg_event_add(iev);
43953293e44Sflorian else {
44053293e44Sflorian /* This pipe is dead. Remove its event handler. */
44153293e44Sflorian event_del(&iev->ev);
44253293e44Sflorian event_loopexit(NULL);
44353293e44Sflorian }
44453293e44Sflorian }
44553293e44Sflorian
44653293e44Sflorian
44753293e44Sflorian void
parse_ra_rs(struct imsg_ra_rs * ra_rs)44853293e44Sflorian parse_ra_rs(struct imsg_ra_rs *ra_rs)
44953293e44Sflorian {
45053293e44Sflorian char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
45153293e44Sflorian struct icmp6_hdr *hdr;
45253293e44Sflorian
45353293e44Sflorian hdr = (struct icmp6_hdr *) ra_rs->packet;
45453293e44Sflorian
45553293e44Sflorian switch (hdr->icmp6_type) {
45653293e44Sflorian case ND_ROUTER_ADVERT:
45753293e44Sflorian parse_ra(ra_rs);
45853293e44Sflorian break;
45953293e44Sflorian case ND_ROUTER_SOLICIT:
46053293e44Sflorian parse_rs(ra_rs);
46153293e44Sflorian break;
46253293e44Sflorian default:
46353293e44Sflorian log_warnx("unexpected icmp6_type: %d from %s on %s",
46453293e44Sflorian hdr->icmp6_type, inet_ntop(AF_INET6, &ra_rs->from.sin6_addr,
46553293e44Sflorian ntopbuf, INET6_ADDRSTRLEN), if_indextoname(ra_rs->if_index,
46653293e44Sflorian ifnamebuf));
46753293e44Sflorian break;
46853293e44Sflorian }
46953293e44Sflorian }
47053293e44Sflorian
47153293e44Sflorian void
parse_ra(struct imsg_ra_rs * ra)47253293e44Sflorian parse_ra(struct imsg_ra_rs *ra)
47353293e44Sflorian {
47453293e44Sflorian char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
47553293e44Sflorian log_debug("got RA from %s on %s",
47653293e44Sflorian inet_ntop(AF_INET6, &ra->from.sin6_addr, ntopbuf,
47753293e44Sflorian INET6_ADDRSTRLEN), if_indextoname(ra->if_index,
47853293e44Sflorian ifnamebuf));
47953293e44Sflorian /* XXX not yet */
48053293e44Sflorian }
48153293e44Sflorian
48253293e44Sflorian void
parse_rs(struct imsg_ra_rs * rs)48353293e44Sflorian parse_rs(struct imsg_ra_rs *rs)
48453293e44Sflorian {
48553293e44Sflorian struct nd_router_solicit *nd_rs;
486a14293eaSflorian struct engine_iface *engine_iface;
48753293e44Sflorian ssize_t len;
488a14293eaSflorian int unicast_ra = 0;
48953293e44Sflorian const char *hbuf;
49053293e44Sflorian char ifnamebuf[IFNAMSIZ];
49153293e44Sflorian uint8_t *p;
49253293e44Sflorian
49353293e44Sflorian hbuf = sin6_to_str(&rs->from);
49453293e44Sflorian
49553293e44Sflorian log_debug("got RS from %s on %s", hbuf, if_indextoname(rs->if_index,
49653293e44Sflorian ifnamebuf));
49753293e44Sflorian
498a14293eaSflorian if ((engine_iface = find_engine_iface_by_id(rs->if_index)) == NULL)
499a14293eaSflorian return;
500a14293eaSflorian
50153293e44Sflorian len = rs->len;
50253293e44Sflorian
50318ef43d7Sflorian if (!(IN6_IS_ADDR_LINKLOCAL(&rs->from.sin6_addr) ||
50418ef43d7Sflorian IN6_IS_ADDR_UNSPECIFIED(&rs->from.sin6_addr))) {
50518ef43d7Sflorian log_warnx("RA from invalid address %s on %s", hbuf,
5064e3dc766Sclaudio if_indextoname(rs->if_index, ifnamebuf));
50753293e44Sflorian return;
50853293e44Sflorian }
50953293e44Sflorian
51053293e44Sflorian if ((size_t)len < sizeof(struct nd_router_solicit)) {
51153293e44Sflorian log_warnx("received too short message (%ld) from %s", len,
51253293e44Sflorian hbuf);
51353293e44Sflorian return;
51453293e44Sflorian }
51553293e44Sflorian
51653293e44Sflorian p = rs->packet;
51753293e44Sflorian nd_rs = (struct nd_router_solicit *)p;
51853293e44Sflorian len -= sizeof(struct nd_router_solicit);
51953293e44Sflorian p += sizeof(struct nd_router_solicit);
52053293e44Sflorian
52153293e44Sflorian if (nd_rs->nd_rs_code != 0) {
52253293e44Sflorian log_warnx("invalid ICMPv6 code (%d) from %s", nd_rs->nd_rs_code,
52353293e44Sflorian hbuf);
52453293e44Sflorian return;
52553293e44Sflorian }
52653293e44Sflorian while ((size_t)len >= sizeof(struct nd_opt_hdr)) {
52753293e44Sflorian struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p;
52853293e44Sflorian
52953293e44Sflorian len -= sizeof(struct nd_opt_hdr);
53053293e44Sflorian p += sizeof(struct nd_opt_hdr);
53153293e44Sflorian
53253293e44Sflorian if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) {
53353293e44Sflorian log_warnx("invalid option len: %u > %ld",
53453293e44Sflorian nd_opt_hdr->nd_opt_len, len);
53553293e44Sflorian return;
53653293e44Sflorian }
53753293e44Sflorian switch (nd_opt_hdr->nd_opt_type) {
53853293e44Sflorian case ND_OPT_SOURCE_LINKADDR:
53953293e44Sflorian log_debug("got RS with source linkaddr option");
540a14293eaSflorian unicast_ra = 1;
54153293e44Sflorian break;
54253293e44Sflorian default:
54353293e44Sflorian log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type);
54453293e44Sflorian break;
54553293e44Sflorian }
54653293e44Sflorian len -= nd_opt_hdr->nd_opt_len * 8 - 2;
54753293e44Sflorian p += nd_opt_hdr->nd_opt_len * 8 - 2;
54853293e44Sflorian }
549a14293eaSflorian
550a14293eaSflorian if (unicast_ra) {
551a14293eaSflorian struct imsg_send_ra send_ra;
552a14293eaSflorian
553a14293eaSflorian send_ra.if_index = rs->if_index;
554a14293eaSflorian memcpy(&send_ra.to, &rs->from, sizeof(send_ra.to));
55553293e44Sflorian engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
55653293e44Sflorian sizeof(send_ra));
557a14293eaSflorian } else {
558a14293eaSflorian struct timespec now, diff, ra_delay = {MIN_DELAY_BETWEEN_RAS, 0};
559a14293eaSflorian struct timeval tv = {0, 0};
560a14293eaSflorian
561a14293eaSflorian /* a multicast RA is already scheduled within the next 3 seconds */
562a14293eaSflorian if (engine_iface->ras_delayed)
563a14293eaSflorian return;
564a14293eaSflorian
565a14293eaSflorian engine_iface->ras_delayed = 1;
566a14293eaSflorian clock_gettime(CLOCK_MONOTONIC, &now);
567a14293eaSflorian timespecsub(&now, &engine_iface->last_ra, &diff);
568a14293eaSflorian
569a14293eaSflorian if (timespeccmp(&diff, &ra_delay, <)) {
570a14293eaSflorian timespecsub(&ra_delay, &diff, &ra_delay);
571a14293eaSflorian TIMESPEC_TO_TIMEVAL(&tv, &ra_delay);
572a14293eaSflorian }
573a14293eaSflorian
574a14293eaSflorian tv.tv_usec = arc4random_uniform(MAX_RA_DELAY_TIME * 1000);
575a14293eaSflorian evtimer_add(&engine_iface->timer, &tv);
576a14293eaSflorian }
57753293e44Sflorian }
5780c40990eSflorian
5790c40990eSflorian struct engine_iface*
find_engine_iface_by_id(uint32_t if_index)5800c40990eSflorian find_engine_iface_by_id(uint32_t if_index)
5810c40990eSflorian {
5820c40990eSflorian struct engine_iface *engine_iface;
5830c40990eSflorian
5840c40990eSflorian TAILQ_FOREACH(engine_iface, &engine_interfaces, entry) {
5850c40990eSflorian if (engine_iface->if_index == if_index)
5860c40990eSflorian return engine_iface;
5870c40990eSflorian }
5880c40990eSflorian return (NULL);
5890c40990eSflorian }
5900c40990eSflorian
5910c40990eSflorian void
update_iface(uint32_t if_index)5920c40990eSflorian update_iface(uint32_t if_index)
5930c40990eSflorian {
5940c40990eSflorian struct engine_iface *engine_iface;
5950c40990eSflorian struct timeval tv;
5960c40990eSflorian
5970c40990eSflorian if ((engine_iface = find_engine_iface_by_id(if_index)) == NULL) {
5980c40990eSflorian engine_iface = calloc(1, sizeof(*engine_iface));
5990c40990eSflorian engine_iface->if_index = if_index;
6000c40990eSflorian evtimer_set(&engine_iface->timer, iface_timeout, engine_iface);
6014a78c7cfSflorian TAILQ_INSERT_TAIL(&engine_interfaces, engine_iface, entry);
6020c40990eSflorian }
6030c40990eSflorian
6040c40990eSflorian tv.tv_sec = 0;
6050c40990eSflorian tv.tv_usec = arc4random_uniform(1000000);
6060c40990eSflorian evtimer_add(&engine_iface->timer, &tv);
6070c40990eSflorian }
6080c40990eSflorian
6090c40990eSflorian void
remove_iface(uint32_t if_index)6104a78c7cfSflorian remove_iface(uint32_t if_index)
6114a78c7cfSflorian {
6124a78c7cfSflorian struct engine_iface *engine_iface;
6134a78c7cfSflorian struct imsg_send_ra send_ra;
6144a78c7cfSflorian char if_name[IF_NAMESIZE];
6154a78c7cfSflorian
6164a78c7cfSflorian if ((engine_iface = find_engine_iface_by_id(if_index)) == NULL) {
6174a78c7cfSflorian /* we don't know this interface, frontend can delete it */
6184a78c7cfSflorian engine_imsg_compose_frontend(IMSG_REMOVE_IF, 0,
6194a78c7cfSflorian &if_index, sizeof(if_index));
6204a78c7cfSflorian return;
6214a78c7cfSflorian }
6224a78c7cfSflorian
6234a78c7cfSflorian send_ra.if_index = engine_iface->if_index;
6244a78c7cfSflorian memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
6254a78c7cfSflorian
6264a78c7cfSflorian TAILQ_REMOVE(&engine_interfaces, engine_iface, entry);
6274a78c7cfSflorian evtimer_del(&engine_iface->timer);
6284a78c7cfSflorian
6294a78c7cfSflorian if (if_indextoname(if_index, if_name) != NULL)
6304a78c7cfSflorian engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
6314a78c7cfSflorian sizeof(send_ra));
6324a78c7cfSflorian engine_imsg_compose_frontend(IMSG_REMOVE_IF, 0,
6334a78c7cfSflorian &engine_iface->if_index, sizeof(engine_iface->if_index));
6344a78c7cfSflorian free(engine_iface);
6354a78c7cfSflorian }
6364a78c7cfSflorian
6374a78c7cfSflorian void
iface_timeout(int fd,short events,void * arg)6380c40990eSflorian iface_timeout(int fd, short events, void *arg)
6390c40990eSflorian {
6400c40990eSflorian struct engine_iface *engine_iface = (struct engine_iface *)arg;
6410c40990eSflorian struct imsg_send_ra send_ra;
6420c40990eSflorian struct timeval tv;
6430c40990eSflorian
6440c40990eSflorian tv.tv_sec = MIN_RTR_ADV_INTERVAL +
6450c40990eSflorian arc4random_uniform(MAX_RTR_ADV_INTERVAL - MIN_RTR_ADV_INTERVAL);
6460c40990eSflorian tv.tv_usec = arc4random_uniform(1000000);
6470c40990eSflorian
6480c40990eSflorian log_debug("%s new timeout in %lld", __func__, tv.tv_sec);
6490c40990eSflorian
6500c40990eSflorian evtimer_add(&engine_iface->timer, &tv);
6510c40990eSflorian
6520c40990eSflorian send_ra.if_index = engine_iface->if_index;
6530c40990eSflorian memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
6540c40990eSflorian engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
6550c40990eSflorian sizeof(send_ra));
656a14293eaSflorian clock_gettime(CLOCK_MONOTONIC, &engine_iface->last_ra);
657a14293eaSflorian engine_iface->ras_delayed = 0;
6580c40990eSflorian }
659