1*ea86c963Sflorian /* $OpenBSD: frontend.c,v 1.12 2021/06/16 14:08:38 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); 9357419a7fSflorian ssize_t build_packet(uint8_t, uint32_t, struct ether_addr *, struct 9457419a7fSflorian 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); 9857419a7fSflorian void udp_send_packet(struct iface *, uint8_t *, ssize_t); 9957419a7fSflorian 10057419a7fSflorian LIST_HEAD(, iface) interfaces; 10157419a7fSflorian static struct imsgev *iev_main; 10257419a7fSflorian static struct imsgev *iev_engine; 10357419a7fSflorian struct event ev_route; 10457419a7fSflorian int ioctlsock; 10557419a7fSflorian 10657419a7fSflorian uint8_t dhcp_packet[1500]; 10757419a7fSflorian 10857419a7fSflorian void 10957419a7fSflorian frontend_sig_handler(int sig, short event, void *bula) 11057419a7fSflorian { 11157419a7fSflorian /* 11257419a7fSflorian * Normal signal handler rules don't apply because libevent 11357419a7fSflorian * decouples for us. 11457419a7fSflorian */ 11557419a7fSflorian 11657419a7fSflorian switch (sig) { 11757419a7fSflorian case SIGINT: 11857419a7fSflorian case SIGTERM: 11957419a7fSflorian frontend_shutdown(); 12057419a7fSflorian default: 12157419a7fSflorian fatalx("unexpected signal"); 12257419a7fSflorian } 12357419a7fSflorian } 12457419a7fSflorian 12557419a7fSflorian void 12657419a7fSflorian frontend(int debug, int verbose) 12757419a7fSflorian { 12857419a7fSflorian struct event ev_sigint, ev_sigterm; 12957419a7fSflorian struct passwd *pw; 13057419a7fSflorian 13157419a7fSflorian log_init(debug, LOG_DAEMON); 13257419a7fSflorian log_setverbose(verbose); 13357419a7fSflorian 13457419a7fSflorian if ((pw = getpwnam(DHCPLEASED_USER)) == NULL) 13557419a7fSflorian fatal("getpwnam"); 13657419a7fSflorian 13757419a7fSflorian if (chdir("/") == -1) 13857419a7fSflorian fatal("chdir(\"/\")"); 13957419a7fSflorian 140c7d84514Sflorian if (unveil("/", "") == -1) 141c7d84514Sflorian fatal("unveil(\"/\", \"\")"); 142c7d84514Sflorian if (unveil(NULL, NULL) == -1) 143c7d84514Sflorian fatal("unveil(NULL, NULL)"); 144c7d84514Sflorian 14557419a7fSflorian setproctitle("%s", "frontend"); 14657419a7fSflorian log_procinit("frontend"); 14757419a7fSflorian 14857419a7fSflorian if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) 14957419a7fSflorian fatal("socket"); 15057419a7fSflorian 15157419a7fSflorian if (setgroups(1, &pw->pw_gid) || 15257419a7fSflorian setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 15357419a7fSflorian setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 15457419a7fSflorian fatal("can't drop privileges"); 15557419a7fSflorian 15657419a7fSflorian if (pledge("stdio unix recvfd route", NULL) == -1) 15757419a7fSflorian fatal("pledge"); 15857419a7fSflorian event_init(); 15957419a7fSflorian 16057419a7fSflorian /* Setup signal handler. */ 16157419a7fSflorian signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL); 16257419a7fSflorian signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL); 16357419a7fSflorian signal_add(&ev_sigint, NULL); 16457419a7fSflorian signal_add(&ev_sigterm, NULL); 16557419a7fSflorian signal(SIGPIPE, SIG_IGN); 16657419a7fSflorian signal(SIGHUP, SIG_IGN); 16757419a7fSflorian 16857419a7fSflorian /* Setup pipe and event handler to the parent process. */ 16957419a7fSflorian if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) 17057419a7fSflorian fatal(NULL); 17157419a7fSflorian imsg_init(&iev_main->ibuf, 3); 17257419a7fSflorian iev_main->handler = frontend_dispatch_main; 17357419a7fSflorian iev_main->events = EV_READ; 17457419a7fSflorian event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, 17557419a7fSflorian iev_main->handler, iev_main); 17657419a7fSflorian event_add(&iev_main->ev, NULL); 17757419a7fSflorian 17857419a7fSflorian LIST_INIT(&interfaces); 17957419a7fSflorian event_dispatch(); 18057419a7fSflorian 18157419a7fSflorian frontend_shutdown(); 18257419a7fSflorian } 18357419a7fSflorian 18457419a7fSflorian __dead void 18557419a7fSflorian frontend_shutdown(void) 18657419a7fSflorian { 18757419a7fSflorian /* Close pipes. */ 18857419a7fSflorian msgbuf_write(&iev_engine->ibuf.w); 18957419a7fSflorian msgbuf_clear(&iev_engine->ibuf.w); 19057419a7fSflorian close(iev_engine->ibuf.fd); 19157419a7fSflorian msgbuf_write(&iev_main->ibuf.w); 19257419a7fSflorian msgbuf_clear(&iev_main->ibuf.w); 19357419a7fSflorian close(iev_main->ibuf.fd); 19457419a7fSflorian 19557419a7fSflorian free(iev_engine); 19657419a7fSflorian free(iev_main); 19757419a7fSflorian 19857419a7fSflorian log_info("frontend exiting"); 19957419a7fSflorian exit(0); 20057419a7fSflorian } 20157419a7fSflorian 20257419a7fSflorian int 20357419a7fSflorian frontend_imsg_compose_main(int type, pid_t pid, void *data, 20457419a7fSflorian uint16_t datalen) 20557419a7fSflorian { 20657419a7fSflorian return (imsg_compose_event(iev_main, type, 0, pid, -1, data, 20757419a7fSflorian datalen)); 20857419a7fSflorian } 20957419a7fSflorian 21057419a7fSflorian int 21157419a7fSflorian frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid, 21257419a7fSflorian void *data, uint16_t datalen) 21357419a7fSflorian { 21457419a7fSflorian return (imsg_compose_event(iev_engine, type, peerid, pid, -1, 21557419a7fSflorian data, datalen)); 21657419a7fSflorian } 21757419a7fSflorian 21857419a7fSflorian void 21957419a7fSflorian frontend_dispatch_main(int fd, short event, void *bula) 22057419a7fSflorian { 22157419a7fSflorian struct imsg imsg; 22257419a7fSflorian struct imsgev *iev = bula; 22357419a7fSflorian struct imsgbuf *ibuf = &iev->ibuf; 22457419a7fSflorian struct iface *iface; 22557419a7fSflorian ssize_t n; 22657419a7fSflorian int shut = 0, bpfsock, if_index, udpsock; 22757419a7fSflorian 22857419a7fSflorian if (event & EV_READ) { 22957419a7fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 23057419a7fSflorian fatal("imsg_read error"); 23157419a7fSflorian if (n == 0) /* Connection closed. */ 23257419a7fSflorian shut = 1; 23357419a7fSflorian } 23457419a7fSflorian if (event & EV_WRITE) { 23557419a7fSflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 23657419a7fSflorian fatal("msgbuf_write"); 23757419a7fSflorian if (n == 0) /* Connection closed. */ 23857419a7fSflorian shut = 1; 23957419a7fSflorian } 24057419a7fSflorian 24157419a7fSflorian for (;;) { 24257419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 24357419a7fSflorian fatal("%s: imsg_get error", __func__); 24457419a7fSflorian if (n == 0) /* No more messages. */ 24557419a7fSflorian break; 24657419a7fSflorian 24757419a7fSflorian switch (imsg.hdr.type) { 24857419a7fSflorian case IMSG_SOCKET_IPC: 24957419a7fSflorian /* 25057419a7fSflorian * Setup pipe and event handler to the engine 25157419a7fSflorian * process. 25257419a7fSflorian */ 25357419a7fSflorian if (iev_engine) 25457419a7fSflorian fatalx("%s: received unexpected imsg fd " 25557419a7fSflorian "to frontend", __func__); 25657419a7fSflorian 25757419a7fSflorian if ((fd = imsg.fd) == -1) 25857419a7fSflorian fatalx("%s: expected to receive imsg fd to " 25957419a7fSflorian "frontend but didn't receive any", 26057419a7fSflorian __func__); 26157419a7fSflorian 26257419a7fSflorian iev_engine = malloc(sizeof(struct imsgev)); 26357419a7fSflorian if (iev_engine == NULL) 26457419a7fSflorian fatal(NULL); 26557419a7fSflorian 26657419a7fSflorian imsg_init(&iev_engine->ibuf, fd); 26757419a7fSflorian iev_engine->handler = frontend_dispatch_engine; 26857419a7fSflorian iev_engine->events = EV_READ; 26957419a7fSflorian 27057419a7fSflorian event_set(&iev_engine->ev, iev_engine->ibuf.fd, 27157419a7fSflorian iev_engine->events, iev_engine->handler, iev_engine); 27257419a7fSflorian event_add(&iev_engine->ev, NULL); 27357419a7fSflorian break; 27457419a7fSflorian case IMSG_BPFSOCK: 27557419a7fSflorian if ((bpfsock = imsg.fd) == -1) 27657419a7fSflorian fatalx("%s: expected to receive imsg " 27757419a7fSflorian "bpf fd but didn't receive any", 27857419a7fSflorian __func__); 27957419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 28057419a7fSflorian fatalx("%s: IMSG_BPFSOCK wrong length: " 28157419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 28257419a7fSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 28357419a7fSflorian set_bpfsock(bpfsock, if_index); 28457419a7fSflorian break; 28557419a7fSflorian case IMSG_UDPSOCK: 28657419a7fSflorian if ((udpsock = imsg.fd) == -1) 28757419a7fSflorian fatalx("%s: expected to receive imsg " 28857419a7fSflorian "udpsocket fd but didn't receive any", 28957419a7fSflorian __func__); 29057419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 29157419a7fSflorian fatalx("%s: IMSG_UDPSOCK wrong length: " 29257419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 29357419a7fSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 29457419a7fSflorian if ((iface = get_iface_by_id(if_index)) == NULL) { 29557419a7fSflorian close(fd); 29657419a7fSflorian break; 29757419a7fSflorian } 29857419a7fSflorian if (iface->udpsock != -1) 29957419a7fSflorian fatalx("%s: received unexpected udpsocket", 30057419a7fSflorian __func__); 30157419a7fSflorian iface->udpsock = udpsock; 30257419a7fSflorian break; 30357419a7fSflorian case IMSG_CLOSE_UDPSOCK: 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 iface->udpsock != -1) { 31057419a7fSflorian close(iface->udpsock); 31157419a7fSflorian iface->udpsock = -1; 31257419a7fSflorian } 31357419a7fSflorian break; 31457419a7fSflorian case IMSG_ROUTESOCK: 31557419a7fSflorian if ((fd = imsg.fd) == -1) 31657419a7fSflorian fatalx("%s: expected to receive imsg " 31757419a7fSflorian "routesocket fd but didn't receive any", 31857419a7fSflorian __func__); 31957419a7fSflorian event_set(&ev_route, fd, EV_READ | EV_PERSIST, 32057419a7fSflorian route_receive, NULL); 32157419a7fSflorian break; 32257419a7fSflorian case IMSG_STARTUP: 32357419a7fSflorian frontend_startup(); 32457419a7fSflorian break; 32557419a7fSflorian #ifndef SMALL 32657419a7fSflorian case IMSG_CONTROLFD: 32757419a7fSflorian if ((fd = imsg.fd) == -1) 32857419a7fSflorian fatalx("%s: expected to receive imsg " 32957419a7fSflorian "control fd but didn't receive any", 33057419a7fSflorian __func__); 33157419a7fSflorian /* Listen on control socket. */ 33257419a7fSflorian control_listen(fd); 33357419a7fSflorian break; 33457419a7fSflorian case IMSG_CTL_END: 33557419a7fSflorian control_imsg_relay(&imsg); 33657419a7fSflorian break; 33757419a7fSflorian #endif /* SMALL */ 33857419a7fSflorian default: 33957419a7fSflorian log_debug("%s: error handling imsg %d", __func__, 34057419a7fSflorian imsg.hdr.type); 34157419a7fSflorian break; 34257419a7fSflorian } 34357419a7fSflorian imsg_free(&imsg); 34457419a7fSflorian } 34557419a7fSflorian if (!shut) 34657419a7fSflorian imsg_event_add(iev); 34757419a7fSflorian else { 34857419a7fSflorian /* This pipe is dead. Remove its event handler. */ 34957419a7fSflorian event_del(&iev->ev); 35057419a7fSflorian event_loopexit(NULL); 35157419a7fSflorian } 35257419a7fSflorian } 35357419a7fSflorian 35457419a7fSflorian void 35557419a7fSflorian frontend_dispatch_engine(int fd, short event, void *bula) 35657419a7fSflorian { 35757419a7fSflorian struct imsgev *iev = bula; 35857419a7fSflorian struct imsgbuf *ibuf = &iev->ibuf; 35957419a7fSflorian struct imsg imsg; 36057419a7fSflorian struct iface *iface; 36157419a7fSflorian ssize_t n; 36257419a7fSflorian int shut = 0; 36357419a7fSflorian 36457419a7fSflorian if (event & EV_READ) { 36557419a7fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 36657419a7fSflorian fatal("imsg_read error"); 36757419a7fSflorian if (n == 0) /* Connection closed. */ 36857419a7fSflorian shut = 1; 36957419a7fSflorian } 37057419a7fSflorian if (event & EV_WRITE) { 37157419a7fSflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 37257419a7fSflorian fatal("msgbuf_write"); 37357419a7fSflorian if (n == 0) /* Connection closed. */ 37457419a7fSflorian shut = 1; 37557419a7fSflorian } 37657419a7fSflorian 37757419a7fSflorian for (;;) { 37857419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 37957419a7fSflorian fatal("%s: imsg_get error", __func__); 38057419a7fSflorian if (n == 0) /* No more messages. */ 38157419a7fSflorian break; 38257419a7fSflorian 38357419a7fSflorian switch (imsg.hdr.type) { 38457419a7fSflorian #ifndef SMALL 38557419a7fSflorian case IMSG_CTL_END: 38657419a7fSflorian case IMSG_CTL_SHOW_INTERFACE_INFO: 38757419a7fSflorian control_imsg_relay(&imsg); 38857419a7fSflorian break; 38957419a7fSflorian #endif /* SMALL */ 39057419a7fSflorian case IMSG_SEND_DISCOVER: { 39157419a7fSflorian struct imsg_req_discover imsg_req_discover; 39257419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_discover)) 39357419a7fSflorian fatalx("%s: IMSG_SEND_DISCOVER wrong " 39457419a7fSflorian "length: %lu", __func__, 39557419a7fSflorian IMSG_DATA_SIZE(imsg)); 39657419a7fSflorian memcpy(&imsg_req_discover, imsg.data, 39757419a7fSflorian sizeof(imsg_req_discover)); 39857419a7fSflorian iface = get_iface_by_id(imsg_req_discover.if_index); 39957419a7fSflorian if (iface != NULL) { 40057419a7fSflorian iface->xid = imsg_req_discover.xid; 40157419a7fSflorian send_discover(iface); 40257419a7fSflorian } 40357419a7fSflorian break; 40457419a7fSflorian } 40557419a7fSflorian case IMSG_SEND_REQUEST: { 40657419a7fSflorian struct imsg_req_request imsg_req_request; 40757419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_request)) 40857419a7fSflorian fatalx("%s: IMSG_SEND_REQUEST wrong " 40957419a7fSflorian "length: %lu", __func__, 41057419a7fSflorian IMSG_DATA_SIZE(imsg)); 41157419a7fSflorian memcpy(&imsg_req_request, imsg.data, 41257419a7fSflorian sizeof(imsg_req_request)); 41357419a7fSflorian iface = get_iface_by_id(imsg_req_request.if_index); 41457419a7fSflorian if (iface != NULL) { 41557419a7fSflorian iface->xid = imsg_req_request.xid; 41657419a7fSflorian iface->requested_ip.s_addr = 41757419a7fSflorian imsg_req_request.requested_ip.s_addr; 41857419a7fSflorian iface->server_identifier.s_addr = 41957419a7fSflorian imsg_req_request.server_identifier.s_addr; 42057419a7fSflorian iface->dhcp_server.s_addr = 42157419a7fSflorian imsg_req_request.dhcp_server.s_addr; 42257419a7fSflorian send_request(iface); 42357419a7fSflorian } 42457419a7fSflorian break; 42557419a7fSflorian } 42657419a7fSflorian default: 42757419a7fSflorian log_debug("%s: error handling imsg %d", __func__, 42857419a7fSflorian imsg.hdr.type); 42957419a7fSflorian break; 43057419a7fSflorian } 43157419a7fSflorian imsg_free(&imsg); 43257419a7fSflorian } 43357419a7fSflorian if (!shut) 43457419a7fSflorian imsg_event_add(iev); 43557419a7fSflorian else { 43657419a7fSflorian /* This pipe is dead. Remove its event handler. */ 43757419a7fSflorian event_del(&iev->ev); 43857419a7fSflorian event_loopexit(NULL); 43957419a7fSflorian } 44057419a7fSflorian } 44157419a7fSflorian 44257419a7fSflorian int 44357419a7fSflorian get_flags(char *if_name) 44457419a7fSflorian { 44557419a7fSflorian struct ifreq ifr; 44657419a7fSflorian 44757419a7fSflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 44857419a7fSflorian if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) { 44957419a7fSflorian log_warn("SIOCGIFFLAGS"); 45057419a7fSflorian return -1; 45157419a7fSflorian } 45257419a7fSflorian return ifr.ifr_flags; 45357419a7fSflorian } 45457419a7fSflorian 45557419a7fSflorian int 45657419a7fSflorian get_xflags(char *if_name) 45757419a7fSflorian { 45857419a7fSflorian struct ifreq ifr; 45957419a7fSflorian 46057419a7fSflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 46157419a7fSflorian if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) { 46257419a7fSflorian log_warn("SIOCGIFXFLAGS"); 46357419a7fSflorian return -1; 46457419a7fSflorian } 46557419a7fSflorian return ifr.ifr_flags; 46657419a7fSflorian } 46757419a7fSflorian 46857419a7fSflorian void 46948e1b614Sflorian update_iface(struct if_msghdr *ifm, struct sockaddr_dl *sdl) 47057419a7fSflorian { 47157419a7fSflorian struct iface *iface; 4721bbf741cSflorian struct imsg_ifinfo ifinfo; 4731bbf741cSflorian uint32_t if_index; 47448e1b614Sflorian int flags, xflags; 4751bbf741cSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 47657419a7fSflorian 4771bbf741cSflorian if_index = ifm->ifm_index; 4781bbf741cSflorian 4791bbf741cSflorian flags = ifm->ifm_flags; 4801bbf741cSflorian xflags = ifm->ifm_xflags; 4811bbf741cSflorian 4821bbf741cSflorian iface = get_iface_by_id(if_index); 4831bbf741cSflorian if_name = if_indextoname(if_index, ifnamebuf); 4841bbf741cSflorian 4851bbf741cSflorian if (if_name == NULL) { 4861bbf741cSflorian if (iface != NULL) { 4871bbf741cSflorian log_debug("interface with idx %d removed", if_index); 4881bbf741cSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 4891bbf741cSflorian &if_index, sizeof(if_index)); 4901bbf741cSflorian remove_iface(if_index); 4911bbf741cSflorian } 49257419a7fSflorian return; 4931bbf741cSflorian } 49457419a7fSflorian 4951bbf741cSflorian if (!(xflags & IFXF_AUTOCONF4)) { 4961bbf741cSflorian if (iface != NULL) { 4971bbf741cSflorian log_info("Removed autoconf flag from %s", if_name); 4981bbf741cSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 4991bbf741cSflorian &if_index, sizeof(if_index)); 5001bbf741cSflorian remove_iface(if_index); 5011bbf741cSflorian } 50257419a7fSflorian return; 5031bbf741cSflorian } 50457419a7fSflorian 5051bbf741cSflorian memset(&ifinfo, 0, sizeof(ifinfo)); 5061bbf741cSflorian ifinfo.if_index = if_index; 5071bbf741cSflorian ifinfo.link_state = ifm->ifm_data.ifi_link_state; 5081bbf741cSflorian ifinfo.rdomain = ifm->ifm_tableid; 5091bbf741cSflorian ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == 5101bbf741cSflorian (IFF_UP | IFF_RUNNING); 51157419a7fSflorian 51248e1b614Sflorian if (sdl != NULL && sdl->sdl_type == IFT_ETHER && 51348e1b614Sflorian sdl->sdl_alen == ETHER_ADDR_LEN) 5141bbf741cSflorian memcpy(ifinfo.hw_address.ether_addr_octet, LLADDR(sdl), 5151bbf741cSflorian ETHER_ADDR_LEN); 51648e1b614Sflorian else if (iface == NULL) { 5171bbf741cSflorian log_warnx("Could not find AF_LINK address for %s.", if_name); 5181bbf741cSflorian return; 5194fd2b0e0Sflorian } 5201bbf741cSflorian 5211bbf741cSflorian if (iface == NULL) { 5224fd2b0e0Sflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 5234fd2b0e0Sflorian fatal("calloc"); 5244fd2b0e0Sflorian iface->udpsock = -1; 5254fd2b0e0Sflorian LIST_INSERT_HEAD(&interfaces, iface, entries); 5264fd2b0e0Sflorian frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0, 5274fd2b0e0Sflorian &if_index, sizeof(if_index)); 5281bbf741cSflorian } else { 5291bbf741cSflorian if (iface->ifinfo.rdomain != ifinfo.rdomain && 5301bbf741cSflorian iface->udpsock != -1) { 5311bbf741cSflorian close(iface->udpsock); 5321bbf741cSflorian iface->udpsock = -1; 5331bbf741cSflorian } 5344fd2b0e0Sflorian } 5354fd2b0e0Sflorian 5361bbf741cSflorian if (memcmp(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)) != 0) { 5371bbf741cSflorian memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); 5381bbf741cSflorian frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo, 5391bbf741cSflorian sizeof(iface->ifinfo)); 5401bbf741cSflorian } 54157419a7fSflorian } 54257419a7fSflorian 54357419a7fSflorian void 54457419a7fSflorian frontend_startup(void) 54557419a7fSflorian { 54657419a7fSflorian if (!event_initialized(&ev_route)) 54757419a7fSflorian fatalx("%s: did not receive a route socket from the main " 54857419a7fSflorian "process", __func__); 54957419a7fSflorian 5501bbf741cSflorian init_ifaces(); 55148e1b614Sflorian if (pledge("stdio unix recvfd", NULL) == -1) 55248e1b614Sflorian fatal("pledge"); 55357419a7fSflorian event_add(&ev_route, NULL); 5541bbf741cSflorian } 5551bbf741cSflorian 5561bbf741cSflorian void 5571bbf741cSflorian init_ifaces(void) 5581bbf741cSflorian { 5591bbf741cSflorian struct iface *iface; 5601bbf741cSflorian struct imsg_ifinfo ifinfo; 5611bbf741cSflorian struct if_nameindex *ifnidxp, *ifnidx; 5621bbf741cSflorian struct ifaddrs *ifap, *ifa; 5631bbf741cSflorian uint32_t if_index; 5641bbf741cSflorian int flags, xflags; 5651bbf741cSflorian char *if_name; 56657419a7fSflorian 56757419a7fSflorian if ((ifnidxp = if_nameindex()) == NULL) 56857419a7fSflorian fatalx("if_nameindex"); 56957419a7fSflorian 5701bbf741cSflorian if (getifaddrs(&ifap) != 0) 5711bbf741cSflorian fatal("getifaddrs"); 57257419a7fSflorian 5731bbf741cSflorian for (ifnidx = ifnidxp; ifnidx->if_index != 0 && ifnidx->if_name != NULL; 5741bbf741cSflorian ifnidx++) { 5751bbf741cSflorian if_index = ifnidx->if_index; 5761bbf741cSflorian if_name = ifnidx->if_name; 5771bbf741cSflorian if ((flags = get_flags(if_name)) == -1) 5781bbf741cSflorian continue; 5791bbf741cSflorian if ((xflags = get_xflags(if_name)) == -1) 5801bbf741cSflorian continue; 5811bbf741cSflorian if (!(xflags & IFXF_AUTOCONF4)) 5821bbf741cSflorian continue; 5831bbf741cSflorian 5841bbf741cSflorian memset(&ifinfo, 0, sizeof(ifinfo)); 5851bbf741cSflorian ifinfo.if_index = if_index; 5861bbf741cSflorian ifinfo.link_state = -1; 5871bbf741cSflorian ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == 5881bbf741cSflorian (IFF_UP | IFF_RUNNING); 5891bbf741cSflorian 5901bbf741cSflorian for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 5911bbf741cSflorian if (strcmp(if_name, ifa->ifa_name) != 0) 5921bbf741cSflorian continue; 5931bbf741cSflorian if (ifa->ifa_addr == NULL) 5941bbf741cSflorian continue; 5951bbf741cSflorian 5961bbf741cSflorian switch (ifa->ifa_addr->sa_family) { 5971bbf741cSflorian case AF_LINK: { 5981bbf741cSflorian struct if_data *if_data; 5991bbf741cSflorian struct sockaddr_dl *sdl; 6001bbf741cSflorian 6011bbf741cSflorian sdl = (struct sockaddr_dl *)ifa->ifa_addr; 6021bbf741cSflorian if (sdl->sdl_type != IFT_ETHER || 6031bbf741cSflorian sdl->sdl_alen != ETHER_ADDR_LEN) 6041bbf741cSflorian continue; 6051bbf741cSflorian memcpy(ifinfo.hw_address.ether_addr_octet, 6061bbf741cSflorian LLADDR(sdl), ETHER_ADDR_LEN); 6071bbf741cSflorian 6081bbf741cSflorian if_data = (struct if_data *)ifa->ifa_data; 6091bbf741cSflorian ifinfo.link_state = if_data->ifi_link_state; 6101bbf741cSflorian ifinfo.rdomain = if_data->ifi_rdomain; 6111bbf741cSflorian goto out; 6121bbf741cSflorian } 6131bbf741cSflorian default: 6141bbf741cSflorian break; 6151bbf741cSflorian } 6161bbf741cSflorian } 6171bbf741cSflorian out: 6181bbf741cSflorian if (ifinfo.link_state == -1) 6191bbf741cSflorian /* no AF_LINK found */ 6201bbf741cSflorian continue; 6211bbf741cSflorian 6221bbf741cSflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 6231bbf741cSflorian fatal("calloc"); 6241bbf741cSflorian iface->udpsock = -1; 6251bbf741cSflorian memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); 6261bbf741cSflorian LIST_INSERT_HEAD(&interfaces, iface, entries); 6271bbf741cSflorian frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0, 6281bbf741cSflorian &if_index, sizeof(if_index)); 6291bbf741cSflorian frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo, 6301bbf741cSflorian sizeof(iface->ifinfo)); 6311bbf741cSflorian } 6321bbf741cSflorian 6331bbf741cSflorian freeifaddrs(ifap); 63457419a7fSflorian if_freenameindex(ifnidxp); 63557419a7fSflorian } 63657419a7fSflorian 63757419a7fSflorian void 63857419a7fSflorian route_receive(int fd, short events, void *arg) 63957419a7fSflorian { 64057419a7fSflorian static uint8_t *buf; 64157419a7fSflorian 64257419a7fSflorian struct rt_msghdr *rtm; 64357419a7fSflorian struct sockaddr *sa, *rti_info[RTAX_MAX]; 64457419a7fSflorian ssize_t n; 64557419a7fSflorian 64657419a7fSflorian if (buf == NULL) { 64757419a7fSflorian buf = malloc(ROUTE_SOCKET_BUF_SIZE); 64857419a7fSflorian if (buf == NULL) 64957419a7fSflorian fatal("malloc"); 65057419a7fSflorian } 65157419a7fSflorian rtm = (struct rt_msghdr *)buf; 65257419a7fSflorian if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) { 65357419a7fSflorian if (errno == EAGAIN || errno == EINTR) 65457419a7fSflorian return; 65557419a7fSflorian log_warn("dispatch_rtmsg: read error"); 65657419a7fSflorian return; 65757419a7fSflorian } 65857419a7fSflorian 65957419a7fSflorian if (n == 0) 66057419a7fSflorian fatal("routing socket closed"); 66157419a7fSflorian 66257419a7fSflorian if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) { 66357419a7fSflorian log_warnx("partial rtm of %zd in buffer", n); 66457419a7fSflorian return; 66557419a7fSflorian } 66657419a7fSflorian 66757419a7fSflorian if (rtm->rtm_version != RTM_VERSION) 66857419a7fSflorian return; 66957419a7fSflorian 67057419a7fSflorian sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen); 67157419a7fSflorian get_rtaddrs(rtm->rtm_addrs, sa, rti_info); 67257419a7fSflorian 67357419a7fSflorian handle_route_message(rtm, rti_info); 67457419a7fSflorian } 67557419a7fSflorian 67657419a7fSflorian void 67757419a7fSflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info) 67857419a7fSflorian { 67948e1b614Sflorian struct sockaddr_dl *sdl = NULL; 68057419a7fSflorian switch (rtm->rtm_type) { 68157419a7fSflorian case RTM_IFINFO: 68248e1b614Sflorian if (rtm->rtm_addrs & RTA_IFP && rti_info[RTAX_IFP]->sa_family 68348e1b614Sflorian == AF_LINK) 68448e1b614Sflorian sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP]; 68548e1b614Sflorian update_iface((struct if_msghdr *)rtm, sdl); 68657419a7fSflorian break; 68757419a7fSflorian case RTM_PROPOSAL: 68857419a7fSflorian if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) { 68957419a7fSflorian log_debug("RTP_PROPOSAL_SOLICIT"); 69057419a7fSflorian frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS, 69157419a7fSflorian 0, 0, NULL, 0); 69257419a7fSflorian } 69357419a7fSflorian break; 69457419a7fSflorian default: 69557419a7fSflorian log_debug("unexpected RTM: %d", rtm->rtm_type); 69657419a7fSflorian break; 69757419a7fSflorian } 69857419a7fSflorian } 69957419a7fSflorian 70057419a7fSflorian #define ROUNDUP(a) \ 70157419a7fSflorian ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 70257419a7fSflorian 70357419a7fSflorian void 70457419a7fSflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 70557419a7fSflorian { 70657419a7fSflorian int i; 70757419a7fSflorian 70857419a7fSflorian for (i = 0; i < RTAX_MAX; i++) { 70957419a7fSflorian if (addrs & (1 << i)) { 71057419a7fSflorian rti_info[i] = sa; 71157419a7fSflorian sa = (struct sockaddr *)((char *)(sa) + 71257419a7fSflorian ROUNDUP(sa->sa_len)); 71357419a7fSflorian } else 71457419a7fSflorian rti_info[i] = NULL; 71557419a7fSflorian } 71657419a7fSflorian } 71757419a7fSflorian 71857419a7fSflorian void 71957419a7fSflorian bpf_receive(int fd, short events, void *arg) 72057419a7fSflorian { 72157419a7fSflorian struct bpf_hdr *hdr; 72257419a7fSflorian struct imsg_dhcp imsg_dhcp; 72357419a7fSflorian struct iface *iface; 72457419a7fSflorian ssize_t len, rem; 72557419a7fSflorian uint8_t *p; 72657419a7fSflorian 72757419a7fSflorian iface = (struct iface *)arg; 72857419a7fSflorian 72957419a7fSflorian if ((len = read(fd, iface->bpfev.buf, BPFLEN)) == -1) { 73057419a7fSflorian log_warn("read"); 73157419a7fSflorian return; 73257419a7fSflorian } 733e998cdbeSflorian 734e998cdbeSflorian if (len == 0) 735e998cdbeSflorian fatal("%s len == 0", __func__); 73657419a7fSflorian 73757419a7fSflorian memset(&imsg_dhcp, 0, sizeof(imsg_dhcp)); 7381bbf741cSflorian imsg_dhcp.if_index = iface->ifinfo.if_index; 73957419a7fSflorian 74057419a7fSflorian rem = len; 74157419a7fSflorian p = iface->bpfev.buf; 74257419a7fSflorian 74357419a7fSflorian while (rem > 0) { 74457419a7fSflorian if ((size_t)rem < sizeof(*hdr)) { 74557419a7fSflorian log_warnx("packet too short"); 74657419a7fSflorian return; 74757419a7fSflorian } 74857419a7fSflorian hdr = (struct bpf_hdr *)p; 74957419a7fSflorian if (hdr->bh_caplen != hdr->bh_datalen) { 75057419a7fSflorian log_warnx("skipping truncated packet"); 75157419a7fSflorian goto cont; 75257419a7fSflorian } 75357419a7fSflorian if (rem < hdr->bh_hdrlen + hdr->bh_caplen) 75457419a7fSflorian /* we are done */ 75557419a7fSflorian break; 75657419a7fSflorian if (hdr->bh_caplen > sizeof(imsg_dhcp.packet)) { 75757419a7fSflorian log_warn("packet too big"); 75857419a7fSflorian goto cont; 75957419a7fSflorian } 76057419a7fSflorian memcpy(&imsg_dhcp.packet, p + hdr->bh_hdrlen, hdr->bh_caplen); 76157419a7fSflorian imsg_dhcp.len = hdr->bh_caplen; 76257419a7fSflorian frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp, 76357419a7fSflorian sizeof(imsg_dhcp)); 76457419a7fSflorian cont: 76557419a7fSflorian p += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 76657419a7fSflorian rem -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 76757419a7fSflorian 76857419a7fSflorian } 76957419a7fSflorian } 77057419a7fSflorian 77157419a7fSflorian ssize_t 77257419a7fSflorian build_packet(uint8_t message_type, uint32_t xid, struct ether_addr *hw_address, 77357419a7fSflorian struct in_addr *requested_ip, struct in_addr *server_identifier) 77457419a7fSflorian { 77557419a7fSflorian static uint8_t dhcp_cookie[] = DHCP_COOKIE; 77657419a7fSflorian static uint8_t dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1, 77757419a7fSflorian DHCPDISCOVER}; 77857419a7fSflorian static uint8_t dhcp_hostname[255] = {DHO_HOST_NAME, 0 /*, ... */}; 77957419a7fSflorian static uint8_t dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7, 78057419a7fSflorian HTYPE_ETHER, 0, 0, 0, 0, 0, 0}; 78157419a7fSflorian static uint8_t dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST, 782*ea86c963Sflorian 8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS, 78357419a7fSflorian DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS, 784*ea86c963Sflorian DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES}; 78557419a7fSflorian static uint8_t dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS, 78657419a7fSflorian 4, 0, 0, 0, 0}; 78757419a7fSflorian static uint8_t dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER, 78857419a7fSflorian 4, 0, 0, 0, 0}; 78957419a7fSflorian struct dhcp_hdr *hdr; 7902d2edb0eSflorian ssize_t len; 79157419a7fSflorian uint8_t *p; 79257419a7fSflorian char *c; 79357419a7fSflorian 79457419a7fSflorian memset(dhcp_packet, 0, sizeof(dhcp_packet)); 79557419a7fSflorian dhcp_message_type[2] = message_type; 79657419a7fSflorian p = dhcp_packet; 79757419a7fSflorian hdr = (struct dhcp_hdr *)p; 79857419a7fSflorian hdr->op = DHCP_BOOTREQUEST; 79957419a7fSflorian hdr->htype = HTYPE_ETHER; 80057419a7fSflorian hdr->hlen = 6; 80157419a7fSflorian hdr->hops = 0; 80257419a7fSflorian hdr->xid = xid; 80357419a7fSflorian hdr->secs = 0; 80457419a7fSflorian memcpy(hdr->chaddr, hw_address, sizeof(*hw_address)); 80557419a7fSflorian p += sizeof(struct dhcp_hdr); 80657419a7fSflorian memcpy(p, dhcp_cookie, sizeof(dhcp_cookie)); 80757419a7fSflorian p += sizeof(dhcp_cookie); 80857419a7fSflorian memcpy(p, dhcp_message_type, sizeof(dhcp_message_type)); 80957419a7fSflorian p += sizeof(dhcp_message_type); 81057419a7fSflorian if (gethostname(dhcp_hostname + 2, sizeof(dhcp_hostname) - 2) == 0) { 81157419a7fSflorian if ((c = strchr(dhcp_hostname + 2, '.')) != NULL) 81257419a7fSflorian *c = '\0'; 81357419a7fSflorian dhcp_hostname[1] = strlen(dhcp_hostname + 2); 81457419a7fSflorian memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2); 81557419a7fSflorian p += dhcp_hostname[1] + 2; 81657419a7fSflorian } 81757419a7fSflorian memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address)); 81857419a7fSflorian memcpy(p, dhcp_client_id, sizeof(dhcp_client_id)); 81957419a7fSflorian p += sizeof(dhcp_client_id); 82057419a7fSflorian memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); 82157419a7fSflorian p += sizeof(dhcp_req_list); 82257419a7fSflorian 82357419a7fSflorian if (message_type == DHCPREQUEST) { 82457419a7fSflorian memcpy(dhcp_requested_address + 2, requested_ip, 82557419a7fSflorian sizeof(*requested_ip)); 82657419a7fSflorian memcpy(p, dhcp_requested_address, 82757419a7fSflorian sizeof(dhcp_requested_address)); 82857419a7fSflorian p += sizeof(dhcp_requested_address); 82957419a7fSflorian 83057419a7fSflorian if (server_identifier->s_addr != INADDR_ANY) { 83157419a7fSflorian memcpy(dhcp_server_identifier + 2, server_identifier, 83257419a7fSflorian sizeof(*server_identifier)); 83357419a7fSflorian memcpy(p, dhcp_server_identifier, 83457419a7fSflorian sizeof(dhcp_server_identifier)); 83557419a7fSflorian p += sizeof(dhcp_server_identifier); 83657419a7fSflorian } 83757419a7fSflorian } 83857419a7fSflorian 83957419a7fSflorian *p = DHO_END; 84057419a7fSflorian p += 1; 84157419a7fSflorian 8422d2edb0eSflorian len = p - dhcp_packet; 8432d2edb0eSflorian 8442d2edb0eSflorian /* dhcp_packet is initialized with DHO_PADs */ 8452d2edb0eSflorian if (len < BOOTP_MIN_LEN) 8462d2edb0eSflorian len = BOOTP_MIN_LEN; 8472d2edb0eSflorian 8482d2edb0eSflorian return (len); 84957419a7fSflorian } 85057419a7fSflorian 85157419a7fSflorian void 85257419a7fSflorian send_discover(struct iface *iface) 85357419a7fSflorian { 85457419a7fSflorian ssize_t pkt_len; 855e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 85657419a7fSflorian 85757419a7fSflorian if (!event_initialized(&iface->bpfev.ev)) { 85857419a7fSflorian iface->send_discover = 1; 85957419a7fSflorian return; 86057419a7fSflorian } 86157419a7fSflorian iface->send_discover = 0; 862e998cdbeSflorian 8631bbf741cSflorian if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf); 864e998cdbeSflorian log_debug("DHCPDISCOVER on %s", if_name == NULL ? "?" : if_name); 865e998cdbeSflorian 8661bbf741cSflorian pkt_len = build_packet(DHCPDISCOVER, iface->xid, 8671bbf741cSflorian &iface->ifinfo.hw_address, &iface->requested_ip, NULL); 86857419a7fSflorian bpf_send_packet(iface, dhcp_packet, pkt_len); 86957419a7fSflorian } 87057419a7fSflorian 87157419a7fSflorian void 87257419a7fSflorian send_request(struct iface *iface) 87357419a7fSflorian { 87457419a7fSflorian ssize_t pkt_len; 875e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 876e998cdbeSflorian 8771bbf741cSflorian if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf); 878e998cdbeSflorian log_debug("DHCPREQUEST on %s", if_name == NULL ? "?" : if_name); 87957419a7fSflorian 8801bbf741cSflorian pkt_len = build_packet(DHCPREQUEST, iface->xid, 8811bbf741cSflorian &iface->ifinfo.hw_address, &iface->requested_ip, 8821bbf741cSflorian &iface->server_identifier); 88357419a7fSflorian if (iface->dhcp_server.s_addr != INADDR_ANY) 88457419a7fSflorian udp_send_packet(iface, dhcp_packet, pkt_len); 88557419a7fSflorian else 88657419a7fSflorian bpf_send_packet(iface, dhcp_packet, pkt_len); 88757419a7fSflorian } 88857419a7fSflorian 88957419a7fSflorian void 89057419a7fSflorian udp_send_packet(struct iface *iface, uint8_t *packet, ssize_t len) 89157419a7fSflorian { 89257419a7fSflorian struct sockaddr_in to; 89357419a7fSflorian 89457419a7fSflorian memset(&to, 0, sizeof(to)); 89557419a7fSflorian to.sin_family = AF_INET; 89657419a7fSflorian to.sin_len = sizeof(to); 89757419a7fSflorian to.sin_addr.s_addr = iface->dhcp_server.s_addr; 89857419a7fSflorian to.sin_port = ntohs(SERVER_PORT); 89957419a7fSflorian 90057419a7fSflorian if (sendto(iface->udpsock, packet, len, 0, (struct sockaddr *)&to, 90157419a7fSflorian sizeof(to)) == -1) 90257419a7fSflorian log_warn("sendto"); 90357419a7fSflorian } 90457419a7fSflorian void 90557419a7fSflorian bpf_send_packet(struct iface *iface, uint8_t *packet, ssize_t len) 90657419a7fSflorian { 90757419a7fSflorian struct iovec iov[4]; 90857419a7fSflorian struct ether_header eh; 90957419a7fSflorian struct ip ip; 91057419a7fSflorian struct udphdr udp; 91157419a7fSflorian ssize_t total, result; 91257419a7fSflorian int iovcnt = 0, i; 91357419a7fSflorian 91457419a7fSflorian memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost)); 9151bbf741cSflorian memcpy(eh.ether_shost, &iface->ifinfo.hw_address, 9161bbf741cSflorian sizeof(eh.ether_dhost)); 91757419a7fSflorian eh.ether_type = htons(ETHERTYPE_IP); 91857419a7fSflorian iov[0].iov_base = &eh; 91957419a7fSflorian iov[0].iov_len = sizeof(eh); 92057419a7fSflorian iovcnt++; 92157419a7fSflorian 92257419a7fSflorian ip.ip_v = 4; 92357419a7fSflorian ip.ip_hl = 5; 92457419a7fSflorian ip.ip_tos = IPTOS_LOWDELAY; 92557419a7fSflorian ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); 92657419a7fSflorian ip.ip_id = 0; 92757419a7fSflorian ip.ip_off = 0; 92857419a7fSflorian ip.ip_ttl = 128; 92957419a7fSflorian ip.ip_p = IPPROTO_UDP; 93057419a7fSflorian ip.ip_sum = 0; 93157419a7fSflorian ip.ip_src.s_addr = 0; 93257419a7fSflorian ip.ip_dst.s_addr = INADDR_BROADCAST; 93357419a7fSflorian ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); 93457419a7fSflorian iov[iovcnt].iov_base = &ip; 93557419a7fSflorian iov[iovcnt].iov_len = sizeof(ip); 93657419a7fSflorian iovcnt++; 93757419a7fSflorian 93857419a7fSflorian udp.uh_sport = htons(CLIENT_PORT); 93957419a7fSflorian udp.uh_dport = htons(SERVER_PORT); 94057419a7fSflorian udp.uh_ulen = htons(sizeof(udp) + len); 94157419a7fSflorian udp.uh_sum = 0; 94257419a7fSflorian udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), 94357419a7fSflorian checksum((unsigned char *)packet, len, 94457419a7fSflorian checksum((unsigned char *)&ip.ip_src, 94557419a7fSflorian 2 * sizeof(ip.ip_src), 94657419a7fSflorian IPPROTO_UDP + (uint32_t)ntohs(udp.uh_ulen))))); 94757419a7fSflorian iov[iovcnt].iov_base = &udp; 94857419a7fSflorian iov[iovcnt].iov_len = sizeof(udp); 94957419a7fSflorian iovcnt++; 95057419a7fSflorian 95157419a7fSflorian iov[iovcnt].iov_base = packet; 95257419a7fSflorian iov[iovcnt].iov_len = len; 95357419a7fSflorian iovcnt++; 95457419a7fSflorian 95557419a7fSflorian total = 0; 95657419a7fSflorian for (i = 0; i < iovcnt; i++) 95757419a7fSflorian total += iov[i].iov_len; 95857419a7fSflorian 95957419a7fSflorian result = writev(EVENT_FD(&iface->bpfev.ev), iov, iovcnt); 96057419a7fSflorian if (result == -1) 96157419a7fSflorian log_warn("%s: writev", __func__); 96257419a7fSflorian else if (result < total) { 96357419a7fSflorian log_warnx("%s, writev: %zd of %zd bytes", __func__, result, 96457419a7fSflorian total); 96557419a7fSflorian } 96657419a7fSflorian } 96757419a7fSflorian 96857419a7fSflorian struct iface* 96957419a7fSflorian get_iface_by_id(uint32_t if_index) 97057419a7fSflorian { 97157419a7fSflorian struct iface *iface; 97257419a7fSflorian 97357419a7fSflorian LIST_FOREACH (iface, &interfaces, entries) { 9741bbf741cSflorian if (iface->ifinfo.if_index == if_index) 97557419a7fSflorian return (iface); 97657419a7fSflorian } 97757419a7fSflorian 97857419a7fSflorian return (NULL); 97957419a7fSflorian } 98057419a7fSflorian 98157419a7fSflorian void 98257419a7fSflorian remove_iface(uint32_t if_index) 98357419a7fSflorian { 98457419a7fSflorian struct iface *iface; 98557419a7fSflorian 98657419a7fSflorian iface = get_iface_by_id(if_index); 98757419a7fSflorian 98857419a7fSflorian if (iface == NULL) 98957419a7fSflorian return; 99057419a7fSflorian 99157419a7fSflorian LIST_REMOVE(iface, entries); 99257419a7fSflorian event_del(&iface->bpfev.ev); 99357419a7fSflorian close(EVENT_FD(&iface->bpfev.ev)); 99457419a7fSflorian if (iface->udpsock != -1) 99557419a7fSflorian close(iface->udpsock); 99657419a7fSflorian free(iface); 99757419a7fSflorian } 99857419a7fSflorian 99957419a7fSflorian void 100057419a7fSflorian set_bpfsock(int bpfsock, uint32_t if_index) 100157419a7fSflorian { 100257419a7fSflorian struct iface *iface; 100357419a7fSflorian 100457419a7fSflorian if ((iface = get_iface_by_id(if_index)) == NULL) { 100557419a7fSflorian /* 100657419a7fSflorian * The interface disappeared while we were waiting for the 100757419a7fSflorian * parent process to open the raw socket. 100857419a7fSflorian */ 100957419a7fSflorian close(bpfsock); 101057419a7fSflorian } else { 101157419a7fSflorian event_set(&iface->bpfev.ev, bpfsock, EV_READ | 101257419a7fSflorian EV_PERSIST, bpf_receive, iface); 101357419a7fSflorian event_add(&iface->bpfev.ev, NULL); 101457419a7fSflorian if (iface->send_discover) 101557419a7fSflorian send_discover(iface); 101657419a7fSflorian } 101757419a7fSflorian } 1018