1*db7d8776Sflorian /* $OpenBSD: dhcpleased.c,v 1.17 2021/07/26 09:22:00 florian 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 8957419a7fSflorian static struct imsgev *iev_frontend; 9057419a7fSflorian static struct imsgev *iev_engine; 9157419a7fSflorian 9257419a7fSflorian pid_t frontend_pid; 9357419a7fSflorian pid_t engine_pid; 9457419a7fSflorian 95ae968ffdSflorian int routesock, ioctl_sock, rtm_seq, no_lease_files; 9657419a7fSflorian 9757419a7fSflorian void 9857419a7fSflorian main_sig_handler(int sig, short event, void *arg) 9957419a7fSflorian { 10057419a7fSflorian /* 10157419a7fSflorian * Normal signal handler rules don't apply because libevent 10257419a7fSflorian * decouples for us. 10357419a7fSflorian */ 10457419a7fSflorian 10557419a7fSflorian switch (sig) { 10657419a7fSflorian case SIGTERM: 10757419a7fSflorian case SIGINT: 10857419a7fSflorian main_shutdown(); 10957419a7fSflorian default: 11057419a7fSflorian fatalx("unexpected signal"); 11157419a7fSflorian } 11257419a7fSflorian } 11357419a7fSflorian 11457419a7fSflorian __dead void 11557419a7fSflorian usage(void) 11657419a7fSflorian { 11757419a7fSflorian extern char *__progname; 11857419a7fSflorian 11957419a7fSflorian fprintf(stderr, "usage: %s [-dv] [-s socket]\n", 12057419a7fSflorian __progname); 12157419a7fSflorian exit(1); 12257419a7fSflorian } 12357419a7fSflorian 12457419a7fSflorian int 12557419a7fSflorian main(int argc, char *argv[]) 12657419a7fSflorian { 12757419a7fSflorian struct event ev_sigint, ev_sigterm; 12857419a7fSflorian int ch; 12957419a7fSflorian int debug = 0, engine_flag = 0, frontend_flag = 0; 13057419a7fSflorian int verbose = 0; 13157419a7fSflorian char *saved_argv0; 13257419a7fSflorian int pipe_main2frontend[2]; 13357419a7fSflorian int pipe_main2engine[2]; 134bc9eb55cSkn int frontend_routesock, rtfilter, lockfd; 13557419a7fSflorian int rtable_any = RTABLE_ANY; 136131c304bSflorian char *csock = _PATH_DHCPLEASED_SOCKET; 13757419a7fSflorian #ifndef SMALL 13857419a7fSflorian int control_fd; 13957419a7fSflorian #endif /* SMALL */ 14057419a7fSflorian 14157419a7fSflorian log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */ 14257419a7fSflorian log_setverbose(1); 14357419a7fSflorian 14457419a7fSflorian saved_argv0 = argv[0]; 14557419a7fSflorian if (saved_argv0 == NULL) 14657419a7fSflorian saved_argv0 = "dhcpleased"; 14757419a7fSflorian 14857419a7fSflorian while ((ch = getopt(argc, argv, "dEFs:v")) != -1) { 14957419a7fSflorian switch (ch) { 15057419a7fSflorian case 'd': 15157419a7fSflorian debug = 1; 15257419a7fSflorian break; 15357419a7fSflorian case 'E': 15457419a7fSflorian engine_flag = 1; 15557419a7fSflorian break; 15657419a7fSflorian case 'F': 15757419a7fSflorian frontend_flag = 1; 15857419a7fSflorian break; 15957419a7fSflorian case 's': 16057419a7fSflorian csock = optarg; 16157419a7fSflorian break; 16257419a7fSflorian case 'v': 16357419a7fSflorian verbose++; 16457419a7fSflorian break; 16557419a7fSflorian default: 16657419a7fSflorian usage(); 16757419a7fSflorian } 16857419a7fSflorian } 16957419a7fSflorian 17057419a7fSflorian argc -= optind; 17157419a7fSflorian argv += optind; 17257419a7fSflorian if (argc > 0 || (engine_flag && frontend_flag)) 17357419a7fSflorian usage(); 17457419a7fSflorian 17557419a7fSflorian if (engine_flag) 17657419a7fSflorian engine(debug, verbose); 17757419a7fSflorian else if (frontend_flag) 17857419a7fSflorian frontend(debug, verbose); 17957419a7fSflorian 18057419a7fSflorian /* Check for root privileges. */ 18157419a7fSflorian if (geteuid()) 18257419a7fSflorian errx(1, "need root privileges"); 18357419a7fSflorian 184bc9eb55cSkn lockfd = open(_PATH_LOCKFILE, O_CREAT|O_RDWR|O_EXLOCK|O_NONBLOCK, 0600); 185bc9eb55cSkn if (lockfd == -1) 186bc9eb55cSkn errx(1, "already running"); 187bc9eb55cSkn 18857419a7fSflorian /* Check for assigned daemon user */ 18957419a7fSflorian if (getpwnam(DHCPLEASED_USER) == NULL) 19057419a7fSflorian errx(1, "unknown user %s", DHCPLEASED_USER); 19157419a7fSflorian 19257419a7fSflorian log_init(debug, LOG_DAEMON); 19357419a7fSflorian log_setverbose(verbose); 19457419a7fSflorian 19557419a7fSflorian if (!debug) 19657419a7fSflorian daemon(0, 0); 19757419a7fSflorian 19857419a7fSflorian if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 19957419a7fSflorian PF_UNSPEC, pipe_main2frontend) == -1) 20057419a7fSflorian fatal("main2frontend socketpair"); 20157419a7fSflorian if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 20257419a7fSflorian PF_UNSPEC, pipe_main2engine) == -1) 20357419a7fSflorian fatal("main2engine socketpair"); 20457419a7fSflorian 20557419a7fSflorian /* Start children. */ 20657419a7fSflorian engine_pid = start_child(PROC_ENGINE, saved_argv0, pipe_main2engine[1], 20757419a7fSflorian debug, verbose); 20857419a7fSflorian frontend_pid = start_child(PROC_FRONTEND, saved_argv0, 20957419a7fSflorian pipe_main2frontend[1], debug, verbose); 21057419a7fSflorian 21157419a7fSflorian log_procinit("main"); 21257419a7fSflorian 21357419a7fSflorian if ((routesock = socket(AF_ROUTE, SOCK_RAW | SOCK_CLOEXEC | 21457419a7fSflorian SOCK_NONBLOCK, AF_INET)) == -1) 21557419a7fSflorian fatal("route socket"); 21657419a7fSflorian shutdown(SHUT_RD, routesock); 21757419a7fSflorian 21857419a7fSflorian event_init(); 21957419a7fSflorian 22057419a7fSflorian /* Setup signal handler. */ 22157419a7fSflorian signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); 22257419a7fSflorian signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); 22357419a7fSflorian signal_add(&ev_sigint, NULL); 22457419a7fSflorian signal_add(&ev_sigterm, NULL); 22557419a7fSflorian signal(SIGPIPE, SIG_IGN); 22657419a7fSflorian signal(SIGHUP, SIG_IGN); 22757419a7fSflorian 22857419a7fSflorian /* Setup pipes to children. */ 22957419a7fSflorian 23057419a7fSflorian if ((iev_frontend = malloc(sizeof(struct imsgev))) == NULL || 23157419a7fSflorian (iev_engine = malloc(sizeof(struct imsgev))) == NULL) 23257419a7fSflorian fatal(NULL); 23357419a7fSflorian imsg_init(&iev_frontend->ibuf, pipe_main2frontend[0]); 23457419a7fSflorian iev_frontend->handler = main_dispatch_frontend; 23557419a7fSflorian imsg_init(&iev_engine->ibuf, pipe_main2engine[0]); 23657419a7fSflorian iev_engine->handler = main_dispatch_engine; 23757419a7fSflorian 23857419a7fSflorian /* Setup event handlers for pipes to engine & frontend. */ 23957419a7fSflorian iev_frontend->events = EV_READ; 24057419a7fSflorian event_set(&iev_frontend->ev, iev_frontend->ibuf.fd, 24157419a7fSflorian iev_frontend->events, iev_frontend->handler, iev_frontend); 24257419a7fSflorian event_add(&iev_frontend->ev, NULL); 24357419a7fSflorian 24457419a7fSflorian iev_engine->events = EV_READ; 24557419a7fSflorian event_set(&iev_engine->ev, iev_engine->ibuf.fd, iev_engine->events, 24657419a7fSflorian iev_engine->handler, iev_engine); 24757419a7fSflorian event_add(&iev_engine->ev, NULL); 24857419a7fSflorian 24957419a7fSflorian if (main_imsg_send_ipc_sockets(&iev_frontend->ibuf, &iev_engine->ibuf)) 25057419a7fSflorian fatal("could not establish imsg links"); 25157419a7fSflorian 25257419a7fSflorian if ((ioctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) 25357419a7fSflorian fatal("socket"); 25457419a7fSflorian 25557419a7fSflorian if ((frontend_routesock = socket(AF_ROUTE, SOCK_RAW | SOCK_CLOEXEC, 25657419a7fSflorian AF_INET)) == -1) 25757419a7fSflorian fatal("route socket"); 25857419a7fSflorian 2595651a563Sflorian rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_PROPOSAL); 26057419a7fSflorian if (setsockopt(frontend_routesock, AF_ROUTE, ROUTE_MSGFILTER, 26157419a7fSflorian &rtfilter, sizeof(rtfilter)) == -1) 26257419a7fSflorian fatal("setsockopt(ROUTE_MSGFILTER)"); 26357419a7fSflorian if (setsockopt(frontend_routesock, AF_ROUTE, ROUTE_TABLEFILTER, 26457419a7fSflorian &rtable_any, sizeof(rtable_any)) == -1) 26557419a7fSflorian fatal("setsockopt(ROUTE_TABLEFILTER)"); 26657419a7fSflorian 26757419a7fSflorian #ifndef SMALL 26857419a7fSflorian if ((control_fd = control_init(csock)) == -1) 2694b9fcc1bSflorian warnx("control socket setup failed"); 27057419a7fSflorian #endif /* SMALL */ 27157419a7fSflorian 27257419a7fSflorian if (unveil("/dev/bpf", "rw") == -1) 273f5208191Sflorian fatal("unveil /dev/bpf"); 27457419a7fSflorian 275ae968ffdSflorian if (unveil(_PATH_LEASE, "rwc") == -1) { 276ae968ffdSflorian no_lease_files = 1; 277ae968ffdSflorian log_warn("disabling lease files, unveil " _PATH_LEASE); 278ae968ffdSflorian } 27957419a7fSflorian 28057419a7fSflorian if (unveil(NULL, NULL) == -1) 281bc5a8259Sbeck fatal("unveil"); 28257419a7fSflorian #if notyet 28357419a7fSflorian if (pledge("stdio inet rpath wpath sendfd wroute bpf", NULL) == -1) 28457419a7fSflorian fatal("pledge"); 28557419a7fSflorian #endif 28657419a7fSflorian main_imsg_compose_frontend(IMSG_ROUTESOCK, frontend_routesock, NULL, 0); 28757419a7fSflorian 28857419a7fSflorian #ifndef SMALL 2894b9fcc1bSflorian if (control_fd != -1) 29057419a7fSflorian main_imsg_compose_frontend(IMSG_CONTROLFD, control_fd, NULL, 0); 29157419a7fSflorian #endif /* SMALL */ 29257419a7fSflorian 29357419a7fSflorian main_imsg_compose_frontend(IMSG_STARTUP, -1, NULL, 0); 29457419a7fSflorian 29557419a7fSflorian event_dispatch(); 29657419a7fSflorian 29757419a7fSflorian main_shutdown(); 29857419a7fSflorian return (0); 29957419a7fSflorian } 30057419a7fSflorian 30157419a7fSflorian __dead void 30257419a7fSflorian main_shutdown(void) 30357419a7fSflorian { 30457419a7fSflorian pid_t pid; 30557419a7fSflorian int status; 30657419a7fSflorian 30757419a7fSflorian /* Close pipes. */ 30857419a7fSflorian msgbuf_clear(&iev_frontend->ibuf.w); 30957419a7fSflorian close(iev_frontend->ibuf.fd); 31057419a7fSflorian msgbuf_clear(&iev_engine->ibuf.w); 31157419a7fSflorian close(iev_engine->ibuf.fd); 31257419a7fSflorian 31357419a7fSflorian log_debug("waiting for children to terminate"); 31457419a7fSflorian do { 31557419a7fSflorian pid = wait(&status); 31657419a7fSflorian if (pid == -1) { 31757419a7fSflorian if (errno != EINTR && errno != ECHILD) 31857419a7fSflorian fatal("wait"); 31957419a7fSflorian } else if (WIFSIGNALED(status)) 32057419a7fSflorian log_warnx("%s terminated; signal %d", 32157419a7fSflorian (pid == engine_pid) ? "engine" : 32257419a7fSflorian "frontend", WTERMSIG(status)); 32357419a7fSflorian } while (pid != -1 || (pid == -1 && errno == EINTR)); 32457419a7fSflorian 32557419a7fSflorian free(iev_frontend); 32657419a7fSflorian free(iev_engine); 32757419a7fSflorian 32857419a7fSflorian log_info("terminating"); 32957419a7fSflorian exit(0); 33057419a7fSflorian } 33157419a7fSflorian 33257419a7fSflorian static pid_t 33357419a7fSflorian start_child(enum dhcpleased_process p, char *argv0, int fd, int debug, int 33457419a7fSflorian verbose) 33557419a7fSflorian { 33657419a7fSflorian char *argv[7]; 33757419a7fSflorian int argc = 0; 33857419a7fSflorian pid_t pid; 33957419a7fSflorian 34057419a7fSflorian switch (pid = fork()) { 34157419a7fSflorian case -1: 34257419a7fSflorian fatal("cannot fork"); 34357419a7fSflorian case 0: 34457419a7fSflorian break; 34557419a7fSflorian default: 34657419a7fSflorian close(fd); 34757419a7fSflorian return (pid); 34857419a7fSflorian } 34957419a7fSflorian 35057419a7fSflorian if (fd != 3) { 35157419a7fSflorian if (dup2(fd, 3) == -1) 35257419a7fSflorian fatal("cannot setup imsg fd"); 35357419a7fSflorian } else if (fcntl(fd, F_SETFD, 0) == -1) 35457419a7fSflorian fatal("cannot setup imsg fd"); 35557419a7fSflorian 35657419a7fSflorian argv[argc++] = argv0; 35757419a7fSflorian switch (p) { 35857419a7fSflorian case PROC_MAIN: 35957419a7fSflorian fatalx("Can not start main process"); 36057419a7fSflorian case PROC_ENGINE: 36157419a7fSflorian argv[argc++] = "-E"; 36257419a7fSflorian break; 36357419a7fSflorian case PROC_FRONTEND: 36457419a7fSflorian argv[argc++] = "-F"; 36557419a7fSflorian break; 36657419a7fSflorian } 36757419a7fSflorian if (debug) 36857419a7fSflorian argv[argc++] = "-d"; 36957419a7fSflorian if (verbose) 37057419a7fSflorian argv[argc++] = "-v"; 37157419a7fSflorian if (verbose > 1) 37257419a7fSflorian argv[argc++] = "-v"; 37357419a7fSflorian argv[argc++] = NULL; 37457419a7fSflorian 37557419a7fSflorian execvp(argv0, argv); 37657419a7fSflorian fatal("execvp"); 37757419a7fSflorian } 37857419a7fSflorian 37957419a7fSflorian void 38057419a7fSflorian main_dispatch_frontend(int fd, short event, void *bula) 38157419a7fSflorian { 38257419a7fSflorian struct imsgev *iev = bula; 38357419a7fSflorian struct imsgbuf *ibuf; 38457419a7fSflorian struct imsg imsg; 38557419a7fSflorian struct imsg_ifinfo imsg_ifinfo; 38657419a7fSflorian ssize_t n; 38757419a7fSflorian int shut = 0; 38857419a7fSflorian uint32_t if_index; 38957419a7fSflorian #ifndef SMALL 39057419a7fSflorian int verbose; 39157419a7fSflorian #endif /* SMALL */ 39257419a7fSflorian 39357419a7fSflorian ibuf = &iev->ibuf; 39457419a7fSflorian 39557419a7fSflorian if (event & EV_READ) { 39657419a7fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 39757419a7fSflorian fatal("imsg_read error"); 39857419a7fSflorian if (n == 0) /* Connection closed. */ 39957419a7fSflorian shut = 1; 40057419a7fSflorian } 40157419a7fSflorian if (event & EV_WRITE) { 40257419a7fSflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 40357419a7fSflorian fatal("msgbuf_write"); 40457419a7fSflorian if (n == 0) /* Connection closed. */ 40557419a7fSflorian shut = 1; 40657419a7fSflorian } 40757419a7fSflorian 40857419a7fSflorian for (;;) { 40957419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 41057419a7fSflorian fatal("imsg_get"); 41157419a7fSflorian if (n == 0) /* No more messages. */ 41257419a7fSflorian break; 41357419a7fSflorian 41457419a7fSflorian switch (imsg.hdr.type) { 41557419a7fSflorian case IMSG_OPEN_BPFSOCK: 41657419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 41757419a7fSflorian fatalx("%s: IMSG_OPEN_BPFSOCK wrong length: " 41857419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 41957419a7fSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 42057419a7fSflorian open_bpfsock(if_index); 42157419a7fSflorian break; 42257419a7fSflorian #ifndef SMALL 42357419a7fSflorian case IMSG_CTL_LOG_VERBOSE: 42457419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(verbose)) 42557419a7fSflorian fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: " 42657419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 42757419a7fSflorian memcpy(&verbose, imsg.data, sizeof(verbose)); 42857419a7fSflorian log_setverbose(verbose); 42957419a7fSflorian break; 43057419a7fSflorian #endif /* SMALL */ 43157419a7fSflorian case IMSG_UPDATE_IF: 43257419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_ifinfo)) 43357419a7fSflorian fatalx("%s: IMSG_UPDATE_IF wrong length: %lu", 43457419a7fSflorian __func__, IMSG_DATA_SIZE(imsg)); 43557419a7fSflorian memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo)); 4366e93e3e9Sflorian read_lease_file(&imsg_ifinfo); 4376e93e3e9Sflorian main_imsg_compose_engine(IMSG_UPDATE_IF, -1, 4386e93e3e9Sflorian &imsg_ifinfo, sizeof(imsg_ifinfo)); 43957419a7fSflorian break; 44057419a7fSflorian default: 44157419a7fSflorian log_debug("%s: error handling imsg %d", __func__, 44257419a7fSflorian imsg.hdr.type); 44357419a7fSflorian break; 44457419a7fSflorian } 44557419a7fSflorian imsg_free(&imsg); 44657419a7fSflorian } 44757419a7fSflorian if (!shut) 44857419a7fSflorian imsg_event_add(iev); 44957419a7fSflorian else { 45057419a7fSflorian /* This pipe is dead. Remove its event handler */ 45157419a7fSflorian event_del(&iev->ev); 45257419a7fSflorian event_loopexit(NULL); 45357419a7fSflorian } 45457419a7fSflorian } 45557419a7fSflorian 45657419a7fSflorian void 45757419a7fSflorian main_dispatch_engine(int fd, short event, void *bula) 45857419a7fSflorian { 45957419a7fSflorian struct imsgev *iev = bula; 46057419a7fSflorian struct imsgbuf *ibuf; 46157419a7fSflorian struct imsg imsg; 46257419a7fSflorian ssize_t n; 46357419a7fSflorian int shut = 0; 46457419a7fSflorian 46557419a7fSflorian ibuf = &iev->ibuf; 46657419a7fSflorian 46757419a7fSflorian if (event & EV_READ) { 46857419a7fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 46957419a7fSflorian fatal("imsg_read error"); 47057419a7fSflorian if (n == 0) /* Connection closed. */ 47157419a7fSflorian shut = 1; 47257419a7fSflorian } 47357419a7fSflorian if (event & EV_WRITE) { 47457419a7fSflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 47557419a7fSflorian fatal("msgbuf_write"); 47657419a7fSflorian if (n == 0) /* Connection closed. */ 47757419a7fSflorian shut = 1; 47857419a7fSflorian } 47957419a7fSflorian 48057419a7fSflorian for (;;) { 48157419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 48257419a7fSflorian fatal("imsg_get"); 48357419a7fSflorian if (n == 0) /* No more messages. */ 48457419a7fSflorian break; 48557419a7fSflorian 48657419a7fSflorian switch (imsg.hdr.type) { 48757419a7fSflorian case IMSG_CONFIGURE_INTERFACE: { 48857419a7fSflorian struct imsg_configure_interface imsg_interface; 48957419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_interface)) 49057419a7fSflorian fatalx("%s: IMSG_CONFIGURE_INTERFACE wrong " 49157419a7fSflorian "length: %lu", __func__, 49257419a7fSflorian IMSG_DATA_SIZE(imsg)); 49357419a7fSflorian memcpy(&imsg_interface, imsg.data, 49457419a7fSflorian sizeof(imsg_interface)); 495351dd593Sflorian if (imsg_interface.routes_len >= MAX_DHCP_ROUTES) 496351dd593Sflorian fatalx("%s: too many routes in imsg", __func__); 49757419a7fSflorian configure_interface(&imsg_interface); 49857419a7fSflorian break; 49957419a7fSflorian } 50057419a7fSflorian case IMSG_DECONFIGURE_INTERFACE: { 50157419a7fSflorian struct imsg_configure_interface imsg_interface; 50257419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_interface)) 50357419a7fSflorian fatalx("%s: IMSG_CONFIGURE_INTERFACE wrong " 50457419a7fSflorian "length: %lu", __func__, 50557419a7fSflorian IMSG_DATA_SIZE(imsg)); 50657419a7fSflorian memcpy(&imsg_interface, imsg.data, 50757419a7fSflorian sizeof(imsg_interface)); 508351dd593Sflorian if (imsg_interface.routes_len >= MAX_DHCP_ROUTES) 509351dd593Sflorian fatalx("%s: too many routes in imsg", __func__); 51057419a7fSflorian deconfigure_interface(&imsg_interface); 51157419a7fSflorian main_imsg_compose_frontend(IMSG_CLOSE_UDPSOCK, -1, 51257419a7fSflorian &imsg_interface.if_index, 51357419a7fSflorian sizeof(imsg_interface.if_index)); 51457419a7fSflorian break; 51557419a7fSflorian } 51657419a7fSflorian case IMSG_PROPOSE_RDNS: { 51757419a7fSflorian struct imsg_propose_rdns rdns; 51857419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(rdns)) 51957419a7fSflorian fatalx("%s: IMSG_PROPOSE_RDNS wrong " 52057419a7fSflorian "length: %lu", __func__, 52157419a7fSflorian IMSG_DATA_SIZE(imsg)); 52257419a7fSflorian memcpy(&rdns, imsg.data, sizeof(rdns)); 52357419a7fSflorian if ((2 + rdns.rdns_count * sizeof(struct in_addr)) > 52457419a7fSflorian sizeof(struct sockaddr_rtdns)) 52557419a7fSflorian fatalx("%s: rdns_count too big: %d", __func__, 52657419a7fSflorian rdns.rdns_count); 52757419a7fSflorian propose_rdns(&rdns); 52857419a7fSflorian break; 52957419a7fSflorian } 53057419a7fSflorian case IMSG_WITHDRAW_RDNS: { 53157419a7fSflorian struct imsg_propose_rdns rdns; 53257419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(rdns)) 53357419a7fSflorian fatalx("%s: IMSG_PROPOSE_RDNS wrong " 53457419a7fSflorian "length: %lu", __func__, 53557419a7fSflorian IMSG_DATA_SIZE(imsg)); 53657419a7fSflorian memcpy(&rdns, imsg.data, sizeof(rdns)); 53757419a7fSflorian if (rdns.rdns_count != 0) 53857419a7fSflorian fatalx("%s: expected rdns_count == 0: %d", 53957419a7fSflorian __func__, rdns.rdns_count); 54057419a7fSflorian propose_rdns(&rdns); 54157419a7fSflorian break; 54257419a7fSflorian } 54357419a7fSflorian default: 54457419a7fSflorian log_debug("%s: error handling imsg %d", __func__, 54557419a7fSflorian imsg.hdr.type); 54657419a7fSflorian break; 54757419a7fSflorian } 54857419a7fSflorian imsg_free(&imsg); 54957419a7fSflorian } 55057419a7fSflorian if (!shut) 55157419a7fSflorian imsg_event_add(iev); 55257419a7fSflorian else { 55357419a7fSflorian /* This pipe is dead. Remove its event handler. */ 55457419a7fSflorian event_del(&iev->ev); 55557419a7fSflorian event_loopexit(NULL); 55657419a7fSflorian } 55757419a7fSflorian } 55857419a7fSflorian 55957419a7fSflorian int 56057419a7fSflorian main_imsg_compose_frontend(int type, int fd, void *data, uint16_t datalen) 56157419a7fSflorian { 56257419a7fSflorian if (iev_frontend) 56357419a7fSflorian return (imsg_compose_event(iev_frontend, type, 0, 0, fd, data, 56457419a7fSflorian datalen)); 56557419a7fSflorian else 56657419a7fSflorian return (-1); 56757419a7fSflorian } 56857419a7fSflorian 56957419a7fSflorian int 57057419a7fSflorian main_imsg_compose_engine(int type, int fd, void *data, uint16_t datalen) 57157419a7fSflorian { 57257419a7fSflorian if (iev_engine) 57357419a7fSflorian return(imsg_compose_event(iev_engine, type, 0, 0, fd, data, 57457419a7fSflorian datalen)); 57557419a7fSflorian else 57657419a7fSflorian return (-1); 57757419a7fSflorian } 57857419a7fSflorian 57957419a7fSflorian void 58057419a7fSflorian imsg_event_add(struct imsgev *iev) 58157419a7fSflorian { 58257419a7fSflorian iev->events = EV_READ; 58357419a7fSflorian if (iev->ibuf.w.queued) 58457419a7fSflorian iev->events |= EV_WRITE; 58557419a7fSflorian 58657419a7fSflorian event_del(&iev->ev); 58757419a7fSflorian event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); 58857419a7fSflorian event_add(&iev->ev, NULL); 58957419a7fSflorian } 59057419a7fSflorian 59157419a7fSflorian int 59257419a7fSflorian imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 59357419a7fSflorian pid_t pid, int fd, void *data, uint16_t datalen) 59457419a7fSflorian { 59557419a7fSflorian int ret; 59657419a7fSflorian 59757419a7fSflorian if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, 59857419a7fSflorian datalen)) != -1) 59957419a7fSflorian imsg_event_add(iev); 60057419a7fSflorian 60157419a7fSflorian return (ret); 60257419a7fSflorian } 60357419a7fSflorian 60457419a7fSflorian static int 60557419a7fSflorian main_imsg_send_ipc_sockets(struct imsgbuf *frontend_buf, 60657419a7fSflorian struct imsgbuf *engine_buf) 60757419a7fSflorian { 60857419a7fSflorian int pipe_frontend2engine[2]; 60957419a7fSflorian 61057419a7fSflorian if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 61157419a7fSflorian PF_UNSPEC, pipe_frontend2engine) == -1) 61257419a7fSflorian return (-1); 61357419a7fSflorian 61457419a7fSflorian if (imsg_compose(frontend_buf, IMSG_SOCKET_IPC, 0, 0, 61557419a7fSflorian pipe_frontend2engine[0], NULL, 0) == -1) 61657419a7fSflorian return (-1); 61757419a7fSflorian imsg_flush(frontend_buf); 61857419a7fSflorian if (imsg_compose(engine_buf, IMSG_SOCKET_IPC, 0, 0, 61957419a7fSflorian pipe_frontend2engine[1], NULL, 0) == -1) 62057419a7fSflorian return (-1); 62157419a7fSflorian imsg_flush(engine_buf); 62257419a7fSflorian return (0); 62357419a7fSflorian } 62457419a7fSflorian 62557419a7fSflorian void 62657419a7fSflorian configure_interface(struct imsg_configure_interface *imsg) 62757419a7fSflorian { 62857419a7fSflorian struct ifaliasreq ifaliasreq; 62957419a7fSflorian struct ifaddrs *ifap, *ifa; 63057419a7fSflorian struct sockaddr_in *req_sin_addr, *req_sin_mask; 63157419a7fSflorian int found = 0, udpsock, opt = 1, len, fd = -1; 63257419a7fSflorian char *if_name; 633ebace80cSflorian char ip_ntop_buf[INET_ADDRSTRLEN]; 634ebace80cSflorian char nextserver_ntop_buf[INET_ADDRSTRLEN]; 63557419a7fSflorian char lease_buf[LEASE_SIZE]; 636131c304bSflorian char lease_file_buf[sizeof(_PATH_LEASE) + 63757419a7fSflorian IF_NAMESIZE]; 638131c304bSflorian char tmpl[] = _PATH_LEASE"XXXXXXXXXX"; 63957419a7fSflorian 64057419a7fSflorian memset(&ifaliasreq, 0, sizeof(ifaliasreq)); 64157419a7fSflorian 64257419a7fSflorian if_name = if_indextoname(imsg->if_index, ifaliasreq.ifra_name); 64357419a7fSflorian if (if_name == NULL) { 64457419a7fSflorian log_warnx("%s: cannot find interface %d", __func__, 64557419a7fSflorian imsg->if_index); 64657419a7fSflorian return; 64757419a7fSflorian } 64857419a7fSflorian 649e998cdbeSflorian log_debug("%s %s", __func__, if_name); 650e998cdbeSflorian 65157419a7fSflorian if (getifaddrs(&ifap) != 0) 65257419a7fSflorian fatal("getifaddrs"); 65357419a7fSflorian 65457419a7fSflorian req_sin_addr = (struct sockaddr_in *)&ifaliasreq.ifra_addr; 65557419a7fSflorian req_sin_addr->sin_family = AF_INET; 65657419a7fSflorian req_sin_addr->sin_len = sizeof(*req_sin_addr); 65757419a7fSflorian 65857419a7fSflorian for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 65957419a7fSflorian struct in_addr addr, mask; 66057419a7fSflorian 66157419a7fSflorian if (strcmp(if_name, ifa->ifa_name) != 0) 66257419a7fSflorian continue; 66357419a7fSflorian if (ifa->ifa_addr == NULL) 66457419a7fSflorian continue; 66557419a7fSflorian if (ifa->ifa_addr->sa_family != AF_INET) 66657419a7fSflorian continue; 66757419a7fSflorian 66857419a7fSflorian addr.s_addr = ((struct sockaddr_in *)ifa->ifa_addr) 66957419a7fSflorian ->sin_addr.s_addr; 67057419a7fSflorian mask.s_addr = ((struct sockaddr_in *)ifa->ifa_netmask) 67157419a7fSflorian ->sin_addr.s_addr; 67257419a7fSflorian 67357419a7fSflorian if (imsg->addr.s_addr == addr.s_addr) { 67457419a7fSflorian if (imsg->mask.s_addr == mask.s_addr) 67557419a7fSflorian found = 1; 67657419a7fSflorian else { 67757419a7fSflorian req_sin_addr->sin_addr.s_addr = addr.s_addr; 67857419a7fSflorian if (ioctl(ioctl_sock, SIOCDIFADDR, &ifaliasreq) 67957419a7fSflorian == -1) { 68057419a7fSflorian if (errno != EADDRNOTAVAIL) 68157419a7fSflorian log_warn("SIOCDIFADDR"); 68257419a7fSflorian } 68357419a7fSflorian } 68457419a7fSflorian break; 68557419a7fSflorian } 68657419a7fSflorian } 68757419a7fSflorian 68857419a7fSflorian req_sin_addr->sin_addr.s_addr = imsg->addr.s_addr; 68957419a7fSflorian if (!found) { 69057419a7fSflorian req_sin_mask = (struct sockaddr_in *)&ifaliasreq.ifra_mask; 69157419a7fSflorian req_sin_mask->sin_family = AF_INET; 69257419a7fSflorian req_sin_mask->sin_len = sizeof(*req_sin_mask); 69357419a7fSflorian req_sin_mask->sin_addr.s_addr = imsg->mask.s_addr; 69457419a7fSflorian if (ioctl(ioctl_sock, SIOCAIFADDR, &ifaliasreq) == -1) 695*db7d8776Sflorian log_warn("SIOCAIFADDR"); 696665300f1Sstsp } 697351dd593Sflorian if (imsg->routes_len > 0) 698351dd593Sflorian configure_routes(RTM_ADD, imsg); 699665300f1Sstsp 70057419a7fSflorian req_sin_addr->sin_port = ntohs(CLIENT_PORT); 70157419a7fSflorian if ((udpsock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 70257419a7fSflorian log_warn("socket"); 70357419a7fSflorian return; 70457419a7fSflorian } 70557419a7fSflorian if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR, &opt, 70657419a7fSflorian sizeof(opt)) == -1) 70757419a7fSflorian log_warn("setting SO_REUSEADDR on socket"); 70857419a7fSflorian 70957419a7fSflorian if (setsockopt(udpsock, SOL_SOCKET, SO_RTABLE, &imsg->rdomain, 71057419a7fSflorian sizeof(imsg->rdomain)) == -1) { 71157419a7fSflorian /* we might race against removal of the rdomain */ 71257419a7fSflorian log_warn("setsockopt SO_RTABLE"); 71357419a7fSflorian close(udpsock); 71457419a7fSflorian return; 71557419a7fSflorian } 71657419a7fSflorian 71757419a7fSflorian if (bind(udpsock, (struct sockaddr *)req_sin_addr, 71857419a7fSflorian sizeof(*req_sin_addr)) == -1) { 71957419a7fSflorian close(udpsock); 72057419a7fSflorian return; 72157419a7fSflorian } 72257419a7fSflorian 72357419a7fSflorian shutdown(udpsock, SHUT_RD); 724e998cdbeSflorian 72557419a7fSflorian main_imsg_compose_frontend(IMSG_UDPSOCK, udpsock, 72657419a7fSflorian &imsg->if_index, sizeof(imsg->if_index)); 72757419a7fSflorian 728ae968ffdSflorian if (no_lease_files) 729ae968ffdSflorian return; 730ae968ffdSflorian 731ebace80cSflorian if (inet_ntop(AF_INET, &imsg->addr, ip_ntop_buf, sizeof(ip_ntop_buf)) == 73257419a7fSflorian NULL) { 73357419a7fSflorian log_warn("%s: inet_ntop", __func__); 73457419a7fSflorian return; 73557419a7fSflorian } 73657419a7fSflorian 737ebace80cSflorian if (imsg->siaddr.s_addr == INADDR_ANY) 738ebace80cSflorian nextserver_ntop_buf[0] = '\0'; 739ebace80cSflorian else { 740ebace80cSflorian if (inet_ntop(AF_INET, &imsg->siaddr, nextserver_ntop_buf, 741ebace80cSflorian sizeof(nextserver_ntop_buf)) == NULL) { 742ebace80cSflorian log_warn("%s: inet_ntop", __func__); 743ebace80cSflorian return; 744ebace80cSflorian } 745ebace80cSflorian } 74657419a7fSflorian len = snprintf(lease_file_buf, sizeof(lease_file_buf), "%s%s", 747131c304bSflorian _PATH_LEASE, if_name); 74857419a7fSflorian if ( len == -1 || (size_t) len >= sizeof(lease_file_buf)) { 74957419a7fSflorian log_warnx("%s: failed to encode lease path for %s", __func__, 75057419a7fSflorian if_name); 75157419a7fSflorian return; 75257419a7fSflorian } 75357419a7fSflorian 754ebace80cSflorian len = snprintf(lease_buf, sizeof(lease_buf), 755ebace80cSflorian "%s\n%s%s\n%s%s\n%s%s\n%s%s\n%s%s\n", 756ebace80cSflorian LEASE_VERSION, LEASE_IP_PREFIX, ip_ntop_buf, 757ebace80cSflorian LEASE_NEXTSERVER_PREFIX, nextserver_ntop_buf, LEASE_BOOTFILE_PREFIX, 758ebace80cSflorian imsg->file, LEASE_HOSTNAME_PREFIX, imsg->hostname, 759ebace80cSflorian LEASE_DOMAIN_PREFIX, imsg->domainname); 76057419a7fSflorian if ( len == -1 || (size_t) len >= sizeof(lease_buf)) { 76157419a7fSflorian log_warnx("%s: failed to encode lease for %s", __func__, 762ebace80cSflorian ip_ntop_buf); 76357419a7fSflorian return; 76457419a7fSflorian } 76557419a7fSflorian 76657419a7fSflorian if ((fd = mkstemp(tmpl)) == -1) { 76757419a7fSflorian log_warn("%s: mkstemp", __func__); 76857419a7fSflorian return; 76957419a7fSflorian } 77057419a7fSflorian 77157419a7fSflorian if (write(fd, lease_buf, len) < len) 77257419a7fSflorian goto err; 77357419a7fSflorian 77457419a7fSflorian if (fchmod(fd, 0644) == -1) 77557419a7fSflorian goto err; 77657419a7fSflorian 77757419a7fSflorian if (close(fd) == -1) 77857419a7fSflorian goto err; 77957419a7fSflorian fd = -1; 78057419a7fSflorian 78157419a7fSflorian if (rename(tmpl, lease_file_buf) == -1) 78257419a7fSflorian goto err; 78357419a7fSflorian return; 78457419a7fSflorian err: 78557419a7fSflorian log_warn("%s", __func__); 78657419a7fSflorian if (fd != -1) 78757419a7fSflorian close(fd); 78857419a7fSflorian unlink(tmpl); 78957419a7fSflorian } 79057419a7fSflorian 79157419a7fSflorian void 79257419a7fSflorian deconfigure_interface(struct imsg_configure_interface *imsg) 79357419a7fSflorian { 79457419a7fSflorian struct ifaliasreq ifaliasreq; 79557419a7fSflorian struct sockaddr_in *req_sin_addr; 79657419a7fSflorian 79757419a7fSflorian memset(&ifaliasreq, 0, sizeof(ifaliasreq)); 79857419a7fSflorian 799351dd593Sflorian if (imsg->routes_len > 0) 800351dd593Sflorian configure_routes(RTM_DELETE, imsg); 8017bd66a84Sflorian 80257419a7fSflorian if (if_indextoname(imsg->if_index, ifaliasreq.ifra_name) == NULL) { 80357419a7fSflorian log_warnx("%s: cannot find interface %d", __func__, 80457419a7fSflorian imsg->if_index); 80557419a7fSflorian return; 80657419a7fSflorian } 80757419a7fSflorian 808e998cdbeSflorian log_debug("%s %s", __func__, ifaliasreq.ifra_name); 809e998cdbeSflorian 81057419a7fSflorian req_sin_addr = (struct sockaddr_in *)&ifaliasreq.ifra_addr; 81157419a7fSflorian req_sin_addr->sin_family = AF_INET; 81257419a7fSflorian req_sin_addr->sin_len = sizeof(*req_sin_addr); 81357419a7fSflorian 81457419a7fSflorian req_sin_addr->sin_addr.s_addr = imsg->addr.s_addr; 81557419a7fSflorian if (ioctl(ioctl_sock, SIOCDIFADDR, &ifaliasreq) == -1) { 81657419a7fSflorian if (errno != EADDRNOTAVAIL) 81757419a7fSflorian log_warn("SIOCDIFADDR"); 81857419a7fSflorian } 81957419a7fSflorian } 82057419a7fSflorian 821351dd593Sflorian void 822351dd593Sflorian configure_routes(uint8_t rtm_type, struct imsg_configure_interface *imsg) 823351dd593Sflorian { 824351dd593Sflorian struct sockaddr_in dst, mask, gw, ifa; 825351dd593Sflorian in_addr_t addrnet, gwnet; 826351dd593Sflorian int i; 827351dd593Sflorian 828351dd593Sflorian memset(&ifa, 0, sizeof(ifa)); 829351dd593Sflorian ifa.sin_family = AF_INET; 830351dd593Sflorian ifa.sin_len = sizeof(ifa); 831351dd593Sflorian ifa.sin_addr.s_addr = imsg->addr.s_addr; 832351dd593Sflorian 833351dd593Sflorian memset(&dst, 0, sizeof(dst)); 834351dd593Sflorian dst.sin_family = AF_INET; 835351dd593Sflorian dst.sin_len = sizeof(dst); 836351dd593Sflorian 837351dd593Sflorian memset(&mask, 0, sizeof(mask)); 838351dd593Sflorian mask.sin_family = AF_INET; 839351dd593Sflorian mask.sin_len = sizeof(mask); 840351dd593Sflorian 841351dd593Sflorian memset(&gw, 0, sizeof(gw)); 842351dd593Sflorian gw.sin_family = AF_INET; 843351dd593Sflorian gw.sin_len = sizeof(gw); 844351dd593Sflorian 845351dd593Sflorian addrnet = imsg->addr.s_addr & imsg->mask.s_addr; 846351dd593Sflorian 847351dd593Sflorian for (i = 0; i < imsg->routes_len; i++) { 848351dd593Sflorian dst.sin_addr.s_addr = imsg->routes[i].dst.s_addr; 849351dd593Sflorian mask.sin_addr.s_addr = imsg->routes[i].mask.s_addr; 850351dd593Sflorian gw.sin_addr.s_addr = imsg->routes[i].gw.s_addr; 851351dd593Sflorian 852351dd593Sflorian if (gw.sin_addr.s_addr == INADDR_ANY) { 853351dd593Sflorian /* direct route */ 854351dd593Sflorian configure_route(rtm_type, imsg->if_index, 855351dd593Sflorian imsg->rdomain, &dst, &mask, &ifa, NULL, 856351dd593Sflorian RTF_CLONING); 857351dd593Sflorian } else if (mask.sin_addr.s_addr == INADDR_ANY) { 858351dd593Sflorian /* default route */ 859351dd593Sflorian gwnet = gw.sin_addr.s_addr & imsg->mask.s_addr; 860351dd593Sflorian if (addrnet != gwnet) { 861351dd593Sflorian /* 862351dd593Sflorian * The gateway for the default route is outside 863351dd593Sflorian * the configured prefix. Install a direct 864351dd593Sflorian * cloning route for the gateway to make the 865351dd593Sflorian * default route reachable. 866351dd593Sflorian */ 867351dd593Sflorian mask.sin_addr.s_addr = 0xffffffff; 868351dd593Sflorian configure_route(rtm_type, imsg->if_index, 869351dd593Sflorian imsg->rdomain, &gw, &mask, &ifa, NULL, 870351dd593Sflorian RTF_CLONING); 871351dd593Sflorian mask.sin_addr.s_addr = 872351dd593Sflorian imsg->routes[i].mask.s_addr; 873351dd593Sflorian } 874351dd593Sflorian 875351dd593Sflorian if (gw.sin_addr.s_addr == ifa.sin_addr.s_addr) { 876351dd593Sflorian /* directly connected default */ 877351dd593Sflorian configure_route(rtm_type, imsg->if_index, 878351dd593Sflorian imsg->rdomain, &dst, &mask, &gw, NULL, 0); 879351dd593Sflorian } else { 880351dd593Sflorian /* default route via gateway */ 881351dd593Sflorian configure_route(rtm_type, imsg->if_index, 882351dd593Sflorian imsg->rdomain, &dst, &mask, &gw, &ifa, 883351dd593Sflorian RTF_GATEWAY); 884351dd593Sflorian } 885351dd593Sflorian } else { 886351dd593Sflorian /* non-default via gateway */ 887351dd593Sflorian configure_route(rtm_type, imsg->if_index, imsg->rdomain, 888351dd593Sflorian &dst, &mask, &gw, NULL, RTF_GATEWAY); 889351dd593Sflorian } 890351dd593Sflorian } 891351dd593Sflorian } 892351dd593Sflorian 89357419a7fSflorian #define ROUNDUP(a) \ 89457419a7fSflorian (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a)) 89557419a7fSflorian void 896351dd593Sflorian configure_route(uint8_t rtm_type, uint32_t if_index, int rdomain, struct 897351dd593Sflorian sockaddr_in *dst, struct sockaddr_in *mask, struct sockaddr_in *gw, 898351dd593Sflorian struct sockaddr_in *ifa, int rtm_flags) 89957419a7fSflorian { 90057419a7fSflorian struct rt_msghdr rtm; 90157419a7fSflorian struct sockaddr_rtlabel rl; 9020be69ef6Sflorian struct iovec iov[12]; 90357419a7fSflorian long pad = 0; 90457419a7fSflorian int iovcnt = 0, padlen; 90557419a7fSflorian 90657419a7fSflorian memset(&rtm, 0, sizeof(rtm)); 90757419a7fSflorian 90857419a7fSflorian rtm.rtm_version = RTM_VERSION; 90957419a7fSflorian rtm.rtm_type = rtm_type; 91057419a7fSflorian rtm.rtm_msglen = sizeof(rtm); 911351dd593Sflorian rtm.rtm_index = if_index; 912351dd593Sflorian rtm.rtm_tableid = rdomain; 91357419a7fSflorian rtm.rtm_seq = ++rtm_seq; 91457419a7fSflorian rtm.rtm_priority = RTP_NONE; 915351dd593Sflorian rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_LABEL; 916351dd593Sflorian rtm.rtm_flags = RTF_UP | RTF_STATIC | RTF_MPATH | rtm_flags; 917351dd593Sflorian 918351dd593Sflorian if (ifa) 919351dd593Sflorian rtm.rtm_addrs |= RTA_IFA; 92057419a7fSflorian 92157419a7fSflorian iov[iovcnt].iov_base = &rtm; 92257419a7fSflorian iov[iovcnt++].iov_len = sizeof(rtm); 92357419a7fSflorian 924351dd593Sflorian iov[iovcnt].iov_base = dst; 925351dd593Sflorian iov[iovcnt++].iov_len = dst->sin_len; 926351dd593Sflorian rtm.rtm_msglen += dst->sin_len; 927351dd593Sflorian padlen = ROUNDUP(dst->sin_len) - dst->sin_len; 92857419a7fSflorian if (padlen > 0) { 92957419a7fSflorian iov[iovcnt].iov_base = &pad; 93057419a7fSflorian iov[iovcnt++].iov_len = padlen; 93157419a7fSflorian rtm.rtm_msglen += padlen; 93257419a7fSflorian } 93357419a7fSflorian 934351dd593Sflorian iov[iovcnt].iov_base = gw; 935351dd593Sflorian iov[iovcnt++].iov_len = gw->sin_len; 936351dd593Sflorian rtm.rtm_msglen += gw->sin_len; 937351dd593Sflorian padlen = ROUNDUP(gw->sin_len) - gw->sin_len; 93857419a7fSflorian if (padlen > 0) { 93957419a7fSflorian iov[iovcnt].iov_base = &pad; 94057419a7fSflorian iov[iovcnt++].iov_len = padlen; 94157419a7fSflorian rtm.rtm_msglen += padlen; 94257419a7fSflorian } 94357419a7fSflorian 944351dd593Sflorian iov[iovcnt].iov_base = mask; 945351dd593Sflorian iov[iovcnt++].iov_len = mask->sin_len; 946351dd593Sflorian rtm.rtm_msglen += mask->sin_len; 947351dd593Sflorian padlen = ROUNDUP(mask->sin_len) - mask->sin_len; 94857419a7fSflorian if (padlen > 0) { 94957419a7fSflorian iov[iovcnt].iov_base = &pad; 95057419a7fSflorian iov[iovcnt++].iov_len = padlen; 95157419a7fSflorian rtm.rtm_msglen += padlen; 95257419a7fSflorian } 95357419a7fSflorian 954351dd593Sflorian if (ifa) { 955351dd593Sflorian iov[iovcnt].iov_base = ifa; 956351dd593Sflorian iov[iovcnt++].iov_len = ifa->sin_len; 957351dd593Sflorian rtm.rtm_msglen += ifa->sin_len; 958351dd593Sflorian padlen = ROUNDUP(ifa->sin_len) - ifa->sin_len; 9590be69ef6Sflorian if (padlen > 0) { 9600be69ef6Sflorian iov[iovcnt].iov_base = &pad; 9610be69ef6Sflorian iov[iovcnt++].iov_len = padlen; 9620be69ef6Sflorian rtm.rtm_msglen += padlen; 9630be69ef6Sflorian } 964351dd593Sflorian } 9650be69ef6Sflorian 96657419a7fSflorian memset(&rl, 0, sizeof(rl)); 96757419a7fSflorian rl.sr_len = sizeof(rl); 96857419a7fSflorian rl.sr_family = AF_UNSPEC; 96957419a7fSflorian (void)snprintf(rl.sr_label, sizeof(rl.sr_label), "%s", 97057419a7fSflorian DHCPLEASED_RTA_LABEL); 97157419a7fSflorian iov[iovcnt].iov_base = &rl; 97257419a7fSflorian iov[iovcnt++].iov_len = sizeof(rl); 97357419a7fSflorian rtm.rtm_msglen += sizeof(rl); 97457419a7fSflorian padlen = ROUNDUP(sizeof(rl)) - sizeof(rl); 97557419a7fSflorian if (padlen > 0) { 97657419a7fSflorian iov[iovcnt].iov_base = &pad; 97757419a7fSflorian iov[iovcnt++].iov_len = padlen; 97857419a7fSflorian rtm.rtm_msglen += padlen; 97957419a7fSflorian } 98057419a7fSflorian 98157419a7fSflorian if (writev(routesock, iov, iovcnt) == -1) 98257419a7fSflorian log_warn("failed to send route message"); 98357419a7fSflorian } 98457419a7fSflorian 98557419a7fSflorian #ifndef SMALL 98657419a7fSflorian const char* 98757419a7fSflorian sin_to_str(struct sockaddr_in *sin) 98857419a7fSflorian { 98957419a7fSflorian static char hbuf[NI_MAXHOST]; 99057419a7fSflorian int error; 99157419a7fSflorian 99257419a7fSflorian error = getnameinfo((struct sockaddr *)sin, sin->sin_len, hbuf, 99357419a7fSflorian sizeof(hbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); 99457419a7fSflorian if (error) { 99557419a7fSflorian log_warnx("%s", gai_strerror(error)); 99657419a7fSflorian strlcpy(hbuf, "unknown", sizeof(hbuf)); 99757419a7fSflorian } 99857419a7fSflorian return hbuf; 99957419a7fSflorian } 100057419a7fSflorian #endif /* SMALL */ 100157419a7fSflorian 100257419a7fSflorian void 100357419a7fSflorian open_bpfsock(uint32_t if_index) 100457419a7fSflorian { 100557419a7fSflorian int bpfsock; 100657419a7fSflorian char ifname[IF_NAMESIZE]; 100757419a7fSflorian 1008e998cdbeSflorian if (if_indextoname(if_index, ifname) == NULL) { 100957419a7fSflorian log_warnx("%s: cannot find interface %d", __func__, if_index); 101057419a7fSflorian return; 101157419a7fSflorian } 101257419a7fSflorian 101357419a7fSflorian if ((bpfsock = get_bpf_sock(ifname)) == -1) 101457419a7fSflorian return; 101557419a7fSflorian 101657419a7fSflorian main_imsg_compose_frontend(IMSG_BPFSOCK, bpfsock, &if_index, 101757419a7fSflorian sizeof(if_index)); 101857419a7fSflorian } 101957419a7fSflorian 102057419a7fSflorian void 102157419a7fSflorian propose_rdns(struct imsg_propose_rdns *rdns) 102257419a7fSflorian { 102357419a7fSflorian struct rt_msghdr rtm; 102457419a7fSflorian struct sockaddr_rtdns rtdns; 102557419a7fSflorian struct iovec iov[3]; 102657419a7fSflorian long pad = 0; 102757419a7fSflorian int iovcnt = 0, padlen; 102857419a7fSflorian 102957419a7fSflorian memset(&rtm, 0, sizeof(rtm)); 103057419a7fSflorian 103157419a7fSflorian rtm.rtm_version = RTM_VERSION; 103257419a7fSflorian rtm.rtm_type = RTM_PROPOSAL; 103357419a7fSflorian rtm.rtm_msglen = sizeof(rtm); 103457419a7fSflorian rtm.rtm_tableid = rdns->rdomain; 103557419a7fSflorian rtm.rtm_index = rdns->if_index; 103657419a7fSflorian rtm.rtm_seq = ++rtm_seq; 103757419a7fSflorian rtm.rtm_priority = RTP_PROPOSAL_DHCLIENT; 103857419a7fSflorian rtm.rtm_addrs = RTA_DNS; 103957419a7fSflorian rtm.rtm_flags = RTF_UP; 104057419a7fSflorian 104157419a7fSflorian iov[iovcnt].iov_base = &rtm; 104257419a7fSflorian iov[iovcnt++].iov_len = sizeof(rtm); 104357419a7fSflorian 104457419a7fSflorian memset(&rtdns, 0, sizeof(rtdns)); 104557419a7fSflorian rtdns.sr_family = AF_INET; 104657419a7fSflorian rtdns.sr_len = 2 + rdns->rdns_count * sizeof(struct in_addr); 104757419a7fSflorian memcpy(rtdns.sr_dns, rdns->rdns, sizeof(rtdns.sr_dns)); 104857419a7fSflorian 104957419a7fSflorian iov[iovcnt].iov_base = &rtdns; 105057419a7fSflorian iov[iovcnt++].iov_len = sizeof(rtdns); 105157419a7fSflorian rtm.rtm_msglen += sizeof(rtdns); 105257419a7fSflorian padlen = ROUNDUP(sizeof(rtdns)) - sizeof(rtdns); 105357419a7fSflorian if (padlen > 0) { 105457419a7fSflorian iov[iovcnt].iov_base = &pad; 105557419a7fSflorian iov[iovcnt++].iov_len = padlen; 105657419a7fSflorian rtm.rtm_msglen += padlen; 105757419a7fSflorian } 105857419a7fSflorian 105957419a7fSflorian if (writev(routesock, iov, iovcnt) == -1) 106057419a7fSflorian log_warn("failed to send route message"); 106157419a7fSflorian } 106257419a7fSflorian 10636e93e3e9Sflorian void 10646e93e3e9Sflorian read_lease_file(struct imsg_ifinfo *imsg_ifinfo) 106557419a7fSflorian { 10666e93e3e9Sflorian int len, fd; 106757419a7fSflorian char if_name[IF_NAMESIZE]; 1068131c304bSflorian char lease_file_buf[sizeof(_PATH_LEASE) + IF_NAMESIZE]; 106957419a7fSflorian 1070ae968ffdSflorian if (no_lease_files) 1071ae968ffdSflorian return; 1072ae968ffdSflorian 10736e93e3e9Sflorian memset(imsg_ifinfo->lease, 0, sizeof(imsg_ifinfo->lease)); 10746e93e3e9Sflorian 1075e998cdbeSflorian if (if_indextoname(imsg_ifinfo->if_index, if_name) == NULL) { 10766e93e3e9Sflorian log_warnx("%s: cannot find interface %d", __func__, 10776e93e3e9Sflorian imsg_ifinfo->if_index); 10786e93e3e9Sflorian return; 107957419a7fSflorian } 108057419a7fSflorian 108157419a7fSflorian len = snprintf(lease_file_buf, sizeof(lease_file_buf), "%s%s", 1082131c304bSflorian _PATH_LEASE, if_name); 108357419a7fSflorian if ( len == -1 || (size_t) len >= sizeof(lease_file_buf)) { 108457419a7fSflorian log_warnx("%s: failed to encode lease path for %s", __func__, 108557419a7fSflorian if_name); 10866e93e3e9Sflorian return; 108757419a7fSflorian } 108857419a7fSflorian 10896e93e3e9Sflorian if ((fd = open(lease_file_buf, O_RDONLY)) == -1) 10906e93e3e9Sflorian return; 10916e93e3e9Sflorian 10926e93e3e9Sflorian /* no need for error handling, we'll just do a DHCP discover */ 10936e93e3e9Sflorian read(fd, imsg_ifinfo->lease, sizeof(imsg_ifinfo->lease) - 1); 10946e93e3e9Sflorian close(fd); 109557419a7fSflorian } 1096