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