1*90c58abaSflorian /* $OpenBSD: frontend.c,v 1.24 2021/11/20 17:54:40 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/syslog.h> 2657419a7fSflorian #include <sys/uio.h> 2757419a7fSflorian 2857419a7fSflorian #include <net/bpf.h> 2957419a7fSflorian #include <net/if.h> 3057419a7fSflorian #include <net/if_dl.h> 3157419a7fSflorian #include <net/if_types.h> 3257419a7fSflorian #include <net/route.h> 3357419a7fSflorian 3498b9f859Sclaudio #include <netinet/in.h> 3598b9f859Sclaudio #include <netinet/if_ether.h> 3657419a7fSflorian #include <netinet/ip.h> 3757419a7fSflorian #include <netinet/udp.h> 3857419a7fSflorian 3957419a7fSflorian #include <arpa/inet.h> 4057419a7fSflorian 4157419a7fSflorian #include <errno.h> 4257419a7fSflorian #include <event.h> 4357419a7fSflorian #include <ifaddrs.h> 4457419a7fSflorian #include <imsg.h> 4557419a7fSflorian #include <pwd.h> 4657419a7fSflorian #include <signal.h> 4757419a7fSflorian #include <stdio.h> 4857419a7fSflorian #include <stdlib.h> 4957419a7fSflorian #include <string.h> 5057419a7fSflorian #include <unistd.h> 5157419a7fSflorian 5257419a7fSflorian #include "bpf.h" 5357419a7fSflorian #include "log.h" 5457419a7fSflorian #include "dhcpleased.h" 5557419a7fSflorian #include "frontend.h" 5657419a7fSflorian #include "control.h" 5757419a7fSflorian #include "checksum.h" 5857419a7fSflorian 5957419a7fSflorian #define ROUTE_SOCKET_BUF_SIZE 16384 602d2edb0eSflorian #define BOOTP_MIN_LEN 300 /* fixed bootp packet adds up to 300 */ 6157419a7fSflorian 6257419a7fSflorian struct bpf_ev { 6357419a7fSflorian struct event ev; 6457419a7fSflorian uint8_t buf[BPFLEN]; 6557419a7fSflorian }; 6657419a7fSflorian 6757419a7fSflorian struct iface { 6857419a7fSflorian LIST_ENTRY(iface) entries; 6957419a7fSflorian struct bpf_ev bpfev; 701bbf741cSflorian struct imsg_ifinfo ifinfo; 7157419a7fSflorian int send_discover; 7257419a7fSflorian uint32_t xid; 7357419a7fSflorian struct in_addr requested_ip; 7457419a7fSflorian struct in_addr server_identifier; 7557419a7fSflorian struct in_addr dhcp_server; 7657419a7fSflorian int udpsock; 7757419a7fSflorian }; 7857419a7fSflorian 7957419a7fSflorian __dead void frontend_shutdown(void); 8057419a7fSflorian void frontend_sig_handler(int, short, void *); 8148e1b614Sflorian void update_iface(struct if_msghdr *, struct sockaddr_dl *); 8257419a7fSflorian void frontend_startup(void); 831bbf741cSflorian void init_ifaces(void); 8457419a7fSflorian void route_receive(int, short, void *); 8557419a7fSflorian void handle_route_message(struct rt_msghdr *, struct sockaddr **); 8657419a7fSflorian void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 8757419a7fSflorian void bpf_receive(int, short, void *); 8857419a7fSflorian int get_flags(char *); 8957419a7fSflorian int get_xflags(char *); 9057419a7fSflorian struct iface *get_iface_by_id(uint32_t); 9157419a7fSflorian void remove_iface(uint32_t); 9257419a7fSflorian void set_bpfsock(int, uint32_t); 93a41cc082Sflorian ssize_t build_packet(uint8_t, char *, uint32_t, struct ether_addr *, 94a41cc082Sflorian struct in_addr *, struct in_addr *); 9557419a7fSflorian void send_discover(struct iface *); 9657419a7fSflorian void send_request(struct iface *); 9757419a7fSflorian void bpf_send_packet(struct iface *, uint8_t *, ssize_t); 98c645071eSflorian int udp_send_packet(struct iface *, uint8_t *, ssize_t); 994eb9756fSflorian #ifndef SMALL 100580befd2Sflorian int iface_conf_cmp(struct iface_conf *, struct iface_conf *); 1014eb9756fSflorian #endif /* SMALL */ 10257419a7fSflorian 10357419a7fSflorian LIST_HEAD(, iface) interfaces; 104a41cc082Sflorian struct dhcpleased_conf *frontend_conf; 10557419a7fSflorian static struct imsgev *iev_main; 10657419a7fSflorian static struct imsgev *iev_engine; 10757419a7fSflorian struct event ev_route; 10857419a7fSflorian int ioctlsock; 10957419a7fSflorian 11057419a7fSflorian uint8_t dhcp_packet[1500]; 11157419a7fSflorian 11257419a7fSflorian void 11357419a7fSflorian frontend_sig_handler(int sig, short event, void *bula) 11457419a7fSflorian { 11557419a7fSflorian /* 11657419a7fSflorian * Normal signal handler rules don't apply because libevent 11757419a7fSflorian * decouples for us. 11857419a7fSflorian */ 11957419a7fSflorian 12057419a7fSflorian switch (sig) { 12157419a7fSflorian case SIGINT: 12257419a7fSflorian case SIGTERM: 12357419a7fSflorian frontend_shutdown(); 12457419a7fSflorian default: 12557419a7fSflorian fatalx("unexpected signal"); 12657419a7fSflorian } 12757419a7fSflorian } 12857419a7fSflorian 12957419a7fSflorian void 13057419a7fSflorian frontend(int debug, int verbose) 13157419a7fSflorian { 13257419a7fSflorian struct event ev_sigint, ev_sigterm; 13357419a7fSflorian struct passwd *pw; 13457419a7fSflorian 135a41cc082Sflorian #ifndef SMALL 136a41cc082Sflorian frontend_conf = config_new_empty(); 137a41cc082Sflorian #endif /* SMALL */ 138a41cc082Sflorian 13957419a7fSflorian log_init(debug, LOG_DAEMON); 14057419a7fSflorian log_setverbose(verbose); 14157419a7fSflorian 14257419a7fSflorian if ((pw = getpwnam(DHCPLEASED_USER)) == NULL) 14357419a7fSflorian fatal("getpwnam"); 14457419a7fSflorian 14557419a7fSflorian if (chdir("/") == -1) 14657419a7fSflorian fatal("chdir(\"/\")"); 14757419a7fSflorian 148c7d84514Sflorian if (unveil("/", "") == -1) 149bc5a8259Sbeck fatal("unveil /"); 150c7d84514Sflorian if (unveil(NULL, NULL) == -1) 151bc5a8259Sbeck fatal("unveil"); 152c7d84514Sflorian 15357419a7fSflorian setproctitle("%s", "frontend"); 15457419a7fSflorian log_procinit("frontend"); 15557419a7fSflorian 15657419a7fSflorian if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) 15757419a7fSflorian fatal("socket"); 15857419a7fSflorian 15957419a7fSflorian if (setgroups(1, &pw->pw_gid) || 16057419a7fSflorian setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 16157419a7fSflorian setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 16257419a7fSflorian fatal("can't drop privileges"); 16357419a7fSflorian 16457419a7fSflorian if (pledge("stdio unix recvfd route", NULL) == -1) 16557419a7fSflorian fatal("pledge"); 16657419a7fSflorian event_init(); 16757419a7fSflorian 16857419a7fSflorian /* Setup signal handler. */ 16957419a7fSflorian signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL); 17057419a7fSflorian signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL); 17157419a7fSflorian signal_add(&ev_sigint, NULL); 17257419a7fSflorian signal_add(&ev_sigterm, NULL); 17357419a7fSflorian signal(SIGPIPE, SIG_IGN); 17457419a7fSflorian signal(SIGHUP, SIG_IGN); 17557419a7fSflorian 17657419a7fSflorian /* Setup pipe and event handler to the parent process. */ 17757419a7fSflorian if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) 17857419a7fSflorian fatal(NULL); 17957419a7fSflorian imsg_init(&iev_main->ibuf, 3); 18057419a7fSflorian iev_main->handler = frontend_dispatch_main; 18157419a7fSflorian iev_main->events = EV_READ; 18257419a7fSflorian event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, 18357419a7fSflorian iev_main->handler, iev_main); 18457419a7fSflorian event_add(&iev_main->ev, NULL); 18557419a7fSflorian 18657419a7fSflorian LIST_INIT(&interfaces); 18757419a7fSflorian event_dispatch(); 18857419a7fSflorian 18957419a7fSflorian frontend_shutdown(); 19057419a7fSflorian } 19157419a7fSflorian 19257419a7fSflorian __dead void 19357419a7fSflorian frontend_shutdown(void) 19457419a7fSflorian { 19557419a7fSflorian /* Close pipes. */ 19657419a7fSflorian msgbuf_write(&iev_engine->ibuf.w); 19757419a7fSflorian msgbuf_clear(&iev_engine->ibuf.w); 19857419a7fSflorian close(iev_engine->ibuf.fd); 19957419a7fSflorian msgbuf_write(&iev_main->ibuf.w); 20057419a7fSflorian msgbuf_clear(&iev_main->ibuf.w); 20157419a7fSflorian close(iev_main->ibuf.fd); 20257419a7fSflorian 203a41cc082Sflorian #ifndef SMALL 204a41cc082Sflorian config_clear(frontend_conf); 205a41cc082Sflorian #endif /* SMALL */ 206a41cc082Sflorian 20757419a7fSflorian free(iev_engine); 20857419a7fSflorian free(iev_main); 20957419a7fSflorian 21057419a7fSflorian log_info("frontend exiting"); 21157419a7fSflorian exit(0); 21257419a7fSflorian } 21357419a7fSflorian 21457419a7fSflorian int 21557419a7fSflorian frontend_imsg_compose_main(int type, pid_t pid, void *data, 21657419a7fSflorian uint16_t datalen) 21757419a7fSflorian { 21857419a7fSflorian return (imsg_compose_event(iev_main, type, 0, pid, -1, data, 21957419a7fSflorian datalen)); 22057419a7fSflorian } 22157419a7fSflorian 22257419a7fSflorian int 22357419a7fSflorian frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid, 22457419a7fSflorian void *data, uint16_t datalen) 22557419a7fSflorian { 22657419a7fSflorian return (imsg_compose_event(iev_engine, type, peerid, pid, -1, 22757419a7fSflorian data, datalen)); 22857419a7fSflorian } 22957419a7fSflorian 23057419a7fSflorian void 23157419a7fSflorian frontend_dispatch_main(int fd, short event, void *bula) 23257419a7fSflorian { 233a41cc082Sflorian static struct dhcpleased_conf *nconf; 234a41cc082Sflorian static struct iface_conf *iface_conf; 23557419a7fSflorian struct imsg imsg; 23657419a7fSflorian struct imsgev *iev = bula; 23757419a7fSflorian struct imsgbuf *ibuf = &iev->ibuf; 23857419a7fSflorian struct iface *iface; 23957419a7fSflorian ssize_t n; 24057419a7fSflorian int shut = 0, bpfsock, if_index, udpsock; 24157419a7fSflorian 24257419a7fSflorian if (event & EV_READ) { 24357419a7fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 24457419a7fSflorian fatal("imsg_read error"); 24557419a7fSflorian if (n == 0) /* Connection closed. */ 24657419a7fSflorian shut = 1; 24757419a7fSflorian } 24857419a7fSflorian if (event & EV_WRITE) { 24957419a7fSflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 25057419a7fSflorian fatal("msgbuf_write"); 25157419a7fSflorian if (n == 0) /* Connection closed. */ 25257419a7fSflorian shut = 1; 25357419a7fSflorian } 25457419a7fSflorian 25557419a7fSflorian for (;;) { 25657419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 25757419a7fSflorian fatal("%s: imsg_get error", __func__); 25857419a7fSflorian if (n == 0) /* No more messages. */ 25957419a7fSflorian break; 26057419a7fSflorian 26157419a7fSflorian switch (imsg.hdr.type) { 26257419a7fSflorian case IMSG_SOCKET_IPC: 26357419a7fSflorian /* 26457419a7fSflorian * Setup pipe and event handler to the engine 26557419a7fSflorian * process. 26657419a7fSflorian */ 26757419a7fSflorian if (iev_engine) 26857419a7fSflorian fatalx("%s: received unexpected imsg fd " 26957419a7fSflorian "to frontend", __func__); 27057419a7fSflorian 27157419a7fSflorian if ((fd = imsg.fd) == -1) 27257419a7fSflorian fatalx("%s: expected to receive imsg fd to " 27357419a7fSflorian "frontend but didn't receive any", 27457419a7fSflorian __func__); 27557419a7fSflorian 27657419a7fSflorian iev_engine = malloc(sizeof(struct imsgev)); 27757419a7fSflorian if (iev_engine == NULL) 27857419a7fSflorian fatal(NULL); 27957419a7fSflorian 28057419a7fSflorian imsg_init(&iev_engine->ibuf, fd); 28157419a7fSflorian iev_engine->handler = frontend_dispatch_engine; 28257419a7fSflorian iev_engine->events = EV_READ; 28357419a7fSflorian 28457419a7fSflorian event_set(&iev_engine->ev, iev_engine->ibuf.fd, 28557419a7fSflorian iev_engine->events, iev_engine->handler, iev_engine); 28657419a7fSflorian event_add(&iev_engine->ev, NULL); 28757419a7fSflorian break; 28857419a7fSflorian case IMSG_BPFSOCK: 28957419a7fSflorian if ((bpfsock = imsg.fd) == -1) 29057419a7fSflorian fatalx("%s: expected to receive imsg " 29157419a7fSflorian "bpf fd but didn't receive any", 29257419a7fSflorian __func__); 29357419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 29457419a7fSflorian fatalx("%s: IMSG_BPFSOCK wrong length: " 29557419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 29657419a7fSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 29757419a7fSflorian set_bpfsock(bpfsock, if_index); 29857419a7fSflorian break; 29957419a7fSflorian case IMSG_UDPSOCK: 30057419a7fSflorian if ((udpsock = imsg.fd) == -1) 30157419a7fSflorian fatalx("%s: expected to receive imsg " 30257419a7fSflorian "udpsocket fd but didn't receive any", 30357419a7fSflorian __func__); 30457419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 30557419a7fSflorian fatalx("%s: IMSG_UDPSOCK wrong length: " 30657419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 30757419a7fSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 30857419a7fSflorian if ((iface = get_iface_by_id(if_index)) == NULL) { 30957419a7fSflorian close(fd); 31057419a7fSflorian break; 31157419a7fSflorian } 31257419a7fSflorian if (iface->udpsock != -1) 31357419a7fSflorian fatalx("%s: received unexpected udpsocket", 31457419a7fSflorian __func__); 31557419a7fSflorian iface->udpsock = udpsock; 31657419a7fSflorian break; 31757419a7fSflorian case IMSG_CLOSE_UDPSOCK: 31857419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 31957419a7fSflorian fatalx("%s: IMSG_UDPSOCK wrong length: " 32057419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 32157419a7fSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 32257419a7fSflorian if ((iface = get_iface_by_id(if_index)) != NULL && 32357419a7fSflorian iface->udpsock != -1) { 32457419a7fSflorian close(iface->udpsock); 32557419a7fSflorian iface->udpsock = -1; 32657419a7fSflorian } 32757419a7fSflorian break; 32857419a7fSflorian case IMSG_ROUTESOCK: 32957419a7fSflorian if ((fd = imsg.fd) == -1) 33057419a7fSflorian fatalx("%s: expected to receive imsg " 33157419a7fSflorian "routesocket fd but didn't receive any", 33257419a7fSflorian __func__); 33357419a7fSflorian event_set(&ev_route, fd, EV_READ | EV_PERSIST, 33457419a7fSflorian route_receive, NULL); 33557419a7fSflorian break; 33657419a7fSflorian case IMSG_STARTUP: 33757419a7fSflorian frontend_startup(); 33857419a7fSflorian break; 33957419a7fSflorian #ifndef SMALL 340a41cc082Sflorian case IMSG_RECONF_CONF: 341a41cc082Sflorian if (nconf != NULL) 342a41cc082Sflorian fatalx("%s: IMSG_RECONF_CONF already in " 343a41cc082Sflorian "progress", __func__); 344a41cc082Sflorian if ((nconf = malloc(sizeof(struct dhcpleased_conf))) == 345a41cc082Sflorian NULL) 346a41cc082Sflorian fatal(NULL); 347a41cc082Sflorian SIMPLEQ_INIT(&nconf->iface_list); 348a41cc082Sflorian break; 349a41cc082Sflorian case IMSG_RECONF_IFACE: 350a41cc082Sflorian if (IMSG_DATA_SIZE(imsg) != sizeof(struct 351a41cc082Sflorian iface_conf)) 352a41cc082Sflorian fatalx("%s: IMSG_RECONF_IFACE wrong length: " 353a41cc082Sflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 354a41cc082Sflorian if ((iface_conf = malloc(sizeof(struct iface_conf))) 355a41cc082Sflorian == NULL) 356a41cc082Sflorian fatal(NULL); 357a41cc082Sflorian memcpy(iface_conf, imsg.data, sizeof(struct 358a41cc082Sflorian iface_conf)); 359a41cc082Sflorian iface_conf->vc_id = NULL; 360a41cc082Sflorian iface_conf->vc_id_len = 0; 361a41cc082Sflorian iface_conf->c_id = NULL; 362a41cc082Sflorian iface_conf->c_id_len = 0; 363a41cc082Sflorian SIMPLEQ_INSERT_TAIL(&nconf->iface_list, 364a41cc082Sflorian iface_conf, entry); 365a41cc082Sflorian break; 366a41cc082Sflorian case IMSG_RECONF_VC_ID: 367a41cc082Sflorian if (iface_conf == NULL) 368a41cc082Sflorian fatal("IMSG_RECONF_VC_ID without " 369a41cc082Sflorian "IMSG_RECONF_IFACE"); 370a41cc082Sflorian if (IMSG_DATA_SIZE(imsg) > 255 + 2) 371a41cc082Sflorian fatalx("%s: IMSG_RECONF_VC_ID wrong length: " 372a41cc082Sflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 373a41cc082Sflorian if ((iface_conf->vc_id = malloc(IMSG_DATA_SIZE(imsg))) 374a41cc082Sflorian == NULL) 375a41cc082Sflorian fatal(NULL); 376a41cc082Sflorian memcpy(iface_conf->vc_id, imsg.data, 377a41cc082Sflorian IMSG_DATA_SIZE(imsg)); 378a41cc082Sflorian iface_conf->vc_id_len = IMSG_DATA_SIZE(imsg); 379a41cc082Sflorian break; 380a41cc082Sflorian case IMSG_RECONF_C_ID: 381a41cc082Sflorian if (iface_conf == NULL) 382a41cc082Sflorian fatal("IMSG_RECONF_C_ID without " 383a41cc082Sflorian "IMSG_RECONF_IFACE"); 384a41cc082Sflorian if (IMSG_DATA_SIZE(imsg) > 255 + 2) 385a41cc082Sflorian fatalx("%s: IMSG_RECONF_C_ID wrong length: " 386a41cc082Sflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 387a41cc082Sflorian if ((iface_conf->c_id = malloc(IMSG_DATA_SIZE(imsg))) 388a41cc082Sflorian == NULL) 389a41cc082Sflorian fatal(NULL); 390a41cc082Sflorian memcpy(iface_conf->c_id, imsg.data, 391a41cc082Sflorian IMSG_DATA_SIZE(imsg)); 392a41cc082Sflorian iface_conf->c_id_len = IMSG_DATA_SIZE(imsg); 393a41cc082Sflorian break; 394580befd2Sflorian case IMSG_RECONF_END: { 395580befd2Sflorian int i; 396580befd2Sflorian int *ifaces; 397580befd2Sflorian char ifnamebuf[IF_NAMESIZE], *if_name; 398580befd2Sflorian 399a41cc082Sflorian if (nconf == NULL) 400a41cc082Sflorian fatalx("%s: IMSG_RECONF_END without " 401a41cc082Sflorian "IMSG_RECONF_CONF", __func__); 402580befd2Sflorian 403580befd2Sflorian ifaces = changed_ifaces(frontend_conf, nconf); 404a41cc082Sflorian merge_config(frontend_conf, nconf); 405a41cc082Sflorian nconf = NULL; 406580befd2Sflorian for (i = 0; ifaces[i] != 0; i++) { 407580befd2Sflorian if_index = ifaces[i]; 408580befd2Sflorian if_name = if_indextoname(if_index, ifnamebuf); 409580befd2Sflorian log_debug("changed iface: %s[%d]", if_name != 410580befd2Sflorian NULL ? if_name : "<unknown>", if_index); 411580befd2Sflorian frontend_imsg_compose_engine( 412580befd2Sflorian IMSG_REQUEST_REBOOT, 0, 0, &if_index, 413580befd2Sflorian sizeof(if_index)); 414580befd2Sflorian } 415580befd2Sflorian free(ifaces); 416a41cc082Sflorian break; 417580befd2Sflorian } 41857419a7fSflorian case IMSG_CONTROLFD: 41957419a7fSflorian if ((fd = imsg.fd) == -1) 42057419a7fSflorian fatalx("%s: expected to receive imsg " 42157419a7fSflorian "control fd but didn't receive any", 42257419a7fSflorian __func__); 42357419a7fSflorian /* Listen on control socket. */ 42457419a7fSflorian control_listen(fd); 42557419a7fSflorian break; 42657419a7fSflorian case IMSG_CTL_END: 42757419a7fSflorian control_imsg_relay(&imsg); 42857419a7fSflorian break; 42957419a7fSflorian #endif /* SMALL */ 43057419a7fSflorian default: 43157419a7fSflorian log_debug("%s: error handling imsg %d", __func__, 43257419a7fSflorian imsg.hdr.type); 43357419a7fSflorian break; 43457419a7fSflorian } 43557419a7fSflorian imsg_free(&imsg); 43657419a7fSflorian } 43757419a7fSflorian if (!shut) 43857419a7fSflorian imsg_event_add(iev); 43957419a7fSflorian else { 44057419a7fSflorian /* This pipe is dead. Remove its event handler. */ 44157419a7fSflorian event_del(&iev->ev); 44257419a7fSflorian event_loopexit(NULL); 44357419a7fSflorian } 44457419a7fSflorian } 44557419a7fSflorian 44657419a7fSflorian void 44757419a7fSflorian frontend_dispatch_engine(int fd, short event, void *bula) 44857419a7fSflorian { 44957419a7fSflorian struct imsgev *iev = bula; 45057419a7fSflorian struct imsgbuf *ibuf = &iev->ibuf; 45157419a7fSflorian struct imsg imsg; 45257419a7fSflorian struct iface *iface; 45357419a7fSflorian ssize_t n; 45457419a7fSflorian int shut = 0; 45557419a7fSflorian 45657419a7fSflorian if (event & EV_READ) { 45757419a7fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 45857419a7fSflorian fatal("imsg_read error"); 45957419a7fSflorian if (n == 0) /* Connection closed. */ 46057419a7fSflorian shut = 1; 46157419a7fSflorian } 46257419a7fSflorian if (event & EV_WRITE) { 46357419a7fSflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 46457419a7fSflorian fatal("msgbuf_write"); 46557419a7fSflorian if (n == 0) /* Connection closed. */ 46657419a7fSflorian shut = 1; 46757419a7fSflorian } 46857419a7fSflorian 46957419a7fSflorian for (;;) { 47057419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 47157419a7fSflorian fatal("%s: imsg_get error", __func__); 47257419a7fSflorian if (n == 0) /* No more messages. */ 47357419a7fSflorian break; 47457419a7fSflorian 47557419a7fSflorian switch (imsg.hdr.type) { 47657419a7fSflorian #ifndef SMALL 47757419a7fSflorian case IMSG_CTL_END: 47857419a7fSflorian case IMSG_CTL_SHOW_INTERFACE_INFO: 47957419a7fSflorian control_imsg_relay(&imsg); 48057419a7fSflorian break; 48157419a7fSflorian #endif /* SMALL */ 48257419a7fSflorian case IMSG_SEND_DISCOVER: { 48357419a7fSflorian struct imsg_req_discover imsg_req_discover; 48457419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_discover)) 48557419a7fSflorian fatalx("%s: IMSG_SEND_DISCOVER wrong " 48657419a7fSflorian "length: %lu", __func__, 48757419a7fSflorian IMSG_DATA_SIZE(imsg)); 48857419a7fSflorian memcpy(&imsg_req_discover, imsg.data, 48957419a7fSflorian sizeof(imsg_req_discover)); 49057419a7fSflorian iface = get_iface_by_id(imsg_req_discover.if_index); 49157419a7fSflorian if (iface != NULL) { 49257419a7fSflorian iface->xid = imsg_req_discover.xid; 49357419a7fSflorian send_discover(iface); 49457419a7fSflorian } 49557419a7fSflorian break; 49657419a7fSflorian } 49757419a7fSflorian case IMSG_SEND_REQUEST: { 49857419a7fSflorian struct imsg_req_request imsg_req_request; 49957419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_request)) 50057419a7fSflorian fatalx("%s: IMSG_SEND_REQUEST wrong " 50157419a7fSflorian "length: %lu", __func__, 50257419a7fSflorian IMSG_DATA_SIZE(imsg)); 50357419a7fSflorian memcpy(&imsg_req_request, imsg.data, 50457419a7fSflorian sizeof(imsg_req_request)); 50557419a7fSflorian iface = get_iface_by_id(imsg_req_request.if_index); 50657419a7fSflorian if (iface != NULL) { 50757419a7fSflorian iface->xid = imsg_req_request.xid; 50857419a7fSflorian iface->requested_ip.s_addr = 50957419a7fSflorian imsg_req_request.requested_ip.s_addr; 51057419a7fSflorian iface->server_identifier.s_addr = 51157419a7fSflorian imsg_req_request.server_identifier.s_addr; 51257419a7fSflorian iface->dhcp_server.s_addr = 51357419a7fSflorian imsg_req_request.dhcp_server.s_addr; 51457419a7fSflorian send_request(iface); 51557419a7fSflorian } 51657419a7fSflorian break; 51757419a7fSflorian } 51857419a7fSflorian default: 51957419a7fSflorian log_debug("%s: error handling imsg %d", __func__, 52057419a7fSflorian imsg.hdr.type); 52157419a7fSflorian break; 52257419a7fSflorian } 52357419a7fSflorian imsg_free(&imsg); 52457419a7fSflorian } 52557419a7fSflorian if (!shut) 52657419a7fSflorian imsg_event_add(iev); 52757419a7fSflorian else { 52857419a7fSflorian /* This pipe is dead. Remove its event handler. */ 52957419a7fSflorian event_del(&iev->ev); 53057419a7fSflorian event_loopexit(NULL); 53157419a7fSflorian } 53257419a7fSflorian } 53357419a7fSflorian 53457419a7fSflorian int 53557419a7fSflorian get_flags(char *if_name) 53657419a7fSflorian { 53757419a7fSflorian struct ifreq ifr; 53857419a7fSflorian 53957419a7fSflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 54057419a7fSflorian if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) { 54157419a7fSflorian log_warn("SIOCGIFFLAGS"); 54257419a7fSflorian return -1; 54357419a7fSflorian } 54457419a7fSflorian return ifr.ifr_flags; 54557419a7fSflorian } 54657419a7fSflorian 54757419a7fSflorian int 54857419a7fSflorian get_xflags(char *if_name) 54957419a7fSflorian { 55057419a7fSflorian struct ifreq ifr; 55157419a7fSflorian 55257419a7fSflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 55357419a7fSflorian if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) { 55457419a7fSflorian log_warn("SIOCGIFXFLAGS"); 55557419a7fSflorian return -1; 55657419a7fSflorian } 55757419a7fSflorian return ifr.ifr_flags; 55857419a7fSflorian } 55957419a7fSflorian 56057419a7fSflorian void 56148e1b614Sflorian update_iface(struct if_msghdr *ifm, struct sockaddr_dl *sdl) 56257419a7fSflorian { 56357419a7fSflorian struct iface *iface; 5641bbf741cSflorian struct imsg_ifinfo ifinfo; 5651bbf741cSflorian uint32_t if_index; 56648e1b614Sflorian int flags, xflags; 5671bbf741cSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 56857419a7fSflorian 5691bbf741cSflorian if_index = ifm->ifm_index; 5701bbf741cSflorian 5711bbf741cSflorian flags = ifm->ifm_flags; 5721bbf741cSflorian xflags = ifm->ifm_xflags; 5731bbf741cSflorian 5741bbf741cSflorian iface = get_iface_by_id(if_index); 5751bbf741cSflorian if_name = if_indextoname(if_index, ifnamebuf); 5761bbf741cSflorian 5771bbf741cSflorian if (if_name == NULL) { 5781bbf741cSflorian if (iface != NULL) { 5791bbf741cSflorian log_debug("interface with idx %d removed", if_index); 5801bbf741cSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 5811bbf741cSflorian &if_index, sizeof(if_index)); 5821bbf741cSflorian remove_iface(if_index); 5831bbf741cSflorian } 58457419a7fSflorian return; 5851bbf741cSflorian } 58657419a7fSflorian 5871bbf741cSflorian if (!(xflags & IFXF_AUTOCONF4)) { 5881bbf741cSflorian if (iface != NULL) { 5891bbf741cSflorian log_info("Removed autoconf flag from %s", if_name); 5901bbf741cSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 5911bbf741cSflorian &if_index, sizeof(if_index)); 5921bbf741cSflorian remove_iface(if_index); 5931bbf741cSflorian } 59457419a7fSflorian return; 5951bbf741cSflorian } 59657419a7fSflorian 5971bbf741cSflorian memset(&ifinfo, 0, sizeof(ifinfo)); 5981bbf741cSflorian ifinfo.if_index = if_index; 5991bbf741cSflorian ifinfo.link_state = ifm->ifm_data.ifi_link_state; 6001bbf741cSflorian ifinfo.rdomain = ifm->ifm_tableid; 6011bbf741cSflorian ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == 6021bbf741cSflorian (IFF_UP | IFF_RUNNING); 60357419a7fSflorian 604a13fc78dSflorian if (sdl != NULL && (sdl->sdl_type == IFT_ETHER || 605a13fc78dSflorian sdl->sdl_type == IFT_CARP) && sdl->sdl_alen == ETHER_ADDR_LEN) 6061bbf741cSflorian memcpy(ifinfo.hw_address.ether_addr_octet, LLADDR(sdl), 6071bbf741cSflorian ETHER_ADDR_LEN); 60848e1b614Sflorian else if (iface == NULL) { 6091bbf741cSflorian log_warnx("Could not find AF_LINK address for %s.", if_name); 6101bbf741cSflorian return; 6114fd2b0e0Sflorian } 6121bbf741cSflorian 6131bbf741cSflorian if (iface == NULL) { 6144fd2b0e0Sflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 6154fd2b0e0Sflorian fatal("calloc"); 6164fd2b0e0Sflorian iface->udpsock = -1; 6174fd2b0e0Sflorian LIST_INSERT_HEAD(&interfaces, iface, entries); 6184fd2b0e0Sflorian frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0, 6194fd2b0e0Sflorian &if_index, sizeof(if_index)); 6201bbf741cSflorian } else { 6211bbf741cSflorian if (iface->ifinfo.rdomain != ifinfo.rdomain && 6221bbf741cSflorian iface->udpsock != -1) { 6231bbf741cSflorian close(iface->udpsock); 6241bbf741cSflorian iface->udpsock = -1; 6251bbf741cSflorian } 6264fd2b0e0Sflorian } 6274fd2b0e0Sflorian 6281bbf741cSflorian if (memcmp(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)) != 0) { 6291bbf741cSflorian memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); 6301bbf741cSflorian frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo, 6311bbf741cSflorian sizeof(iface->ifinfo)); 6321bbf741cSflorian } 63357419a7fSflorian } 63457419a7fSflorian 63557419a7fSflorian void 63657419a7fSflorian frontend_startup(void) 63757419a7fSflorian { 63857419a7fSflorian if (!event_initialized(&ev_route)) 63957419a7fSflorian fatalx("%s: did not receive a route socket from the main " 64057419a7fSflorian "process", __func__); 64157419a7fSflorian 6421bbf741cSflorian init_ifaces(); 64348e1b614Sflorian if (pledge("stdio unix recvfd", NULL) == -1) 64448e1b614Sflorian fatal("pledge"); 64557419a7fSflorian event_add(&ev_route, NULL); 6461bbf741cSflorian } 6471bbf741cSflorian 6481bbf741cSflorian void 6491bbf741cSflorian init_ifaces(void) 6501bbf741cSflorian { 6511bbf741cSflorian struct iface *iface; 6521bbf741cSflorian struct imsg_ifinfo ifinfo; 6531bbf741cSflorian struct if_nameindex *ifnidxp, *ifnidx; 6541bbf741cSflorian struct ifaddrs *ifap, *ifa; 6551bbf741cSflorian uint32_t if_index; 6561bbf741cSflorian int flags, xflags; 6571bbf741cSflorian char *if_name; 65857419a7fSflorian 65957419a7fSflorian if ((ifnidxp = if_nameindex()) == NULL) 66057419a7fSflorian fatalx("if_nameindex"); 66157419a7fSflorian 6621bbf741cSflorian if (getifaddrs(&ifap) != 0) 6631bbf741cSflorian fatal("getifaddrs"); 66457419a7fSflorian 6651bbf741cSflorian for (ifnidx = ifnidxp; ifnidx->if_index != 0 && ifnidx->if_name != NULL; 6661bbf741cSflorian ifnidx++) { 6671bbf741cSflorian if_index = ifnidx->if_index; 6681bbf741cSflorian if_name = ifnidx->if_name; 6691bbf741cSflorian if ((flags = get_flags(if_name)) == -1) 6701bbf741cSflorian continue; 6711bbf741cSflorian if ((xflags = get_xflags(if_name)) == -1) 6721bbf741cSflorian continue; 6731bbf741cSflorian if (!(xflags & IFXF_AUTOCONF4)) 6741bbf741cSflorian continue; 6751bbf741cSflorian 6761bbf741cSflorian memset(&ifinfo, 0, sizeof(ifinfo)); 6771bbf741cSflorian ifinfo.if_index = if_index; 6781bbf741cSflorian ifinfo.link_state = -1; 6791bbf741cSflorian ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == 6801bbf741cSflorian (IFF_UP | IFF_RUNNING); 6811bbf741cSflorian 6821bbf741cSflorian for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 6831bbf741cSflorian if (strcmp(if_name, ifa->ifa_name) != 0) 6841bbf741cSflorian continue; 6851bbf741cSflorian if (ifa->ifa_addr == NULL) 6861bbf741cSflorian continue; 6871bbf741cSflorian 6881bbf741cSflorian switch (ifa->ifa_addr->sa_family) { 6891bbf741cSflorian case AF_LINK: { 6901bbf741cSflorian struct if_data *if_data; 6911bbf741cSflorian struct sockaddr_dl *sdl; 6921bbf741cSflorian 6931bbf741cSflorian sdl = (struct sockaddr_dl *)ifa->ifa_addr; 694a13fc78dSflorian if ((sdl->sdl_type != IFT_ETHER && 695a13fc78dSflorian sdl->sdl_type != IFT_CARP) || 6961bbf741cSflorian sdl->sdl_alen != ETHER_ADDR_LEN) 6971bbf741cSflorian continue; 6981bbf741cSflorian memcpy(ifinfo.hw_address.ether_addr_octet, 6991bbf741cSflorian LLADDR(sdl), ETHER_ADDR_LEN); 7001bbf741cSflorian 7011bbf741cSflorian if_data = (struct if_data *)ifa->ifa_data; 7021bbf741cSflorian ifinfo.link_state = if_data->ifi_link_state; 7031bbf741cSflorian ifinfo.rdomain = if_data->ifi_rdomain; 7041bbf741cSflorian goto out; 7051bbf741cSflorian } 7061bbf741cSflorian default: 7071bbf741cSflorian break; 7081bbf741cSflorian } 7091bbf741cSflorian } 7101bbf741cSflorian out: 7111bbf741cSflorian if (ifinfo.link_state == -1) 7121bbf741cSflorian /* no AF_LINK found */ 7131bbf741cSflorian continue; 7141bbf741cSflorian 7151bbf741cSflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 7161bbf741cSflorian fatal("calloc"); 7171bbf741cSflorian iface->udpsock = -1; 7181bbf741cSflorian memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); 7191bbf741cSflorian LIST_INSERT_HEAD(&interfaces, iface, entries); 7201bbf741cSflorian frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0, 7211bbf741cSflorian &if_index, sizeof(if_index)); 7221bbf741cSflorian frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo, 7231bbf741cSflorian sizeof(iface->ifinfo)); 7241bbf741cSflorian } 7251bbf741cSflorian 7261bbf741cSflorian freeifaddrs(ifap); 72757419a7fSflorian if_freenameindex(ifnidxp); 72857419a7fSflorian } 72957419a7fSflorian 73057419a7fSflorian void 73157419a7fSflorian route_receive(int fd, short events, void *arg) 73257419a7fSflorian { 73357419a7fSflorian static uint8_t *buf; 73457419a7fSflorian 73557419a7fSflorian struct rt_msghdr *rtm; 73657419a7fSflorian struct sockaddr *sa, *rti_info[RTAX_MAX]; 73757419a7fSflorian ssize_t n; 73857419a7fSflorian 73957419a7fSflorian if (buf == NULL) { 74057419a7fSflorian buf = malloc(ROUTE_SOCKET_BUF_SIZE); 74157419a7fSflorian if (buf == NULL) 74257419a7fSflorian fatal("malloc"); 74357419a7fSflorian } 74457419a7fSflorian rtm = (struct rt_msghdr *)buf; 74557419a7fSflorian if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) { 74657419a7fSflorian if (errno == EAGAIN || errno == EINTR) 74757419a7fSflorian return; 74857419a7fSflorian log_warn("dispatch_rtmsg: read error"); 74957419a7fSflorian return; 75057419a7fSflorian } 75157419a7fSflorian 75257419a7fSflorian if (n == 0) 75357419a7fSflorian fatal("routing socket closed"); 75457419a7fSflorian 75557419a7fSflorian if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) { 75657419a7fSflorian log_warnx("partial rtm of %zd in buffer", n); 75757419a7fSflorian return; 75857419a7fSflorian } 75957419a7fSflorian 76057419a7fSflorian if (rtm->rtm_version != RTM_VERSION) 76157419a7fSflorian return; 76257419a7fSflorian 76357419a7fSflorian sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen); 76457419a7fSflorian get_rtaddrs(rtm->rtm_addrs, sa, rti_info); 76557419a7fSflorian 76657419a7fSflorian handle_route_message(rtm, rti_info); 76757419a7fSflorian } 76857419a7fSflorian 76957419a7fSflorian void 77057419a7fSflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info) 77157419a7fSflorian { 77248e1b614Sflorian struct sockaddr_dl *sdl = NULL; 773d3097762Sflorian struct if_announcemsghdr *ifan; 774d3097762Sflorian uint32_t if_index; 775d3097762Sflorian 77657419a7fSflorian switch (rtm->rtm_type) { 77757419a7fSflorian case RTM_IFINFO: 77848e1b614Sflorian if (rtm->rtm_addrs & RTA_IFP && rti_info[RTAX_IFP]->sa_family 77948e1b614Sflorian == AF_LINK) 78048e1b614Sflorian sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP]; 78148e1b614Sflorian update_iface((struct if_msghdr *)rtm, sdl); 78257419a7fSflorian break; 783d3097762Sflorian case RTM_IFANNOUNCE: 784d3097762Sflorian ifan = (struct if_announcemsghdr *)rtm; 785d3097762Sflorian if_index = ifan->ifan_index; 786d3097762Sflorian if (ifan->ifan_what == IFAN_DEPARTURE) { 787d3097762Sflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 788d3097762Sflorian &if_index, sizeof(if_index)); 789d3097762Sflorian remove_iface(if_index); 790d3097762Sflorian } 791d3097762Sflorian break; 79257419a7fSflorian case RTM_PROPOSAL: 79357419a7fSflorian if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) { 79457419a7fSflorian log_debug("RTP_PROPOSAL_SOLICIT"); 79557419a7fSflorian frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS, 79657419a7fSflorian 0, 0, NULL, 0); 797c7313d44Sflorian } 798c7313d44Sflorian #ifndef SMALL 799c7313d44Sflorian else if (rtm->rtm_flags & RTF_PROTO3) { 800bc837866Sflorian char ifnamebuf[IF_NAMESIZE], *if_name; 801bc837866Sflorian 802bc837866Sflorian if_index = rtm->rtm_index; 803bc837866Sflorian if_name = if_indextoname(if_index, ifnamebuf); 804bc837866Sflorian log_warnx("\"dhclient %s\" ran, requesting new lease", 805bc837866Sflorian if_name != NULL ? if_name : "(unknown)"); 806c7313d44Sflorian frontend_imsg_compose_engine(IMSG_REQUEST_REBOOT, 807bc837866Sflorian 0, 0, &if_index, sizeof(if_index)); 80857419a7fSflorian } 809c7313d44Sflorian #endif /* SMALL */ 81057419a7fSflorian break; 81157419a7fSflorian default: 81257419a7fSflorian log_debug("unexpected RTM: %d", rtm->rtm_type); 81357419a7fSflorian break; 81457419a7fSflorian } 81557419a7fSflorian } 81657419a7fSflorian 81757419a7fSflorian #define ROUNDUP(a) \ 81857419a7fSflorian ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 81957419a7fSflorian 82057419a7fSflorian void 82157419a7fSflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 82257419a7fSflorian { 82357419a7fSflorian int i; 82457419a7fSflorian 82557419a7fSflorian for (i = 0; i < RTAX_MAX; i++) { 82657419a7fSflorian if (addrs & (1 << i)) { 82757419a7fSflorian rti_info[i] = sa; 82857419a7fSflorian sa = (struct sockaddr *)((char *)(sa) + 82957419a7fSflorian ROUNDUP(sa->sa_len)); 83057419a7fSflorian } else 83157419a7fSflorian rti_info[i] = NULL; 83257419a7fSflorian } 83357419a7fSflorian } 83457419a7fSflorian 83557419a7fSflorian void 83657419a7fSflorian bpf_receive(int fd, short events, void *arg) 83757419a7fSflorian { 83857419a7fSflorian struct bpf_hdr *hdr; 83957419a7fSflorian struct imsg_dhcp imsg_dhcp; 84057419a7fSflorian struct iface *iface; 84157419a7fSflorian ssize_t len, rem; 84257419a7fSflorian uint8_t *p; 84357419a7fSflorian 84457419a7fSflorian iface = (struct iface *)arg; 84557419a7fSflorian 84657419a7fSflorian if ((len = read(fd, iface->bpfev.buf, BPFLEN)) == -1) { 84737fb58f6Sflorian log_warn("%s: read", __func__); 84857419a7fSflorian return; 84957419a7fSflorian } 850e998cdbeSflorian 851e998cdbeSflorian if (len == 0) 852e998cdbeSflorian fatal("%s len == 0", __func__); 85357419a7fSflorian 85457419a7fSflorian memset(&imsg_dhcp, 0, sizeof(imsg_dhcp)); 8551bbf741cSflorian imsg_dhcp.if_index = iface->ifinfo.if_index; 85657419a7fSflorian 85757419a7fSflorian rem = len; 85857419a7fSflorian p = iface->bpfev.buf; 85957419a7fSflorian 86057419a7fSflorian while (rem > 0) { 86157419a7fSflorian if ((size_t)rem < sizeof(*hdr)) { 86257419a7fSflorian log_warnx("packet too short"); 86357419a7fSflorian return; 86457419a7fSflorian } 86557419a7fSflorian hdr = (struct bpf_hdr *)p; 86657419a7fSflorian if (hdr->bh_caplen != hdr->bh_datalen) { 86757419a7fSflorian log_warnx("skipping truncated packet"); 86857419a7fSflorian goto cont; 86957419a7fSflorian } 87057419a7fSflorian if (rem < hdr->bh_hdrlen + hdr->bh_caplen) 87157419a7fSflorian /* we are done */ 87257419a7fSflorian break; 87357419a7fSflorian if (hdr->bh_caplen > sizeof(imsg_dhcp.packet)) { 87457419a7fSflorian log_warn("packet too big"); 87557419a7fSflorian goto cont; 87657419a7fSflorian } 87757419a7fSflorian memcpy(&imsg_dhcp.packet, p + hdr->bh_hdrlen, hdr->bh_caplen); 87857419a7fSflorian imsg_dhcp.len = hdr->bh_caplen; 87957419a7fSflorian frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp, 88057419a7fSflorian sizeof(imsg_dhcp)); 88157419a7fSflorian cont: 88257419a7fSflorian p += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 88357419a7fSflorian rem -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 88457419a7fSflorian 88557419a7fSflorian } 88657419a7fSflorian } 88757419a7fSflorian 88857419a7fSflorian ssize_t 889a41cc082Sflorian build_packet(uint8_t message_type, char *if_name, uint32_t xid, 890a41cc082Sflorian struct ether_addr *hw_address, struct in_addr *requested_ip, 891a41cc082Sflorian struct in_addr *server_identifier) 89257419a7fSflorian { 89357419a7fSflorian static uint8_t dhcp_cookie[] = DHCP_COOKIE; 89457419a7fSflorian static uint8_t dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1, 89557419a7fSflorian DHCPDISCOVER}; 89657419a7fSflorian static uint8_t dhcp_hostname[255] = {DHO_HOST_NAME, 0 /*, ... */}; 89757419a7fSflorian static uint8_t dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7, 89857419a7fSflorian HTYPE_ETHER, 0, 0, 0, 0, 0, 0}; 89957419a7fSflorian static uint8_t dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST, 900ea86c963Sflorian 8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS, 90157419a7fSflorian DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS, 902ea86c963Sflorian DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES}; 90357419a7fSflorian static uint8_t dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS, 90457419a7fSflorian 4, 0, 0, 0, 0}; 90557419a7fSflorian static uint8_t dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER, 90657419a7fSflorian 4, 0, 0, 0, 0}; 907a41cc082Sflorian #ifndef SMALL 908a41cc082Sflorian struct iface_conf *iface_conf; 909a41cc082Sflorian #endif /* SMALL */ 91057419a7fSflorian struct dhcp_hdr *hdr; 9112d2edb0eSflorian ssize_t len; 91257419a7fSflorian uint8_t *p; 91357419a7fSflorian char *c; 91457419a7fSflorian 915a41cc082Sflorian #ifndef SMALL 916a41cc082Sflorian iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name); 917a41cc082Sflorian #endif /* SMALL */ 918a41cc082Sflorian 91957419a7fSflorian memset(dhcp_packet, 0, sizeof(dhcp_packet)); 92057419a7fSflorian dhcp_message_type[2] = message_type; 92157419a7fSflorian p = dhcp_packet; 92257419a7fSflorian hdr = (struct dhcp_hdr *)p; 92357419a7fSflorian hdr->op = DHCP_BOOTREQUEST; 92457419a7fSflorian hdr->htype = HTYPE_ETHER; 92557419a7fSflorian hdr->hlen = 6; 92657419a7fSflorian hdr->hops = 0; 92757419a7fSflorian hdr->xid = xid; 92857419a7fSflorian hdr->secs = 0; 92957419a7fSflorian memcpy(hdr->chaddr, hw_address, sizeof(*hw_address)); 93057419a7fSflorian p += sizeof(struct dhcp_hdr); 93157419a7fSflorian memcpy(p, dhcp_cookie, sizeof(dhcp_cookie)); 93257419a7fSflorian p += sizeof(dhcp_cookie); 93357419a7fSflorian memcpy(p, dhcp_message_type, sizeof(dhcp_message_type)); 93457419a7fSflorian p += sizeof(dhcp_message_type); 93557419a7fSflorian if (gethostname(dhcp_hostname + 2, sizeof(dhcp_hostname) - 2) == 0) { 93657419a7fSflorian if ((c = strchr(dhcp_hostname + 2, '.')) != NULL) 93757419a7fSflorian *c = '\0'; 93857419a7fSflorian dhcp_hostname[1] = strlen(dhcp_hostname + 2); 93957419a7fSflorian memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2); 94057419a7fSflorian p += dhcp_hostname[1] + 2; 94157419a7fSflorian } 942a41cc082Sflorian 943a41cc082Sflorian #ifndef SMALL 944a41cc082Sflorian if (iface_conf != NULL) { 945a41cc082Sflorian if (iface_conf->c_id_len > 0) { 946a41cc082Sflorian /* XXX check space */ 947a41cc082Sflorian memcpy(p, iface_conf->c_id, iface_conf->c_id_len); 948a41cc082Sflorian p += iface_conf->c_id_len; 949*90c58abaSflorian } else { 950*90c58abaSflorian memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address)); 951*90c58abaSflorian memcpy(p, dhcp_client_id, sizeof(dhcp_client_id)); 952*90c58abaSflorian p += sizeof(dhcp_client_id); 953a41cc082Sflorian } 954a41cc082Sflorian if (iface_conf->vc_id_len > 0) { 955a41cc082Sflorian /* XXX check space */ 956a41cc082Sflorian memcpy(p, iface_conf->vc_id, iface_conf->vc_id_len); 957a41cc082Sflorian p += iface_conf->vc_id_len; 958a41cc082Sflorian } 959a41cc082Sflorian } else 960a41cc082Sflorian #endif /* SMALL */ 961a41cc082Sflorian { 96257419a7fSflorian memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address)); 96357419a7fSflorian memcpy(p, dhcp_client_id, sizeof(dhcp_client_id)); 96457419a7fSflorian p += sizeof(dhcp_client_id); 965a41cc082Sflorian } 96657419a7fSflorian memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); 96757419a7fSflorian p += sizeof(dhcp_req_list); 96857419a7fSflorian 96957419a7fSflorian if (message_type == DHCPREQUEST) { 97057419a7fSflorian memcpy(dhcp_requested_address + 2, requested_ip, 97157419a7fSflorian sizeof(*requested_ip)); 97257419a7fSflorian memcpy(p, dhcp_requested_address, 97357419a7fSflorian sizeof(dhcp_requested_address)); 97457419a7fSflorian p += sizeof(dhcp_requested_address); 97557419a7fSflorian 97657419a7fSflorian if (server_identifier->s_addr != INADDR_ANY) { 97757419a7fSflorian memcpy(dhcp_server_identifier + 2, server_identifier, 97857419a7fSflorian sizeof(*server_identifier)); 97957419a7fSflorian memcpy(p, dhcp_server_identifier, 98057419a7fSflorian sizeof(dhcp_server_identifier)); 98157419a7fSflorian p += sizeof(dhcp_server_identifier); 98257419a7fSflorian } 98357419a7fSflorian } 98457419a7fSflorian 98557419a7fSflorian *p = DHO_END; 98657419a7fSflorian p += 1; 98757419a7fSflorian 9882d2edb0eSflorian len = p - dhcp_packet; 9892d2edb0eSflorian 9902d2edb0eSflorian /* dhcp_packet is initialized with DHO_PADs */ 9912d2edb0eSflorian if (len < BOOTP_MIN_LEN) 9922d2edb0eSflorian len = BOOTP_MIN_LEN; 9932d2edb0eSflorian 9942d2edb0eSflorian return (len); 99557419a7fSflorian } 99657419a7fSflorian 99757419a7fSflorian void 99857419a7fSflorian send_discover(struct iface *iface) 99957419a7fSflorian { 100057419a7fSflorian ssize_t pkt_len; 1001e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 100257419a7fSflorian 100357419a7fSflorian if (!event_initialized(&iface->bpfev.ev)) { 100457419a7fSflorian iface->send_discover = 1; 100557419a7fSflorian return; 100657419a7fSflorian } 100757419a7fSflorian iface->send_discover = 0; 1008e998cdbeSflorian 10091bbf741cSflorian if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf); 1010e998cdbeSflorian log_debug("DHCPDISCOVER on %s", if_name == NULL ? "?" : if_name); 1011e998cdbeSflorian 1012a41cc082Sflorian pkt_len = build_packet(DHCPDISCOVER, if_name, iface->xid, 10131bbf741cSflorian &iface->ifinfo.hw_address, &iface->requested_ip, NULL); 101457419a7fSflorian bpf_send_packet(iface, dhcp_packet, pkt_len); 101557419a7fSflorian } 101657419a7fSflorian 101757419a7fSflorian void 101857419a7fSflorian send_request(struct iface *iface) 101957419a7fSflorian { 102057419a7fSflorian ssize_t pkt_len; 1021e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 1022e998cdbeSflorian 10231bbf741cSflorian if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf); 1024e998cdbeSflorian log_debug("DHCPREQUEST on %s", if_name == NULL ? "?" : if_name); 102557419a7fSflorian 1026a41cc082Sflorian pkt_len = build_packet(DHCPREQUEST, if_name, iface->xid, 10271bbf741cSflorian &iface->ifinfo.hw_address, &iface->requested_ip, 10281bbf741cSflorian &iface->server_identifier); 1029c645071eSflorian if (iface->dhcp_server.s_addr != INADDR_ANY) { 1030c645071eSflorian if (udp_send_packet(iface, dhcp_packet, pkt_len) == -1) 1031c645071eSflorian bpf_send_packet(iface, dhcp_packet, pkt_len); 1032c645071eSflorian } else 103357419a7fSflorian bpf_send_packet(iface, dhcp_packet, pkt_len); 103457419a7fSflorian } 103557419a7fSflorian 1036c645071eSflorian int 103757419a7fSflorian udp_send_packet(struct iface *iface, uint8_t *packet, ssize_t len) 103857419a7fSflorian { 103957419a7fSflorian struct sockaddr_in to; 104057419a7fSflorian 104157419a7fSflorian memset(&to, 0, sizeof(to)); 104257419a7fSflorian to.sin_family = AF_INET; 104357419a7fSflorian to.sin_len = sizeof(to); 104457419a7fSflorian to.sin_addr.s_addr = iface->dhcp_server.s_addr; 104557419a7fSflorian to.sin_port = ntohs(SERVER_PORT); 104657419a7fSflorian 104757419a7fSflorian if (sendto(iface->udpsock, packet, len, 0, (struct sockaddr *)&to, 1048c645071eSflorian sizeof(to)) == -1) { 104957419a7fSflorian log_warn("sendto"); 1050c645071eSflorian return -1; 1051c645071eSflorian } 1052c645071eSflorian return 0; 105357419a7fSflorian } 105457419a7fSflorian void 105557419a7fSflorian bpf_send_packet(struct iface *iface, uint8_t *packet, ssize_t len) 105657419a7fSflorian { 105757419a7fSflorian struct iovec iov[4]; 105857419a7fSflorian struct ether_header eh; 105957419a7fSflorian struct ip ip; 106057419a7fSflorian struct udphdr udp; 106157419a7fSflorian ssize_t total, result; 106257419a7fSflorian int iovcnt = 0, i; 106357419a7fSflorian 106457419a7fSflorian memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost)); 10651bbf741cSflorian memcpy(eh.ether_shost, &iface->ifinfo.hw_address, 10661bbf741cSflorian sizeof(eh.ether_dhost)); 106757419a7fSflorian eh.ether_type = htons(ETHERTYPE_IP); 106857419a7fSflorian iov[0].iov_base = &eh; 106957419a7fSflorian iov[0].iov_len = sizeof(eh); 107057419a7fSflorian iovcnt++; 107157419a7fSflorian 107257419a7fSflorian ip.ip_v = 4; 107357419a7fSflorian ip.ip_hl = 5; 107457419a7fSflorian ip.ip_tos = IPTOS_LOWDELAY; 107557419a7fSflorian ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); 107657419a7fSflorian ip.ip_id = 0; 107757419a7fSflorian ip.ip_off = 0; 107857419a7fSflorian ip.ip_ttl = 128; 107957419a7fSflorian ip.ip_p = IPPROTO_UDP; 108057419a7fSflorian ip.ip_sum = 0; 108157419a7fSflorian ip.ip_src.s_addr = 0; 108257419a7fSflorian ip.ip_dst.s_addr = INADDR_BROADCAST; 108357419a7fSflorian ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); 108457419a7fSflorian iov[iovcnt].iov_base = &ip; 108557419a7fSflorian iov[iovcnt].iov_len = sizeof(ip); 108657419a7fSflorian iovcnt++; 108757419a7fSflorian 108857419a7fSflorian udp.uh_sport = htons(CLIENT_PORT); 108957419a7fSflorian udp.uh_dport = htons(SERVER_PORT); 109057419a7fSflorian udp.uh_ulen = htons(sizeof(udp) + len); 109157419a7fSflorian udp.uh_sum = 0; 109257419a7fSflorian udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), 109357419a7fSflorian checksum((unsigned char *)packet, len, 109457419a7fSflorian checksum((unsigned char *)&ip.ip_src, 109557419a7fSflorian 2 * sizeof(ip.ip_src), 109657419a7fSflorian IPPROTO_UDP + (uint32_t)ntohs(udp.uh_ulen))))); 109757419a7fSflorian iov[iovcnt].iov_base = &udp; 109857419a7fSflorian iov[iovcnt].iov_len = sizeof(udp); 109957419a7fSflorian iovcnt++; 110057419a7fSflorian 110157419a7fSflorian iov[iovcnt].iov_base = packet; 110257419a7fSflorian iov[iovcnt].iov_len = len; 110357419a7fSflorian iovcnt++; 110457419a7fSflorian 110557419a7fSflorian total = 0; 110657419a7fSflorian for (i = 0; i < iovcnt; i++) 110757419a7fSflorian total += iov[i].iov_len; 110857419a7fSflorian 110957419a7fSflorian result = writev(EVENT_FD(&iface->bpfev.ev), iov, iovcnt); 111057419a7fSflorian if (result == -1) 111157419a7fSflorian log_warn("%s: writev", __func__); 111257419a7fSflorian else if (result < total) { 111357419a7fSflorian log_warnx("%s, writev: %zd of %zd bytes", __func__, result, 111457419a7fSflorian total); 111557419a7fSflorian } 111657419a7fSflorian } 111757419a7fSflorian 111857419a7fSflorian struct iface* 111957419a7fSflorian get_iface_by_id(uint32_t if_index) 112057419a7fSflorian { 112157419a7fSflorian struct iface *iface; 112257419a7fSflorian 112357419a7fSflorian LIST_FOREACH (iface, &interfaces, entries) { 11241bbf741cSflorian if (iface->ifinfo.if_index == if_index) 112557419a7fSflorian return (iface); 112657419a7fSflorian } 112757419a7fSflorian 112857419a7fSflorian return (NULL); 112957419a7fSflorian } 113057419a7fSflorian 113157419a7fSflorian void 113257419a7fSflorian remove_iface(uint32_t if_index) 113357419a7fSflorian { 113457419a7fSflorian struct iface *iface; 113557419a7fSflorian 113657419a7fSflorian iface = get_iface_by_id(if_index); 113757419a7fSflorian 113857419a7fSflorian if (iface == NULL) 113957419a7fSflorian return; 114057419a7fSflorian 114157419a7fSflorian LIST_REMOVE(iface, entries); 114257419a7fSflorian event_del(&iface->bpfev.ev); 114357419a7fSflorian close(EVENT_FD(&iface->bpfev.ev)); 114457419a7fSflorian if (iface->udpsock != -1) 114557419a7fSflorian close(iface->udpsock); 114657419a7fSflorian free(iface); 114757419a7fSflorian } 114857419a7fSflorian 114957419a7fSflorian void 115057419a7fSflorian set_bpfsock(int bpfsock, uint32_t if_index) 115157419a7fSflorian { 115257419a7fSflorian struct iface *iface; 115357419a7fSflorian 115457419a7fSflorian if ((iface = get_iface_by_id(if_index)) == NULL) { 115557419a7fSflorian /* 115657419a7fSflorian * The interface disappeared while we were waiting for the 115757419a7fSflorian * parent process to open the raw socket. 115857419a7fSflorian */ 115957419a7fSflorian close(bpfsock); 116057419a7fSflorian } else { 116157419a7fSflorian event_set(&iface->bpfev.ev, bpfsock, EV_READ | 116257419a7fSflorian EV_PERSIST, bpf_receive, iface); 116357419a7fSflorian event_add(&iface->bpfev.ev, NULL); 116457419a7fSflorian if (iface->send_discover) 116557419a7fSflorian send_discover(iface); 116657419a7fSflorian } 116757419a7fSflorian } 1168a41cc082Sflorian 1169a41cc082Sflorian #ifndef SMALL 1170a41cc082Sflorian struct iface_conf* 1171a41cc082Sflorian find_iface_conf(struct iface_conf_head *head, char *if_name) 1172a41cc082Sflorian { 1173a41cc082Sflorian struct iface_conf *iface_conf; 1174a41cc082Sflorian 1175a41cc082Sflorian if (if_name == NULL) 1176a41cc082Sflorian return (NULL); 1177a41cc082Sflorian 1178a41cc082Sflorian SIMPLEQ_FOREACH(iface_conf, head, entry) { 1179a41cc082Sflorian if (strcmp(iface_conf->name, if_name) == 0) 1180a41cc082Sflorian return iface_conf; 1181a41cc082Sflorian } 1182a41cc082Sflorian return (NULL); 1183a41cc082Sflorian } 1184580befd2Sflorian 1185580befd2Sflorian int* 1186580befd2Sflorian changed_ifaces(struct dhcpleased_conf *oconf, struct dhcpleased_conf *nconf) 1187580befd2Sflorian { 1188580befd2Sflorian struct iface_conf *iface_conf, *oiface_conf; 1189580befd2Sflorian int *ret, if_index, count = 0, i = 0; 1190580befd2Sflorian 1191580befd2Sflorian /* 1192580befd2Sflorian * Worst case: All old interfaces replaced with new interfaces. 1193580befd2Sflorian * This should still be a small number 1194580befd2Sflorian */ 1195580befd2Sflorian SIMPLEQ_FOREACH(iface_conf, &oconf->iface_list, entry) 1196580befd2Sflorian count++; 1197580befd2Sflorian SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) 1198580befd2Sflorian count++; 1199580befd2Sflorian 1200580befd2Sflorian ret = calloc(count + 1, sizeof(int)); 1201580befd2Sflorian 1202580befd2Sflorian SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) { 1203580befd2Sflorian if ((if_index = if_nametoindex(iface_conf->name)) == 0) 1204580befd2Sflorian continue; 1205580befd2Sflorian oiface_conf = find_iface_conf(&oconf->iface_list, 1206580befd2Sflorian iface_conf->name); 1207580befd2Sflorian if (oiface_conf == NULL) { 1208580befd2Sflorian /* new interface added to config */ 1209580befd2Sflorian ret[i++] = if_index; 1210580befd2Sflorian } else if (iface_conf_cmp(iface_conf, oiface_conf) != 0) { 1211580befd2Sflorian /* interface conf changed */ 1212580befd2Sflorian ret[i++] = if_index; 1213580befd2Sflorian } 1214580befd2Sflorian } 1215580befd2Sflorian SIMPLEQ_FOREACH(oiface_conf, &oconf->iface_list, entry) { 1216580befd2Sflorian if ((if_index = if_nametoindex(oiface_conf->name)) == 0) 1217580befd2Sflorian continue; 1218580befd2Sflorian if (find_iface_conf(&nconf->iface_list, oiface_conf->name) == 1219580befd2Sflorian NULL) { 1220580befd2Sflorian /* interface removed from config */ 1221580befd2Sflorian ret[i++] = if_index; 1222580befd2Sflorian } 1223580befd2Sflorian } 1224580befd2Sflorian return ret; 1225580befd2Sflorian } 1226580befd2Sflorian 1227580befd2Sflorian int 1228580befd2Sflorian iface_conf_cmp(struct iface_conf *a, struct iface_conf *b) 1229580befd2Sflorian { 1230580befd2Sflorian if (a->vc_id_len != b->vc_id_len) 1231580befd2Sflorian return 1; 1232580befd2Sflorian if (memcmp(a->vc_id, b->vc_id, a->vc_id_len) != 0) 1233580befd2Sflorian return 1; 1234580befd2Sflorian if (a->c_id_len != b->c_id_len) 1235580befd2Sflorian return 1; 1236580befd2Sflorian if (memcmp(a->c_id, b->c_id, a->c_id_len) != 0) 1237580befd2Sflorian return 1; 1238c2bc6c6dSflorian if (a->ignore != b->ignore) 1239c2bc6c6dSflorian return 1; 1240c2bc6c6dSflorian if (a->ignore_servers_len != b->ignore_servers_len) 1241c2bc6c6dSflorian return 1; 1242c2bc6c6dSflorian if (memcmp(a->ignore_servers, b->ignore_servers, 1243c2bc6c6dSflorian a->ignore_servers_len * sizeof (struct in_addr)) != 0) 1244c2bc6c6dSflorian return 1; 1245580befd2Sflorian return 0; 1246580befd2Sflorian } 1247a41cc082Sflorian #endif /* SMALL */ 1248