1*5b2dcb8fSdlg /* $OpenBSD: dhcpleased.c,v 1.24 2022/03/21 04:35:41 dlg Exp $ */ 257419a7fSflorian 357419a7fSflorian /* 457419a7fSflorian * Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org> 557419a7fSflorian * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 657419a7fSflorian * Copyright (c) 2004 Esben Norby <norby@openbsd.org> 757419a7fSflorian * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 857419a7fSflorian * 957419a7fSflorian * Permission to use, copy, modify, and distribute this software for any 1057419a7fSflorian * purpose with or without fee is hereby granted, provided that the above 1157419a7fSflorian * copyright notice and this permission notice appear in all copies. 1257419a7fSflorian * 1357419a7fSflorian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1457419a7fSflorian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1557419a7fSflorian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1657419a7fSflorian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1757419a7fSflorian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1857419a7fSflorian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1957419a7fSflorian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 2057419a7fSflorian */ 2157419a7fSflorian #include <sys/types.h> 2257419a7fSflorian #include <sys/ioctl.h> 2357419a7fSflorian #include <sys/queue.h> 2457419a7fSflorian #include <sys/socket.h> 2557419a7fSflorian #include <sys/stat.h> 2657419a7fSflorian #include <sys/syslog.h> 2757419a7fSflorian #include <sys/sysctl.h> 2857419a7fSflorian #include <sys/uio.h> 2957419a7fSflorian #include <sys/wait.h> 3057419a7fSflorian 3157419a7fSflorian #include <net/if.h> 3257419a7fSflorian #include <net/route.h> 3357419a7fSflorian #include <netinet/in.h> 3457419a7fSflorian #include <netinet/if_ether.h> 3557419a7fSflorian #include <netinet/in_var.h> 3657419a7fSflorian 3757419a7fSflorian #include <arpa/inet.h> 3857419a7fSflorian 3957419a7fSflorian #include <err.h> 4057419a7fSflorian #include <errno.h> 4157419a7fSflorian #include <fcntl.h> 4257419a7fSflorian #include <event.h> 4357419a7fSflorian #include <ifaddrs.h> 4457419a7fSflorian #include <imsg.h> 4557419a7fSflorian #include <netdb.h> 4657419a7fSflorian #include <pwd.h> 4757419a7fSflorian #include <stddef.h> 4857419a7fSflorian #include <stdio.h> 4957419a7fSflorian #include <stdlib.h> 5057419a7fSflorian #include <string.h> 5157419a7fSflorian #include <signal.h> 5257419a7fSflorian #include <unistd.h> 5357419a7fSflorian 5457419a7fSflorian #include "bpf.h" 5557419a7fSflorian #include "log.h" 5657419a7fSflorian #include "dhcpleased.h" 5757419a7fSflorian #include "frontend.h" 5857419a7fSflorian #include "engine.h" 5957419a7fSflorian #include "control.h" 6057419a7fSflorian 6157419a7fSflorian enum dhcpleased_process { 6257419a7fSflorian PROC_MAIN, 6357419a7fSflorian PROC_ENGINE, 6457419a7fSflorian PROC_FRONTEND 6557419a7fSflorian }; 6657419a7fSflorian 6757419a7fSflorian __dead void usage(void); 6857419a7fSflorian __dead void main_shutdown(void); 6957419a7fSflorian 7057419a7fSflorian void main_sig_handler(int, short, void *); 7157419a7fSflorian 7257419a7fSflorian static pid_t start_child(enum dhcpleased_process, char *, int, int, int); 7357419a7fSflorian 7457419a7fSflorian void main_dispatch_frontend(int, short, void *); 7557419a7fSflorian void main_dispatch_engine(int, short, void *); 7657419a7fSflorian void open_bpfsock(uint32_t); 7757419a7fSflorian void configure_interface(struct imsg_configure_interface *); 7857419a7fSflorian void deconfigure_interface(struct imsg_configure_interface *); 7957419a7fSflorian void propose_rdns(struct imsg_propose_rdns *); 80351dd593Sflorian void configure_routes(uint8_t, struct imsg_configure_interface *); 81351dd593Sflorian void configure_route(uint8_t, uint32_t, int, struct sockaddr_in *, struct 82351dd593Sflorian sockaddr_in *, struct sockaddr_in *, struct sockaddr_in *, int); 836e93e3e9Sflorian void read_lease_file(struct imsg_ifinfo *); 8457419a7fSflorian 8557419a7fSflorian static int main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *); 8657419a7fSflorian int main_imsg_compose_frontend(int, int, void *, uint16_t); 8757419a7fSflorian int main_imsg_compose_engine(int, int, void *, uint16_t); 8857419a7fSflorian 89a41cc082Sflorian #ifndef SMALL 90a41cc082Sflorian int main_imsg_send_config(struct dhcpleased_conf *); 91a41cc082Sflorian #endif /* SMALL */ 92a41cc082Sflorian int main_reload(void); 93a41cc082Sflorian 9457419a7fSflorian static struct imsgev *iev_frontend; 9557419a7fSflorian static struct imsgev *iev_engine; 9657419a7fSflorian 97a41cc082Sflorian #ifndef SMALL 98a41cc082Sflorian struct dhcpleased_conf *main_conf; 99a41cc082Sflorian #endif 100*5b2dcb8fSdlg const char default_conffile[] = _PATH_CONF_FILE; 101*5b2dcb8fSdlg const char *conffile = default_conffile; 10257419a7fSflorian pid_t frontend_pid; 10357419a7fSflorian pid_t engine_pid; 10457419a7fSflorian 105ae968ffdSflorian int routesock, ioctl_sock, rtm_seq, no_lease_files; 10657419a7fSflorian 10757419a7fSflorian void 10857419a7fSflorian main_sig_handler(int sig, short event, void *arg) 10957419a7fSflorian { 11057419a7fSflorian /* 11157419a7fSflorian * Normal signal handler rules don't apply because libevent 11257419a7fSflorian * decouples for us. 11357419a7fSflorian */ 11457419a7fSflorian 11557419a7fSflorian switch (sig) { 11657419a7fSflorian case SIGTERM: 11757419a7fSflorian case SIGINT: 11857419a7fSflorian main_shutdown(); 119a41cc082Sflorian case SIGHUP: 120a41cc082Sflorian #ifndef SMALL 121a41cc082Sflorian if (main_reload() == -1) 122a41cc082Sflorian log_warnx("configuration reload failed"); 123a41cc082Sflorian else 124a41cc082Sflorian log_debug("configuration reloaded"); 125a41cc082Sflorian #endif /* SMALL */ 126a41cc082Sflorian break; 12757419a7fSflorian default: 12857419a7fSflorian fatalx("unexpected signal"); 12957419a7fSflorian } 13057419a7fSflorian } 13157419a7fSflorian 13257419a7fSflorian __dead void 13357419a7fSflorian usage(void) 13457419a7fSflorian { 13557419a7fSflorian extern char *__progname; 13657419a7fSflorian 137a41cc082Sflorian fprintf(stderr, "usage: %s [-dnv] [-f file] [-s socket]\n", 13857419a7fSflorian __progname); 13957419a7fSflorian exit(1); 14057419a7fSflorian } 14157419a7fSflorian 14257419a7fSflorian int 14357419a7fSflorian main(int argc, char *argv[]) 14457419a7fSflorian { 145a41cc082Sflorian struct event ev_sigint, ev_sigterm, ev_sighup; 14657419a7fSflorian int ch; 14757419a7fSflorian int debug = 0, engine_flag = 0, frontend_flag = 0; 148a41cc082Sflorian int verbose = 0, no_action = 0; 14957419a7fSflorian char *saved_argv0; 15057419a7fSflorian int pipe_main2frontend[2]; 15157419a7fSflorian int pipe_main2engine[2]; 152bc9eb55cSkn int frontend_routesock, rtfilter, lockfd; 15357419a7fSflorian int rtable_any = RTABLE_ANY; 154131c304bSflorian char *csock = _PATH_DHCPLEASED_SOCKET; 15557419a7fSflorian #ifndef SMALL 15657419a7fSflorian int control_fd; 15757419a7fSflorian #endif /* SMALL */ 15857419a7fSflorian 15957419a7fSflorian log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */ 16057419a7fSflorian log_setverbose(1); 16157419a7fSflorian 16257419a7fSflorian saved_argv0 = argv[0]; 16357419a7fSflorian if (saved_argv0 == NULL) 16457419a7fSflorian saved_argv0 = "dhcpleased"; 16557419a7fSflorian 166a41cc082Sflorian while ((ch = getopt(argc, argv, "dEFf:ns:v")) != -1) { 16757419a7fSflorian switch (ch) { 16857419a7fSflorian case 'd': 16957419a7fSflorian debug = 1; 17057419a7fSflorian break; 17157419a7fSflorian case 'E': 17257419a7fSflorian engine_flag = 1; 17357419a7fSflorian break; 17457419a7fSflorian case 'F': 17557419a7fSflorian frontend_flag = 1; 17657419a7fSflorian break; 177a41cc082Sflorian case 'f': 178a41cc082Sflorian conffile = optarg; 179a41cc082Sflorian break; 180a41cc082Sflorian case 'n': 181a41cc082Sflorian no_action = 1; 182a41cc082Sflorian break; 18357419a7fSflorian case 's': 18457419a7fSflorian csock = optarg; 18557419a7fSflorian break; 18657419a7fSflorian case 'v': 18757419a7fSflorian verbose++; 18857419a7fSflorian break; 18957419a7fSflorian default: 19057419a7fSflorian usage(); 19157419a7fSflorian } 19257419a7fSflorian } 19357419a7fSflorian 19457419a7fSflorian argc -= optind; 19557419a7fSflorian argv += optind; 19657419a7fSflorian if (argc > 0 || (engine_flag && frontend_flag)) 19757419a7fSflorian usage(); 19857419a7fSflorian 19957419a7fSflorian if (engine_flag) 20057419a7fSflorian engine(debug, verbose); 20157419a7fSflorian else if (frontend_flag) 20257419a7fSflorian frontend(debug, verbose); 20357419a7fSflorian 204a41cc082Sflorian #ifndef SMALL 205a41cc082Sflorian /* parse config file */ 206a41cc082Sflorian if ((main_conf = parse_config(conffile)) == NULL) 207a41cc082Sflorian exit(1); 208a41cc082Sflorian 209a41cc082Sflorian if (no_action) { 210a41cc082Sflorian if (verbose) 211a41cc082Sflorian print_config(main_conf); 212a41cc082Sflorian else 213a41cc082Sflorian fprintf(stderr, "configuration OK\n"); 214a41cc082Sflorian exit(0); 215a41cc082Sflorian } 216a41cc082Sflorian #endif /* SMALL */ 217a41cc082Sflorian 21857419a7fSflorian /* Check for root privileges. */ 21957419a7fSflorian if (geteuid()) 22057419a7fSflorian errx(1, "need root privileges"); 22157419a7fSflorian 222bc9eb55cSkn lockfd = open(_PATH_LOCKFILE, O_CREAT|O_RDWR|O_EXLOCK|O_NONBLOCK, 0600); 223bc9eb55cSkn if (lockfd == -1) 224bc9eb55cSkn errx(1, "already running"); 225bc9eb55cSkn 22657419a7fSflorian /* Check for assigned daemon user */ 22757419a7fSflorian if (getpwnam(DHCPLEASED_USER) == NULL) 22857419a7fSflorian errx(1, "unknown user %s", DHCPLEASED_USER); 22957419a7fSflorian 23057419a7fSflorian log_init(debug, LOG_DAEMON); 23157419a7fSflorian log_setverbose(verbose); 23257419a7fSflorian 23357419a7fSflorian if (!debug) 23457419a7fSflorian daemon(0, 0); 23557419a7fSflorian 23657419a7fSflorian if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 23757419a7fSflorian PF_UNSPEC, pipe_main2frontend) == -1) 23857419a7fSflorian fatal("main2frontend socketpair"); 23957419a7fSflorian if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 24057419a7fSflorian PF_UNSPEC, pipe_main2engine) == -1) 24157419a7fSflorian fatal("main2engine socketpair"); 24257419a7fSflorian 24357419a7fSflorian /* Start children. */ 24457419a7fSflorian engine_pid = start_child(PROC_ENGINE, saved_argv0, pipe_main2engine[1], 24557419a7fSflorian debug, verbose); 24657419a7fSflorian frontend_pid = start_child(PROC_FRONTEND, saved_argv0, 24757419a7fSflorian pipe_main2frontend[1], debug, verbose); 24857419a7fSflorian 24957419a7fSflorian log_procinit("main"); 25057419a7fSflorian 25157419a7fSflorian if ((routesock = socket(AF_ROUTE, SOCK_RAW | SOCK_CLOEXEC | 25257419a7fSflorian SOCK_NONBLOCK, AF_INET)) == -1) 25357419a7fSflorian fatal("route socket"); 25457419a7fSflorian shutdown(SHUT_RD, routesock); 25557419a7fSflorian 25657419a7fSflorian event_init(); 25757419a7fSflorian 25857419a7fSflorian /* Setup signal handler. */ 25957419a7fSflorian signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); 26057419a7fSflorian signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); 261a41cc082Sflorian signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); 26257419a7fSflorian signal_add(&ev_sigint, NULL); 26357419a7fSflorian signal_add(&ev_sigterm, NULL); 264a41cc082Sflorian signal_add(&ev_sighup, NULL); 26557419a7fSflorian signal(SIGPIPE, SIG_IGN); 26657419a7fSflorian 26757419a7fSflorian /* Setup pipes to children. */ 26857419a7fSflorian 26957419a7fSflorian if ((iev_frontend = malloc(sizeof(struct imsgev))) == NULL || 27057419a7fSflorian (iev_engine = malloc(sizeof(struct imsgev))) == NULL) 27157419a7fSflorian fatal(NULL); 27257419a7fSflorian imsg_init(&iev_frontend->ibuf, pipe_main2frontend[0]); 27357419a7fSflorian iev_frontend->handler = main_dispatch_frontend; 27457419a7fSflorian imsg_init(&iev_engine->ibuf, pipe_main2engine[0]); 27557419a7fSflorian iev_engine->handler = main_dispatch_engine; 27657419a7fSflorian 27757419a7fSflorian /* Setup event handlers for pipes to engine & frontend. */ 27857419a7fSflorian iev_frontend->events = EV_READ; 27957419a7fSflorian event_set(&iev_frontend->ev, iev_frontend->ibuf.fd, 28057419a7fSflorian iev_frontend->events, iev_frontend->handler, iev_frontend); 28157419a7fSflorian event_add(&iev_frontend->ev, NULL); 28257419a7fSflorian 28357419a7fSflorian iev_engine->events = EV_READ; 28457419a7fSflorian event_set(&iev_engine->ev, iev_engine->ibuf.fd, iev_engine->events, 28557419a7fSflorian iev_engine->handler, iev_engine); 28657419a7fSflorian event_add(&iev_engine->ev, NULL); 28757419a7fSflorian 28857419a7fSflorian if (main_imsg_send_ipc_sockets(&iev_frontend->ibuf, &iev_engine->ibuf)) 28957419a7fSflorian fatal("could not establish imsg links"); 29057419a7fSflorian 29157419a7fSflorian if ((ioctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) 29257419a7fSflorian fatal("socket"); 29357419a7fSflorian 29457419a7fSflorian if ((frontend_routesock = socket(AF_ROUTE, SOCK_RAW | SOCK_CLOEXEC, 29557419a7fSflorian AF_INET)) == -1) 29657419a7fSflorian fatal("route socket"); 29757419a7fSflorian 298d3097762Sflorian rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_PROPOSAL) | 299d3097762Sflorian ROUTE_FILTER(RTM_IFANNOUNCE); 30057419a7fSflorian if (setsockopt(frontend_routesock, AF_ROUTE, ROUTE_MSGFILTER, 30157419a7fSflorian &rtfilter, sizeof(rtfilter)) == -1) 30257419a7fSflorian fatal("setsockopt(ROUTE_MSGFILTER)"); 30357419a7fSflorian if (setsockopt(frontend_routesock, AF_ROUTE, ROUTE_TABLEFILTER, 30457419a7fSflorian &rtable_any, sizeof(rtable_any)) == -1) 30557419a7fSflorian fatal("setsockopt(ROUTE_TABLEFILTER)"); 30657419a7fSflorian 30757419a7fSflorian #ifndef SMALL 30857419a7fSflorian if ((control_fd = control_init(csock)) == -1) 3094b9fcc1bSflorian warnx("control socket setup failed"); 31057419a7fSflorian #endif /* SMALL */ 31157419a7fSflorian 312a41cc082Sflorian if (unveil(conffile, "r") == -1) 313a41cc082Sflorian fatal("unveil %s", conffile); 31457419a7fSflorian if (unveil("/dev/bpf", "rw") == -1) 315f5208191Sflorian fatal("unveil /dev/bpf"); 31657419a7fSflorian 317ae968ffdSflorian if (unveil(_PATH_LEASE, "rwc") == -1) { 318ae968ffdSflorian no_lease_files = 1; 319ae968ffdSflorian log_warn("disabling lease files, unveil " _PATH_LEASE); 320ae968ffdSflorian } 32157419a7fSflorian 32257419a7fSflorian if (unveil(NULL, NULL) == -1) 323bc5a8259Sbeck fatal("unveil"); 32457419a7fSflorian #if notyet 32557419a7fSflorian if (pledge("stdio inet rpath wpath sendfd wroute bpf", NULL) == -1) 32657419a7fSflorian fatal("pledge"); 32757419a7fSflorian #endif 32857419a7fSflorian main_imsg_compose_frontend(IMSG_ROUTESOCK, frontend_routesock, NULL, 0); 32957419a7fSflorian 33057419a7fSflorian #ifndef SMALL 3314b9fcc1bSflorian if (control_fd != -1) 33257419a7fSflorian main_imsg_compose_frontend(IMSG_CONTROLFD, control_fd, NULL, 0); 333a41cc082Sflorian main_imsg_send_config(main_conf); 33457419a7fSflorian #endif /* SMALL */ 33557419a7fSflorian 33657419a7fSflorian main_imsg_compose_frontend(IMSG_STARTUP, -1, NULL, 0); 33757419a7fSflorian 33857419a7fSflorian event_dispatch(); 33957419a7fSflorian 34057419a7fSflorian main_shutdown(); 34157419a7fSflorian return (0); 34257419a7fSflorian } 34357419a7fSflorian 34457419a7fSflorian __dead void 34557419a7fSflorian main_shutdown(void) 34657419a7fSflorian { 34757419a7fSflorian pid_t pid; 34857419a7fSflorian int status; 34957419a7fSflorian 35057419a7fSflorian /* Close pipes. */ 35157419a7fSflorian msgbuf_clear(&iev_frontend->ibuf.w); 35257419a7fSflorian close(iev_frontend->ibuf.fd); 35357419a7fSflorian msgbuf_clear(&iev_engine->ibuf.w); 35457419a7fSflorian close(iev_engine->ibuf.fd); 35557419a7fSflorian 356a41cc082Sflorian #ifndef SMALL 357a41cc082Sflorian config_clear(main_conf); 358a41cc082Sflorian #endif /* SMALL */ 359a41cc082Sflorian 36057419a7fSflorian log_debug("waiting for children to terminate"); 36157419a7fSflorian do { 36257419a7fSflorian pid = wait(&status); 36357419a7fSflorian if (pid == -1) { 36457419a7fSflorian if (errno != EINTR && errno != ECHILD) 36557419a7fSflorian fatal("wait"); 36657419a7fSflorian } else if (WIFSIGNALED(status)) 36757419a7fSflorian log_warnx("%s terminated; signal %d", 36857419a7fSflorian (pid == engine_pid) ? "engine" : 36957419a7fSflorian "frontend", WTERMSIG(status)); 37057419a7fSflorian } while (pid != -1 || (pid == -1 && errno == EINTR)); 37157419a7fSflorian 37257419a7fSflorian free(iev_frontend); 37357419a7fSflorian free(iev_engine); 37457419a7fSflorian 37557419a7fSflorian log_info("terminating"); 37657419a7fSflorian exit(0); 37757419a7fSflorian } 37857419a7fSflorian 37957419a7fSflorian static pid_t 38057419a7fSflorian start_child(enum dhcpleased_process p, char *argv0, int fd, int debug, int 38157419a7fSflorian verbose) 38257419a7fSflorian { 38357419a7fSflorian char *argv[7]; 38457419a7fSflorian int argc = 0; 38557419a7fSflorian pid_t pid; 38657419a7fSflorian 38757419a7fSflorian switch (pid = fork()) { 38857419a7fSflorian case -1: 38957419a7fSflorian fatal("cannot fork"); 39057419a7fSflorian case 0: 39157419a7fSflorian break; 39257419a7fSflorian default: 39357419a7fSflorian close(fd); 39457419a7fSflorian return (pid); 39557419a7fSflorian } 39657419a7fSflorian 39757419a7fSflorian if (fd != 3) { 39857419a7fSflorian if (dup2(fd, 3) == -1) 39957419a7fSflorian fatal("cannot setup imsg fd"); 40057419a7fSflorian } else if (fcntl(fd, F_SETFD, 0) == -1) 40157419a7fSflorian fatal("cannot setup imsg fd"); 40257419a7fSflorian 40357419a7fSflorian argv[argc++] = argv0; 40457419a7fSflorian switch (p) { 40557419a7fSflorian case PROC_MAIN: 40657419a7fSflorian fatalx("Can not start main process"); 40757419a7fSflorian case PROC_ENGINE: 40857419a7fSflorian argv[argc++] = "-E"; 40957419a7fSflorian break; 41057419a7fSflorian case PROC_FRONTEND: 41157419a7fSflorian argv[argc++] = "-F"; 41257419a7fSflorian break; 41357419a7fSflorian } 41457419a7fSflorian if (debug) 41557419a7fSflorian argv[argc++] = "-d"; 41657419a7fSflorian if (verbose) 41757419a7fSflorian argv[argc++] = "-v"; 41857419a7fSflorian if (verbose > 1) 41957419a7fSflorian argv[argc++] = "-v"; 42057419a7fSflorian argv[argc++] = NULL; 42157419a7fSflorian 42257419a7fSflorian execvp(argv0, argv); 42357419a7fSflorian fatal("execvp"); 42457419a7fSflorian } 42557419a7fSflorian 42657419a7fSflorian void 42757419a7fSflorian main_dispatch_frontend(int fd, short event, void *bula) 42857419a7fSflorian { 42957419a7fSflorian struct imsgev *iev = bula; 43057419a7fSflorian struct imsgbuf *ibuf; 43157419a7fSflorian struct imsg imsg; 43257419a7fSflorian struct imsg_ifinfo imsg_ifinfo; 43357419a7fSflorian ssize_t n; 43457419a7fSflorian int shut = 0; 43557419a7fSflorian uint32_t if_index; 43657419a7fSflorian #ifndef SMALL 43757419a7fSflorian int verbose; 43857419a7fSflorian #endif /* SMALL */ 43957419a7fSflorian 44057419a7fSflorian ibuf = &iev->ibuf; 44157419a7fSflorian 44257419a7fSflorian if (event & EV_READ) { 44357419a7fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 44457419a7fSflorian fatal("imsg_read error"); 44557419a7fSflorian if (n == 0) /* Connection closed. */ 44657419a7fSflorian shut = 1; 44757419a7fSflorian } 44857419a7fSflorian if (event & EV_WRITE) { 44957419a7fSflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 45057419a7fSflorian fatal("msgbuf_write"); 45157419a7fSflorian if (n == 0) /* Connection closed. */ 45257419a7fSflorian shut = 1; 45357419a7fSflorian } 45457419a7fSflorian 45557419a7fSflorian for (;;) { 45657419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 45757419a7fSflorian fatal("imsg_get"); 45857419a7fSflorian if (n == 0) /* No more messages. */ 45957419a7fSflorian break; 46057419a7fSflorian 46157419a7fSflorian switch (imsg.hdr.type) { 46257419a7fSflorian case IMSG_OPEN_BPFSOCK: 46357419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 46457419a7fSflorian fatalx("%s: IMSG_OPEN_BPFSOCK wrong length: " 46557419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 46657419a7fSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 46757419a7fSflorian open_bpfsock(if_index); 46857419a7fSflorian break; 46957419a7fSflorian #ifndef SMALL 470a41cc082Sflorian case IMSG_CTL_RELOAD: 471a41cc082Sflorian if (main_reload() == -1) 472a41cc082Sflorian log_warnx("configuration reload failed"); 473a41cc082Sflorian else 474a41cc082Sflorian log_warnx("configuration reloaded"); 475a41cc082Sflorian break; 47657419a7fSflorian case IMSG_CTL_LOG_VERBOSE: 47757419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(verbose)) 47857419a7fSflorian fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: " 47957419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 48057419a7fSflorian memcpy(&verbose, imsg.data, sizeof(verbose)); 48157419a7fSflorian log_setverbose(verbose); 48257419a7fSflorian break; 48357419a7fSflorian #endif /* SMALL */ 48457419a7fSflorian case IMSG_UPDATE_IF: 48557419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_ifinfo)) 48657419a7fSflorian fatalx("%s: IMSG_UPDATE_IF wrong length: %lu", 48757419a7fSflorian __func__, IMSG_DATA_SIZE(imsg)); 48857419a7fSflorian memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo)); 4896e93e3e9Sflorian read_lease_file(&imsg_ifinfo); 4906e93e3e9Sflorian main_imsg_compose_engine(IMSG_UPDATE_IF, -1, 4916e93e3e9Sflorian &imsg_ifinfo, sizeof(imsg_ifinfo)); 49257419a7fSflorian break; 49357419a7fSflorian default: 49457419a7fSflorian log_debug("%s: error handling imsg %d", __func__, 49557419a7fSflorian imsg.hdr.type); 49657419a7fSflorian break; 49757419a7fSflorian } 49857419a7fSflorian imsg_free(&imsg); 49957419a7fSflorian } 50057419a7fSflorian if (!shut) 50157419a7fSflorian imsg_event_add(iev); 50257419a7fSflorian else { 50357419a7fSflorian /* This pipe is dead. Remove its event handler */ 50457419a7fSflorian event_del(&iev->ev); 50557419a7fSflorian event_loopexit(NULL); 50657419a7fSflorian } 50757419a7fSflorian } 50857419a7fSflorian 50957419a7fSflorian void 51057419a7fSflorian main_dispatch_engine(int fd, short event, void *bula) 51157419a7fSflorian { 51257419a7fSflorian struct imsgev *iev = bula; 51357419a7fSflorian struct imsgbuf *ibuf; 51457419a7fSflorian struct imsg imsg; 51557419a7fSflorian ssize_t n; 51657419a7fSflorian int shut = 0; 51757419a7fSflorian 51857419a7fSflorian ibuf = &iev->ibuf; 51957419a7fSflorian 52057419a7fSflorian if (event & EV_READ) { 52157419a7fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 52257419a7fSflorian fatal("imsg_read error"); 52357419a7fSflorian if (n == 0) /* Connection closed. */ 52457419a7fSflorian shut = 1; 52557419a7fSflorian } 52657419a7fSflorian if (event & EV_WRITE) { 52757419a7fSflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 52857419a7fSflorian fatal("msgbuf_write"); 52957419a7fSflorian if (n == 0) /* Connection closed. */ 53057419a7fSflorian shut = 1; 53157419a7fSflorian } 53257419a7fSflorian 53357419a7fSflorian for (;;) { 53457419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 53557419a7fSflorian fatal("imsg_get"); 53657419a7fSflorian if (n == 0) /* No more messages. */ 53757419a7fSflorian break; 53857419a7fSflorian 53957419a7fSflorian switch (imsg.hdr.type) { 54057419a7fSflorian case IMSG_CONFIGURE_INTERFACE: { 54157419a7fSflorian struct imsg_configure_interface imsg_interface; 54257419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_interface)) 54357419a7fSflorian fatalx("%s: IMSG_CONFIGURE_INTERFACE wrong " 54457419a7fSflorian "length: %lu", __func__, 54557419a7fSflorian IMSG_DATA_SIZE(imsg)); 54657419a7fSflorian memcpy(&imsg_interface, imsg.data, 54757419a7fSflorian sizeof(imsg_interface)); 548351dd593Sflorian if (imsg_interface.routes_len >= MAX_DHCP_ROUTES) 549351dd593Sflorian fatalx("%s: too many routes in imsg", __func__); 55057419a7fSflorian configure_interface(&imsg_interface); 55157419a7fSflorian break; 55257419a7fSflorian } 55357419a7fSflorian case IMSG_DECONFIGURE_INTERFACE: { 55457419a7fSflorian struct imsg_configure_interface imsg_interface; 55557419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_interface)) 55657419a7fSflorian fatalx("%s: IMSG_CONFIGURE_INTERFACE wrong " 55757419a7fSflorian "length: %lu", __func__, 55857419a7fSflorian IMSG_DATA_SIZE(imsg)); 55957419a7fSflorian memcpy(&imsg_interface, imsg.data, 56057419a7fSflorian sizeof(imsg_interface)); 561351dd593Sflorian if (imsg_interface.routes_len >= MAX_DHCP_ROUTES) 562351dd593Sflorian fatalx("%s: too many routes in imsg", __func__); 56357419a7fSflorian deconfigure_interface(&imsg_interface); 56457419a7fSflorian main_imsg_compose_frontend(IMSG_CLOSE_UDPSOCK, -1, 56557419a7fSflorian &imsg_interface.if_index, 56657419a7fSflorian sizeof(imsg_interface.if_index)); 56757419a7fSflorian break; 56857419a7fSflorian } 569c2bc6c6dSflorian case IMSG_WITHDRAW_ROUTES: { 570c2bc6c6dSflorian struct imsg_configure_interface imsg_interface; 571c2bc6c6dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_interface)) 572c2bc6c6dSflorian fatalx("%s: IMSG_CONFIGURE_INTERFACE wrong " 573c2bc6c6dSflorian "length: %lu", __func__, 574c2bc6c6dSflorian IMSG_DATA_SIZE(imsg)); 575c2bc6c6dSflorian memcpy(&imsg_interface, imsg.data, 576c2bc6c6dSflorian sizeof(imsg_interface)); 577c2bc6c6dSflorian if (imsg_interface.routes_len >= MAX_DHCP_ROUTES) 578c2bc6c6dSflorian fatalx("%s: too many routes in imsg", __func__); 579c2bc6c6dSflorian if (imsg_interface.routes_len > 0) 580c2bc6c6dSflorian configure_routes(RTM_DELETE, &imsg_interface); 581c2bc6c6dSflorian break; 582c2bc6c6dSflorian } 58357419a7fSflorian case IMSG_PROPOSE_RDNS: { 58457419a7fSflorian struct imsg_propose_rdns rdns; 58557419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(rdns)) 58657419a7fSflorian fatalx("%s: IMSG_PROPOSE_RDNS wrong " 58757419a7fSflorian "length: %lu", __func__, 58857419a7fSflorian IMSG_DATA_SIZE(imsg)); 58957419a7fSflorian memcpy(&rdns, imsg.data, sizeof(rdns)); 59057419a7fSflorian if ((2 + rdns.rdns_count * sizeof(struct in_addr)) > 59157419a7fSflorian sizeof(struct sockaddr_rtdns)) 59257419a7fSflorian fatalx("%s: rdns_count too big: %d", __func__, 59357419a7fSflorian rdns.rdns_count); 59457419a7fSflorian propose_rdns(&rdns); 59557419a7fSflorian break; 59657419a7fSflorian } 59757419a7fSflorian case IMSG_WITHDRAW_RDNS: { 59857419a7fSflorian struct imsg_propose_rdns rdns; 59957419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(rdns)) 60057419a7fSflorian fatalx("%s: IMSG_PROPOSE_RDNS wrong " 60157419a7fSflorian "length: %lu", __func__, 60257419a7fSflorian IMSG_DATA_SIZE(imsg)); 60357419a7fSflorian memcpy(&rdns, imsg.data, sizeof(rdns)); 60457419a7fSflorian if (rdns.rdns_count != 0) 60557419a7fSflorian fatalx("%s: expected rdns_count == 0: %d", 60657419a7fSflorian __func__, rdns.rdns_count); 60757419a7fSflorian propose_rdns(&rdns); 60857419a7fSflorian break; 60957419a7fSflorian } 61057419a7fSflorian default: 61157419a7fSflorian log_debug("%s: error handling imsg %d", __func__, 61257419a7fSflorian imsg.hdr.type); 61357419a7fSflorian break; 61457419a7fSflorian } 61557419a7fSflorian imsg_free(&imsg); 61657419a7fSflorian } 61757419a7fSflorian if (!shut) 61857419a7fSflorian imsg_event_add(iev); 61957419a7fSflorian else { 62057419a7fSflorian /* This pipe is dead. Remove its event handler. */ 62157419a7fSflorian event_del(&iev->ev); 62257419a7fSflorian event_loopexit(NULL); 62357419a7fSflorian } 62457419a7fSflorian } 62557419a7fSflorian 62657419a7fSflorian int 62757419a7fSflorian main_imsg_compose_frontend(int type, int fd, void *data, uint16_t datalen) 62857419a7fSflorian { 62957419a7fSflorian if (iev_frontend) 63057419a7fSflorian return (imsg_compose_event(iev_frontend, type, 0, 0, fd, data, 63157419a7fSflorian datalen)); 63257419a7fSflorian else 63357419a7fSflorian return (-1); 63457419a7fSflorian } 63557419a7fSflorian 63657419a7fSflorian int 63757419a7fSflorian main_imsg_compose_engine(int type, int fd, void *data, uint16_t datalen) 63857419a7fSflorian { 63957419a7fSflorian if (iev_engine) 64057419a7fSflorian return(imsg_compose_event(iev_engine, type, 0, 0, fd, data, 64157419a7fSflorian datalen)); 64257419a7fSflorian else 64357419a7fSflorian return (-1); 64457419a7fSflorian } 64557419a7fSflorian 64657419a7fSflorian void 64757419a7fSflorian imsg_event_add(struct imsgev *iev) 64857419a7fSflorian { 64957419a7fSflorian iev->events = EV_READ; 65057419a7fSflorian if (iev->ibuf.w.queued) 65157419a7fSflorian iev->events |= EV_WRITE; 65257419a7fSflorian 65357419a7fSflorian event_del(&iev->ev); 65457419a7fSflorian event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); 65557419a7fSflorian event_add(&iev->ev, NULL); 65657419a7fSflorian } 65757419a7fSflorian 65857419a7fSflorian int 65957419a7fSflorian imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 66057419a7fSflorian pid_t pid, int fd, void *data, uint16_t datalen) 66157419a7fSflorian { 66257419a7fSflorian int ret; 66357419a7fSflorian 66457419a7fSflorian if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, 66557419a7fSflorian datalen)) != -1) 66657419a7fSflorian imsg_event_add(iev); 66757419a7fSflorian 66857419a7fSflorian return (ret); 66957419a7fSflorian } 67057419a7fSflorian 67157419a7fSflorian static int 67257419a7fSflorian main_imsg_send_ipc_sockets(struct imsgbuf *frontend_buf, 67357419a7fSflorian struct imsgbuf *engine_buf) 67457419a7fSflorian { 67557419a7fSflorian int pipe_frontend2engine[2]; 67657419a7fSflorian 67757419a7fSflorian if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 67857419a7fSflorian PF_UNSPEC, pipe_frontend2engine) == -1) 67957419a7fSflorian return (-1); 68057419a7fSflorian 68157419a7fSflorian if (imsg_compose(frontend_buf, IMSG_SOCKET_IPC, 0, 0, 68257419a7fSflorian pipe_frontend2engine[0], NULL, 0) == -1) 68357419a7fSflorian return (-1); 68457419a7fSflorian imsg_flush(frontend_buf); 68557419a7fSflorian if (imsg_compose(engine_buf, IMSG_SOCKET_IPC, 0, 0, 68657419a7fSflorian pipe_frontend2engine[1], NULL, 0) == -1) 68757419a7fSflorian return (-1); 68857419a7fSflorian imsg_flush(engine_buf); 68957419a7fSflorian return (0); 69057419a7fSflorian } 69157419a7fSflorian 692a41cc082Sflorian #ifndef SMALL 693a41cc082Sflorian int 694a41cc082Sflorian main_reload(void) 695a41cc082Sflorian { 696a41cc082Sflorian struct dhcpleased_conf *xconf; 697a41cc082Sflorian 698a41cc082Sflorian if ((xconf = parse_config(conffile)) == NULL) 699a41cc082Sflorian return (-1); 700a41cc082Sflorian 701a41cc082Sflorian if (main_imsg_send_config(xconf) == -1) 702a41cc082Sflorian return (-1); 703a41cc082Sflorian 704a41cc082Sflorian merge_config(main_conf, xconf); 705a41cc082Sflorian 706a41cc082Sflorian return (0); 707a41cc082Sflorian } 708a41cc082Sflorian 709a41cc082Sflorian int 710a41cc082Sflorian main_imsg_send_config(struct dhcpleased_conf *xconf) 711a41cc082Sflorian { 712a41cc082Sflorian struct iface_conf *iface_conf; 713a41cc082Sflorian 714a41cc082Sflorian main_imsg_compose_frontend(IMSG_RECONF_CONF, -1, NULL, 0); 715a41cc082Sflorian main_imsg_compose_engine(IMSG_RECONF_CONF, -1, NULL, 0); 716a41cc082Sflorian 717a41cc082Sflorian /* Send the interface list to the frontend & engine. */ 718a41cc082Sflorian SIMPLEQ_FOREACH(iface_conf, &xconf->iface_list, entry) { 719a41cc082Sflorian main_imsg_compose_frontend(IMSG_RECONF_IFACE, -1, iface_conf, 720a41cc082Sflorian sizeof(*iface_conf)); 721a41cc082Sflorian main_imsg_compose_engine(IMSG_RECONF_IFACE, -1, iface_conf, 722a41cc082Sflorian sizeof(*iface_conf)); 723a41cc082Sflorian main_imsg_compose_frontend(IMSG_RECONF_VC_ID, -1, 724a41cc082Sflorian iface_conf->vc_id, iface_conf->vc_id_len); 725a41cc082Sflorian main_imsg_compose_engine(IMSG_RECONF_VC_ID, -1, 726a41cc082Sflorian iface_conf->vc_id, iface_conf->vc_id_len); 727a41cc082Sflorian main_imsg_compose_frontend(IMSG_RECONF_C_ID, -1, 728a41cc082Sflorian iface_conf->c_id, iface_conf->c_id_len); 729a41cc082Sflorian main_imsg_compose_engine(IMSG_RECONF_C_ID, -1, 730a41cc082Sflorian iface_conf->c_id, iface_conf->c_id_len); 731b3441518Sflorian if (iface_conf->h_name != NULL) 732b3441518Sflorian main_imsg_compose_frontend(IMSG_RECONF_H_NAME, -1, 733b3441518Sflorian iface_conf->h_name, strlen(iface_conf->h_name) + 1); 734a41cc082Sflorian } 735a41cc082Sflorian 736a41cc082Sflorian /* Config is now complete. */ 737a41cc082Sflorian main_imsg_compose_frontend(IMSG_RECONF_END, -1, NULL, 0); 738a41cc082Sflorian main_imsg_compose_engine(IMSG_RECONF_END, -1, NULL, 0); 739a41cc082Sflorian 740a41cc082Sflorian return (0); 741a41cc082Sflorian } 742a41cc082Sflorian #endif /* SMALL */ 743a41cc082Sflorian 74457419a7fSflorian void 74557419a7fSflorian configure_interface(struct imsg_configure_interface *imsg) 74657419a7fSflorian { 74757419a7fSflorian struct ifaliasreq ifaliasreq; 74857419a7fSflorian struct ifaddrs *ifap, *ifa; 74957419a7fSflorian struct sockaddr_in *req_sin_addr, *req_sin_mask; 75057419a7fSflorian int found = 0, udpsock, opt = 1, len, fd = -1; 75157419a7fSflorian char *if_name; 752ebace80cSflorian char ip_ntop_buf[INET_ADDRSTRLEN]; 753ebace80cSflorian char nextserver_ntop_buf[INET_ADDRSTRLEN]; 75457419a7fSflorian char lease_buf[LEASE_SIZE]; 755131c304bSflorian char lease_file_buf[sizeof(_PATH_LEASE) + 75657419a7fSflorian IF_NAMESIZE]; 757131c304bSflorian char tmpl[] = _PATH_LEASE"XXXXXXXXXX"; 75857419a7fSflorian 75957419a7fSflorian memset(&ifaliasreq, 0, sizeof(ifaliasreq)); 76057419a7fSflorian 76157419a7fSflorian if_name = if_indextoname(imsg->if_index, ifaliasreq.ifra_name); 76257419a7fSflorian if (if_name == NULL) { 76357419a7fSflorian log_warnx("%s: cannot find interface %d", __func__, 76457419a7fSflorian imsg->if_index); 76557419a7fSflorian return; 76657419a7fSflorian } 76757419a7fSflorian 768e998cdbeSflorian log_debug("%s %s", __func__, if_name); 769e998cdbeSflorian 77057419a7fSflorian if (getifaddrs(&ifap) != 0) 77157419a7fSflorian fatal("getifaddrs"); 77257419a7fSflorian 77357419a7fSflorian req_sin_addr = (struct sockaddr_in *)&ifaliasreq.ifra_addr; 77457419a7fSflorian req_sin_addr->sin_family = AF_INET; 77557419a7fSflorian req_sin_addr->sin_len = sizeof(*req_sin_addr); 77657419a7fSflorian 77757419a7fSflorian for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 77857419a7fSflorian struct in_addr addr, mask; 77957419a7fSflorian 78057419a7fSflorian if (strcmp(if_name, ifa->ifa_name) != 0) 78157419a7fSflorian continue; 78257419a7fSflorian if (ifa->ifa_addr == NULL) 78357419a7fSflorian continue; 78457419a7fSflorian if (ifa->ifa_addr->sa_family != AF_INET) 78557419a7fSflorian continue; 78657419a7fSflorian 787b2b40540Sflorian addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; 788b2b40540Sflorian mask = ((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr; 78957419a7fSflorian 79057419a7fSflorian if (imsg->addr.s_addr == addr.s_addr) { 79157419a7fSflorian if (imsg->mask.s_addr == mask.s_addr) 79257419a7fSflorian found = 1; 79357419a7fSflorian else { 794b2b40540Sflorian req_sin_addr->sin_addr = addr; 79557419a7fSflorian if (ioctl(ioctl_sock, SIOCDIFADDR, &ifaliasreq) 79657419a7fSflorian == -1) { 79757419a7fSflorian if (errno != EADDRNOTAVAIL) 79857419a7fSflorian log_warn("SIOCDIFADDR"); 79957419a7fSflorian } 80057419a7fSflorian } 80157419a7fSflorian break; 80257419a7fSflorian } 80357419a7fSflorian } 80457419a7fSflorian 805b2b40540Sflorian req_sin_addr->sin_addr = imsg->addr; 80657419a7fSflorian if (!found) { 80757419a7fSflorian req_sin_mask = (struct sockaddr_in *)&ifaliasreq.ifra_mask; 80857419a7fSflorian req_sin_mask->sin_family = AF_INET; 80957419a7fSflorian req_sin_mask->sin_len = sizeof(*req_sin_mask); 810b2b40540Sflorian req_sin_mask->sin_addr = imsg->mask; 81157419a7fSflorian if (ioctl(ioctl_sock, SIOCAIFADDR, &ifaliasreq) == -1) 812db7d8776Sflorian log_warn("SIOCAIFADDR"); 813665300f1Sstsp } 814351dd593Sflorian if (imsg->routes_len > 0) 815351dd593Sflorian configure_routes(RTM_ADD, imsg); 816665300f1Sstsp 81757419a7fSflorian req_sin_addr->sin_port = ntohs(CLIENT_PORT); 81857419a7fSflorian if ((udpsock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 81957419a7fSflorian log_warn("socket"); 82057419a7fSflorian return; 82157419a7fSflorian } 82257419a7fSflorian if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR, &opt, 82357419a7fSflorian sizeof(opt)) == -1) 82457419a7fSflorian log_warn("setting SO_REUSEADDR on socket"); 82557419a7fSflorian 82657419a7fSflorian if (setsockopt(udpsock, SOL_SOCKET, SO_RTABLE, &imsg->rdomain, 82757419a7fSflorian sizeof(imsg->rdomain)) == -1) { 82857419a7fSflorian /* we might race against removal of the rdomain */ 82957419a7fSflorian log_warn("setsockopt SO_RTABLE"); 83057419a7fSflorian close(udpsock); 83157419a7fSflorian return; 83257419a7fSflorian } 83357419a7fSflorian 83457419a7fSflorian if (bind(udpsock, (struct sockaddr *)req_sin_addr, 83557419a7fSflorian sizeof(*req_sin_addr)) == -1) { 83657419a7fSflorian close(udpsock); 83757419a7fSflorian return; 83857419a7fSflorian } 83957419a7fSflorian 84057419a7fSflorian shutdown(udpsock, SHUT_RD); 841e998cdbeSflorian 84257419a7fSflorian main_imsg_compose_frontend(IMSG_UDPSOCK, udpsock, 84357419a7fSflorian &imsg->if_index, sizeof(imsg->if_index)); 84457419a7fSflorian 845ae968ffdSflorian if (no_lease_files) 846ae968ffdSflorian return; 847ae968ffdSflorian 848ebace80cSflorian if (inet_ntop(AF_INET, &imsg->addr, ip_ntop_buf, sizeof(ip_ntop_buf)) == 84957419a7fSflorian NULL) { 85057419a7fSflorian log_warn("%s: inet_ntop", __func__); 85157419a7fSflorian return; 85257419a7fSflorian } 85357419a7fSflorian 854ebace80cSflorian if (imsg->siaddr.s_addr == INADDR_ANY) 855ebace80cSflorian nextserver_ntop_buf[0] = '\0'; 856ebace80cSflorian else { 857ebace80cSflorian if (inet_ntop(AF_INET, &imsg->siaddr, nextserver_ntop_buf, 858ebace80cSflorian sizeof(nextserver_ntop_buf)) == NULL) { 859ebace80cSflorian log_warn("%s: inet_ntop", __func__); 860ebace80cSflorian return; 861ebace80cSflorian } 862ebace80cSflorian } 86357419a7fSflorian len = snprintf(lease_file_buf, sizeof(lease_file_buf), "%s%s", 864131c304bSflorian _PATH_LEASE, if_name); 86557419a7fSflorian if ( len == -1 || (size_t) len >= sizeof(lease_file_buf)) { 86657419a7fSflorian log_warnx("%s: failed to encode lease path for %s", __func__, 86757419a7fSflorian if_name); 86857419a7fSflorian return; 86957419a7fSflorian } 87057419a7fSflorian 871ebace80cSflorian len = snprintf(lease_buf, sizeof(lease_buf), 872ebace80cSflorian "%s\n%s%s\n%s%s\n%s%s\n%s%s\n%s%s\n", 873ebace80cSflorian LEASE_VERSION, LEASE_IP_PREFIX, ip_ntop_buf, 874ebace80cSflorian LEASE_NEXTSERVER_PREFIX, nextserver_ntop_buf, LEASE_BOOTFILE_PREFIX, 875ebace80cSflorian imsg->file, LEASE_HOSTNAME_PREFIX, imsg->hostname, 876ebace80cSflorian LEASE_DOMAIN_PREFIX, imsg->domainname); 87757419a7fSflorian if ( len == -1 || (size_t) len >= sizeof(lease_buf)) { 87857419a7fSflorian log_warnx("%s: failed to encode lease for %s", __func__, 879ebace80cSflorian ip_ntop_buf); 88057419a7fSflorian return; 88157419a7fSflorian } 88257419a7fSflorian 88357419a7fSflorian if ((fd = mkstemp(tmpl)) == -1) { 88457419a7fSflorian log_warn("%s: mkstemp", __func__); 88557419a7fSflorian return; 88657419a7fSflorian } 88757419a7fSflorian 88857419a7fSflorian if (write(fd, lease_buf, len) < len) 88957419a7fSflorian goto err; 89057419a7fSflorian 89157419a7fSflorian if (fchmod(fd, 0644) == -1) 89257419a7fSflorian goto err; 89357419a7fSflorian 89457419a7fSflorian if (close(fd) == -1) 89557419a7fSflorian goto err; 89657419a7fSflorian fd = -1; 89757419a7fSflorian 89857419a7fSflorian if (rename(tmpl, lease_file_buf) == -1) 89957419a7fSflorian goto err; 90057419a7fSflorian return; 90157419a7fSflorian err: 90257419a7fSflorian log_warn("%s", __func__); 90357419a7fSflorian if (fd != -1) 90457419a7fSflorian close(fd); 90557419a7fSflorian unlink(tmpl); 90657419a7fSflorian } 90757419a7fSflorian 90857419a7fSflorian void 90957419a7fSflorian deconfigure_interface(struct imsg_configure_interface *imsg) 91057419a7fSflorian { 91157419a7fSflorian struct ifaliasreq ifaliasreq; 91257419a7fSflorian struct sockaddr_in *req_sin_addr; 91357419a7fSflorian 91457419a7fSflorian memset(&ifaliasreq, 0, sizeof(ifaliasreq)); 91557419a7fSflorian 916351dd593Sflorian if (imsg->routes_len > 0) 917351dd593Sflorian configure_routes(RTM_DELETE, imsg); 9187bd66a84Sflorian 91957419a7fSflorian if (if_indextoname(imsg->if_index, ifaliasreq.ifra_name) == NULL) { 92057419a7fSflorian log_warnx("%s: cannot find interface %d", __func__, 92157419a7fSflorian imsg->if_index); 92257419a7fSflorian return; 92357419a7fSflorian } 92457419a7fSflorian 925e998cdbeSflorian log_debug("%s %s", __func__, ifaliasreq.ifra_name); 926e998cdbeSflorian 92757419a7fSflorian req_sin_addr = (struct sockaddr_in *)&ifaliasreq.ifra_addr; 92857419a7fSflorian req_sin_addr->sin_family = AF_INET; 92957419a7fSflorian req_sin_addr->sin_len = sizeof(*req_sin_addr); 93057419a7fSflorian 931b2b40540Sflorian req_sin_addr->sin_addr = imsg->addr; 93257419a7fSflorian if (ioctl(ioctl_sock, SIOCDIFADDR, &ifaliasreq) == -1) { 93357419a7fSflorian if (errno != EADDRNOTAVAIL) 93457419a7fSflorian log_warn("SIOCDIFADDR"); 93557419a7fSflorian } 93657419a7fSflorian } 93757419a7fSflorian 938351dd593Sflorian void 939351dd593Sflorian configure_routes(uint8_t rtm_type, struct imsg_configure_interface *imsg) 940351dd593Sflorian { 941351dd593Sflorian struct sockaddr_in dst, mask, gw, ifa; 942351dd593Sflorian in_addr_t addrnet, gwnet; 943351dd593Sflorian int i; 944351dd593Sflorian 945351dd593Sflorian memset(&ifa, 0, sizeof(ifa)); 946351dd593Sflorian ifa.sin_family = AF_INET; 947351dd593Sflorian ifa.sin_len = sizeof(ifa); 948b2b40540Sflorian ifa.sin_addr = imsg->addr; 949351dd593Sflorian 950351dd593Sflorian memset(&dst, 0, sizeof(dst)); 951351dd593Sflorian dst.sin_family = AF_INET; 952351dd593Sflorian dst.sin_len = sizeof(dst); 953351dd593Sflorian 954351dd593Sflorian memset(&mask, 0, sizeof(mask)); 955351dd593Sflorian mask.sin_family = AF_INET; 956351dd593Sflorian mask.sin_len = sizeof(mask); 957351dd593Sflorian 958351dd593Sflorian memset(&gw, 0, sizeof(gw)); 959351dd593Sflorian gw.sin_family = AF_INET; 960351dd593Sflorian gw.sin_len = sizeof(gw); 961351dd593Sflorian 962351dd593Sflorian addrnet = imsg->addr.s_addr & imsg->mask.s_addr; 963351dd593Sflorian 964351dd593Sflorian for (i = 0; i < imsg->routes_len; i++) { 965b2b40540Sflorian dst.sin_addr = imsg->routes[i].dst; 966b2b40540Sflorian mask.sin_addr = imsg->routes[i].mask; 967b2b40540Sflorian gw.sin_addr = imsg->routes[i].gw; 968351dd593Sflorian 969351dd593Sflorian if (gw.sin_addr.s_addr == INADDR_ANY) { 970351dd593Sflorian /* direct route */ 971351dd593Sflorian configure_route(rtm_type, imsg->if_index, 972351dd593Sflorian imsg->rdomain, &dst, &mask, &ifa, NULL, 973351dd593Sflorian RTF_CLONING); 974351dd593Sflorian } else if (mask.sin_addr.s_addr == INADDR_ANY) { 975351dd593Sflorian /* default route */ 976351dd593Sflorian gwnet = gw.sin_addr.s_addr & imsg->mask.s_addr; 977351dd593Sflorian if (addrnet != gwnet) { 978351dd593Sflorian /* 979351dd593Sflorian * The gateway for the default route is outside 980351dd593Sflorian * the configured prefix. Install a direct 981351dd593Sflorian * cloning route for the gateway to make the 982351dd593Sflorian * default route reachable. 983351dd593Sflorian */ 984351dd593Sflorian mask.sin_addr.s_addr = 0xffffffff; 985351dd593Sflorian configure_route(rtm_type, imsg->if_index, 986351dd593Sflorian imsg->rdomain, &gw, &mask, &ifa, NULL, 987351dd593Sflorian RTF_CLONING); 988b2b40540Sflorian mask.sin_addr = imsg->routes[i].mask; 989351dd593Sflorian } 990351dd593Sflorian 991351dd593Sflorian if (gw.sin_addr.s_addr == ifa.sin_addr.s_addr) { 992351dd593Sflorian /* directly connected default */ 993351dd593Sflorian configure_route(rtm_type, imsg->if_index, 994351dd593Sflorian imsg->rdomain, &dst, &mask, &gw, NULL, 0); 995351dd593Sflorian } else { 996351dd593Sflorian /* default route via gateway */ 997351dd593Sflorian configure_route(rtm_type, imsg->if_index, 998351dd593Sflorian imsg->rdomain, &dst, &mask, &gw, &ifa, 999351dd593Sflorian RTF_GATEWAY); 1000351dd593Sflorian } 1001351dd593Sflorian } else { 1002351dd593Sflorian /* non-default via gateway */ 1003351dd593Sflorian configure_route(rtm_type, imsg->if_index, imsg->rdomain, 1004351dd593Sflorian &dst, &mask, &gw, NULL, RTF_GATEWAY); 1005351dd593Sflorian } 1006351dd593Sflorian } 1007351dd593Sflorian } 1008351dd593Sflorian 100957419a7fSflorian #define ROUNDUP(a) \ 101057419a7fSflorian (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a)) 101157419a7fSflorian void 1012351dd593Sflorian configure_route(uint8_t rtm_type, uint32_t if_index, int rdomain, struct 1013351dd593Sflorian sockaddr_in *dst, struct sockaddr_in *mask, struct sockaddr_in *gw, 1014351dd593Sflorian struct sockaddr_in *ifa, int rtm_flags) 101557419a7fSflorian { 101657419a7fSflorian struct rt_msghdr rtm; 101757419a7fSflorian struct sockaddr_rtlabel rl; 10180be69ef6Sflorian struct iovec iov[12]; 101957419a7fSflorian long pad = 0; 102057419a7fSflorian int iovcnt = 0, padlen; 102157419a7fSflorian 102257419a7fSflorian memset(&rtm, 0, sizeof(rtm)); 102357419a7fSflorian 102457419a7fSflorian rtm.rtm_version = RTM_VERSION; 102557419a7fSflorian rtm.rtm_type = rtm_type; 102657419a7fSflorian rtm.rtm_msglen = sizeof(rtm); 1027351dd593Sflorian rtm.rtm_index = if_index; 1028351dd593Sflorian rtm.rtm_tableid = rdomain; 102957419a7fSflorian rtm.rtm_seq = ++rtm_seq; 103057419a7fSflorian rtm.rtm_priority = RTP_NONE; 1031351dd593Sflorian rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_LABEL; 1032351dd593Sflorian rtm.rtm_flags = RTF_UP | RTF_STATIC | RTF_MPATH | rtm_flags; 1033351dd593Sflorian 1034351dd593Sflorian if (ifa) 1035351dd593Sflorian rtm.rtm_addrs |= RTA_IFA; 103657419a7fSflorian 103757419a7fSflorian iov[iovcnt].iov_base = &rtm; 103857419a7fSflorian iov[iovcnt++].iov_len = sizeof(rtm); 103957419a7fSflorian 1040351dd593Sflorian iov[iovcnt].iov_base = dst; 1041351dd593Sflorian iov[iovcnt++].iov_len = dst->sin_len; 1042351dd593Sflorian rtm.rtm_msglen += dst->sin_len; 1043351dd593Sflorian padlen = ROUNDUP(dst->sin_len) - dst->sin_len; 104457419a7fSflorian if (padlen > 0) { 104557419a7fSflorian iov[iovcnt].iov_base = &pad; 104657419a7fSflorian iov[iovcnt++].iov_len = padlen; 104757419a7fSflorian rtm.rtm_msglen += padlen; 104857419a7fSflorian } 104957419a7fSflorian 1050351dd593Sflorian iov[iovcnt].iov_base = gw; 1051351dd593Sflorian iov[iovcnt++].iov_len = gw->sin_len; 1052351dd593Sflorian rtm.rtm_msglen += gw->sin_len; 1053351dd593Sflorian padlen = ROUNDUP(gw->sin_len) - gw->sin_len; 105457419a7fSflorian if (padlen > 0) { 105557419a7fSflorian iov[iovcnt].iov_base = &pad; 105657419a7fSflorian iov[iovcnt++].iov_len = padlen; 105757419a7fSflorian rtm.rtm_msglen += padlen; 105857419a7fSflorian } 105957419a7fSflorian 1060351dd593Sflorian iov[iovcnt].iov_base = mask; 1061351dd593Sflorian iov[iovcnt++].iov_len = mask->sin_len; 1062351dd593Sflorian rtm.rtm_msglen += mask->sin_len; 1063351dd593Sflorian padlen = ROUNDUP(mask->sin_len) - mask->sin_len; 106457419a7fSflorian if (padlen > 0) { 106557419a7fSflorian iov[iovcnt].iov_base = &pad; 106657419a7fSflorian iov[iovcnt++].iov_len = padlen; 106757419a7fSflorian rtm.rtm_msglen += padlen; 106857419a7fSflorian } 106957419a7fSflorian 1070351dd593Sflorian if (ifa) { 1071351dd593Sflorian iov[iovcnt].iov_base = ifa; 1072351dd593Sflorian iov[iovcnt++].iov_len = ifa->sin_len; 1073351dd593Sflorian rtm.rtm_msglen += ifa->sin_len; 1074351dd593Sflorian padlen = ROUNDUP(ifa->sin_len) - ifa->sin_len; 10750be69ef6Sflorian if (padlen > 0) { 10760be69ef6Sflorian iov[iovcnt].iov_base = &pad; 10770be69ef6Sflorian iov[iovcnt++].iov_len = padlen; 10780be69ef6Sflorian rtm.rtm_msglen += padlen; 10790be69ef6Sflorian } 1080351dd593Sflorian } 10810be69ef6Sflorian 108257419a7fSflorian memset(&rl, 0, sizeof(rl)); 108357419a7fSflorian rl.sr_len = sizeof(rl); 108457419a7fSflorian rl.sr_family = AF_UNSPEC; 108557419a7fSflorian (void)snprintf(rl.sr_label, sizeof(rl.sr_label), "%s", 108657419a7fSflorian DHCPLEASED_RTA_LABEL); 108757419a7fSflorian iov[iovcnt].iov_base = &rl; 108857419a7fSflorian iov[iovcnt++].iov_len = sizeof(rl); 108957419a7fSflorian rtm.rtm_msglen += sizeof(rl); 109057419a7fSflorian padlen = ROUNDUP(sizeof(rl)) - sizeof(rl); 109157419a7fSflorian if (padlen > 0) { 109257419a7fSflorian iov[iovcnt].iov_base = &pad; 109357419a7fSflorian iov[iovcnt++].iov_len = padlen; 109457419a7fSflorian rtm.rtm_msglen += padlen; 109557419a7fSflorian } 109657419a7fSflorian 1097e81612d0Sflorian if (writev(routesock, iov, iovcnt) == -1) { 1098e81612d0Sflorian if (errno != EEXIST) 109957419a7fSflorian log_warn("failed to send route message"); 110057419a7fSflorian } 1101e81612d0Sflorian } 110257419a7fSflorian 110357419a7fSflorian #ifndef SMALL 110457419a7fSflorian const char* 110557419a7fSflorian sin_to_str(struct sockaddr_in *sin) 110657419a7fSflorian { 110757419a7fSflorian static char hbuf[NI_MAXHOST]; 110857419a7fSflorian int error; 110957419a7fSflorian 111057419a7fSflorian error = getnameinfo((struct sockaddr *)sin, sin->sin_len, hbuf, 111157419a7fSflorian sizeof(hbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); 111257419a7fSflorian if (error) { 111357419a7fSflorian log_warnx("%s", gai_strerror(error)); 111457419a7fSflorian strlcpy(hbuf, "unknown", sizeof(hbuf)); 111557419a7fSflorian } 111657419a7fSflorian return hbuf; 111757419a7fSflorian } 111857419a7fSflorian #endif /* SMALL */ 111957419a7fSflorian 112057419a7fSflorian void 112157419a7fSflorian open_bpfsock(uint32_t if_index) 112257419a7fSflorian { 112357419a7fSflorian int bpfsock; 112457419a7fSflorian char ifname[IF_NAMESIZE]; 112557419a7fSflorian 1126e998cdbeSflorian if (if_indextoname(if_index, ifname) == NULL) { 112757419a7fSflorian log_warnx("%s: cannot find interface %d", __func__, if_index); 112857419a7fSflorian return; 112957419a7fSflorian } 113057419a7fSflorian 113157419a7fSflorian if ((bpfsock = get_bpf_sock(ifname)) == -1) 113257419a7fSflorian return; 113357419a7fSflorian 113457419a7fSflorian main_imsg_compose_frontend(IMSG_BPFSOCK, bpfsock, &if_index, 113557419a7fSflorian sizeof(if_index)); 113657419a7fSflorian } 113757419a7fSflorian 113857419a7fSflorian void 113957419a7fSflorian propose_rdns(struct imsg_propose_rdns *rdns) 114057419a7fSflorian { 114157419a7fSflorian struct rt_msghdr rtm; 114257419a7fSflorian struct sockaddr_rtdns rtdns; 114357419a7fSflorian struct iovec iov[3]; 114457419a7fSflorian long pad = 0; 114557419a7fSflorian int iovcnt = 0, padlen; 114657419a7fSflorian 114757419a7fSflorian memset(&rtm, 0, sizeof(rtm)); 114857419a7fSflorian 114957419a7fSflorian rtm.rtm_version = RTM_VERSION; 115057419a7fSflorian rtm.rtm_type = RTM_PROPOSAL; 115157419a7fSflorian rtm.rtm_msglen = sizeof(rtm); 115257419a7fSflorian rtm.rtm_tableid = rdns->rdomain; 115357419a7fSflorian rtm.rtm_index = rdns->if_index; 115457419a7fSflorian rtm.rtm_seq = ++rtm_seq; 115557419a7fSflorian rtm.rtm_priority = RTP_PROPOSAL_DHCLIENT; 115657419a7fSflorian rtm.rtm_addrs = RTA_DNS; 115757419a7fSflorian rtm.rtm_flags = RTF_UP; 115857419a7fSflorian 115957419a7fSflorian iov[iovcnt].iov_base = &rtm; 116057419a7fSflorian iov[iovcnt++].iov_len = sizeof(rtm); 116157419a7fSflorian 116257419a7fSflorian memset(&rtdns, 0, sizeof(rtdns)); 116357419a7fSflorian rtdns.sr_family = AF_INET; 116457419a7fSflorian rtdns.sr_len = 2 + rdns->rdns_count * sizeof(struct in_addr); 116557419a7fSflorian memcpy(rtdns.sr_dns, rdns->rdns, sizeof(rtdns.sr_dns)); 116657419a7fSflorian 116757419a7fSflorian iov[iovcnt].iov_base = &rtdns; 116857419a7fSflorian iov[iovcnt++].iov_len = sizeof(rtdns); 116957419a7fSflorian rtm.rtm_msglen += sizeof(rtdns); 117057419a7fSflorian padlen = ROUNDUP(sizeof(rtdns)) - sizeof(rtdns); 117157419a7fSflorian if (padlen > 0) { 117257419a7fSflorian iov[iovcnt].iov_base = &pad; 117357419a7fSflorian iov[iovcnt++].iov_len = padlen; 117457419a7fSflorian rtm.rtm_msglen += padlen; 117557419a7fSflorian } 117657419a7fSflorian 117757419a7fSflorian if (writev(routesock, iov, iovcnt) == -1) 1178e81612d0Sflorian log_warn("failed to propose nameservers"); 117957419a7fSflorian } 118057419a7fSflorian 11816e93e3e9Sflorian void 11826e93e3e9Sflorian read_lease_file(struct imsg_ifinfo *imsg_ifinfo) 118357419a7fSflorian { 11846e93e3e9Sflorian int len, fd; 118557419a7fSflorian char if_name[IF_NAMESIZE]; 1186131c304bSflorian char lease_file_buf[sizeof(_PATH_LEASE) + IF_NAMESIZE]; 118757419a7fSflorian 1188ae968ffdSflorian if (no_lease_files) 1189ae968ffdSflorian return; 1190ae968ffdSflorian 11916e93e3e9Sflorian memset(imsg_ifinfo->lease, 0, sizeof(imsg_ifinfo->lease)); 11926e93e3e9Sflorian 1193e998cdbeSflorian if (if_indextoname(imsg_ifinfo->if_index, if_name) == NULL) { 11946e93e3e9Sflorian log_warnx("%s: cannot find interface %d", __func__, 11956e93e3e9Sflorian imsg_ifinfo->if_index); 11966e93e3e9Sflorian return; 119757419a7fSflorian } 119857419a7fSflorian 119957419a7fSflorian len = snprintf(lease_file_buf, sizeof(lease_file_buf), "%s%s", 1200131c304bSflorian _PATH_LEASE, if_name); 120157419a7fSflorian if ( len == -1 || (size_t) len >= sizeof(lease_file_buf)) { 120257419a7fSflorian log_warnx("%s: failed to encode lease path for %s", __func__, 120357419a7fSflorian if_name); 12046e93e3e9Sflorian return; 120557419a7fSflorian } 120657419a7fSflorian 12076e93e3e9Sflorian if ((fd = open(lease_file_buf, O_RDONLY)) == -1) 12086e93e3e9Sflorian return; 12096e93e3e9Sflorian 12106e93e3e9Sflorian /* no need for error handling, we'll just do a DHCP discover */ 12116e93e3e9Sflorian read(fd, imsg_ifinfo->lease, sizeof(imsg_ifinfo->lease) - 1); 12126e93e3e9Sflorian close(fd); 121357419a7fSflorian } 1214a41cc082Sflorian 1215a41cc082Sflorian #ifndef SMALL 1216a41cc082Sflorian void 1217a41cc082Sflorian merge_config(struct dhcpleased_conf *conf, struct dhcpleased_conf *xconf) 1218a41cc082Sflorian { 1219a41cc082Sflorian struct iface_conf *iface_conf; 1220a41cc082Sflorian 1221a41cc082Sflorian /* Remove & discard existing interfaces. */ 1222a41cc082Sflorian while ((iface_conf = SIMPLEQ_FIRST(&conf->iface_list)) != NULL) { 1223a41cc082Sflorian SIMPLEQ_REMOVE_HEAD(&conf->iface_list, entry); 1224a41cc082Sflorian free(iface_conf->vc_id); 1225a41cc082Sflorian free(iface_conf->c_id); 1226b3441518Sflorian free(iface_conf->h_name); 1227a41cc082Sflorian free(iface_conf); 1228a41cc082Sflorian } 1229a41cc082Sflorian 1230a41cc082Sflorian /* Add new interfaces. */ 1231a41cc082Sflorian SIMPLEQ_CONCAT(&conf->iface_list, &xconf->iface_list); 1232a41cc082Sflorian 1233a41cc082Sflorian free(xconf); 1234a41cc082Sflorian } 1235a41cc082Sflorian 1236a41cc082Sflorian struct dhcpleased_conf * 1237a41cc082Sflorian config_new_empty(void) 1238a41cc082Sflorian { 1239a41cc082Sflorian struct dhcpleased_conf *xconf; 1240a41cc082Sflorian 1241a41cc082Sflorian xconf = calloc(1, sizeof(*xconf)); 1242a41cc082Sflorian if (xconf == NULL) 1243a41cc082Sflorian fatal(NULL); 1244a41cc082Sflorian 1245a41cc082Sflorian SIMPLEQ_INIT(&xconf->iface_list); 1246a41cc082Sflorian 1247a41cc082Sflorian return (xconf); 1248a41cc082Sflorian } 1249a41cc082Sflorian 1250a41cc082Sflorian void 1251a41cc082Sflorian config_clear(struct dhcpleased_conf *conf) 1252a41cc082Sflorian { 1253a41cc082Sflorian struct dhcpleased_conf *xconf; 1254a41cc082Sflorian 1255a41cc082Sflorian /* Merge current config with an empty config. */ 1256a41cc082Sflorian xconf = config_new_empty(); 1257a41cc082Sflorian merge_config(conf, xconf); 1258a41cc082Sflorian 1259a41cc082Sflorian free(conf); 1260a41cc082Sflorian } 1261a41cc082Sflorian #endif /* SMALL */ 1262