1*e998cdbeSflorian /* $OpenBSD: frontend.c,v 1.3 2021/03/07 18:39:11 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 6057419a7fSflorian 6157419a7fSflorian struct bpf_ev { 6257419a7fSflorian struct event ev; 6357419a7fSflorian uint8_t buf[BPFLEN]; 6457419a7fSflorian }; 6557419a7fSflorian 6657419a7fSflorian struct iface { 6757419a7fSflorian LIST_ENTRY(iface) entries; 6857419a7fSflorian struct bpf_ev bpfev; 6957419a7fSflorian struct ether_addr hw_address; 7057419a7fSflorian uint32_t if_index; 7157419a7fSflorian int rdomain; 7257419a7fSflorian int send_discover; 7357419a7fSflorian uint32_t xid; 7457419a7fSflorian struct in_addr requested_ip; 7557419a7fSflorian struct in_addr server_identifier; 7657419a7fSflorian struct in_addr dhcp_server; 7757419a7fSflorian int udpsock; 7857419a7fSflorian }; 7957419a7fSflorian 8057419a7fSflorian __dead void frontend_shutdown(void); 8157419a7fSflorian void frontend_sig_handler(int, short, void *); 8257419a7fSflorian void update_iface(uint32_t, char*); 8357419a7fSflorian void frontend_startup(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 int get_ifrdomain(char *); 9157419a7fSflorian struct iface *get_iface_by_id(uint32_t); 9257419a7fSflorian void remove_iface(uint32_t); 9357419a7fSflorian void set_bpfsock(int, uint32_t); 9457419a7fSflorian ssize_t build_packet(uint8_t, uint32_t, struct ether_addr *, struct 9557419a7fSflorian in_addr *, struct in_addr *); 9657419a7fSflorian void send_discover(struct iface *); 9757419a7fSflorian void send_request(struct iface *); 9857419a7fSflorian void bpf_send_packet(struct iface *, uint8_t *, ssize_t); 9957419a7fSflorian void udp_send_packet(struct iface *, uint8_t *, ssize_t); 10057419a7fSflorian 10157419a7fSflorian LIST_HEAD(, iface) interfaces; 10257419a7fSflorian static struct imsgev *iev_main; 10357419a7fSflorian static struct imsgev *iev_engine; 10457419a7fSflorian struct event ev_route; 10557419a7fSflorian int ioctlsock; 10657419a7fSflorian 10757419a7fSflorian uint8_t dhcp_packet[1500]; 10857419a7fSflorian 10957419a7fSflorian void 11057419a7fSflorian frontend_sig_handler(int sig, short event, void *bula) 11157419a7fSflorian { 11257419a7fSflorian /* 11357419a7fSflorian * Normal signal handler rules don't apply because libevent 11457419a7fSflorian * decouples for us. 11557419a7fSflorian */ 11657419a7fSflorian 11757419a7fSflorian switch (sig) { 11857419a7fSflorian case SIGINT: 11957419a7fSflorian case SIGTERM: 12057419a7fSflorian frontend_shutdown(); 12157419a7fSflorian default: 12257419a7fSflorian fatalx("unexpected signal"); 12357419a7fSflorian } 12457419a7fSflorian } 12557419a7fSflorian 12657419a7fSflorian void 12757419a7fSflorian frontend(int debug, int verbose) 12857419a7fSflorian { 12957419a7fSflorian struct event ev_sigint, ev_sigterm; 13057419a7fSflorian struct passwd *pw; 13157419a7fSflorian 13257419a7fSflorian log_init(debug, LOG_DAEMON); 13357419a7fSflorian log_setverbose(verbose); 13457419a7fSflorian 13557419a7fSflorian if ((pw = getpwnam(DHCPLEASED_USER)) == NULL) 13657419a7fSflorian fatal("getpwnam"); 13757419a7fSflorian 13857419a7fSflorian if (chroot(pw->pw_dir) == -1) 13957419a7fSflorian fatal("chroot"); 14057419a7fSflorian if (chdir("/") == -1) 14157419a7fSflorian fatal("chdir(\"/\")"); 14257419a7fSflorian 14357419a7fSflorian setproctitle("%s", "frontend"); 14457419a7fSflorian log_procinit("frontend"); 14557419a7fSflorian 14657419a7fSflorian if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) 14757419a7fSflorian fatal("socket"); 14857419a7fSflorian 14957419a7fSflorian if (setgroups(1, &pw->pw_gid) || 15057419a7fSflorian setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 15157419a7fSflorian setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 15257419a7fSflorian fatal("can't drop privileges"); 15357419a7fSflorian 15457419a7fSflorian if (pledge("stdio unix recvfd route", NULL) == -1) 15557419a7fSflorian fatal("pledge"); 15657419a7fSflorian event_init(); 15757419a7fSflorian 15857419a7fSflorian /* Setup signal handler. */ 15957419a7fSflorian signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL); 16057419a7fSflorian signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL); 16157419a7fSflorian signal_add(&ev_sigint, NULL); 16257419a7fSflorian signal_add(&ev_sigterm, NULL); 16357419a7fSflorian signal(SIGPIPE, SIG_IGN); 16457419a7fSflorian signal(SIGHUP, SIG_IGN); 16557419a7fSflorian 16657419a7fSflorian /* Setup pipe and event handler to the parent process. */ 16757419a7fSflorian if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) 16857419a7fSflorian fatal(NULL); 16957419a7fSflorian imsg_init(&iev_main->ibuf, 3); 17057419a7fSflorian iev_main->handler = frontend_dispatch_main; 17157419a7fSflorian iev_main->events = EV_READ; 17257419a7fSflorian event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, 17357419a7fSflorian iev_main->handler, iev_main); 17457419a7fSflorian event_add(&iev_main->ev, NULL); 17557419a7fSflorian 17657419a7fSflorian LIST_INIT(&interfaces); 17757419a7fSflorian event_dispatch(); 17857419a7fSflorian 17957419a7fSflorian frontend_shutdown(); 18057419a7fSflorian } 18157419a7fSflorian 18257419a7fSflorian __dead void 18357419a7fSflorian frontend_shutdown(void) 18457419a7fSflorian { 18557419a7fSflorian /* Close pipes. */ 18657419a7fSflorian msgbuf_write(&iev_engine->ibuf.w); 18757419a7fSflorian msgbuf_clear(&iev_engine->ibuf.w); 18857419a7fSflorian close(iev_engine->ibuf.fd); 18957419a7fSflorian msgbuf_write(&iev_main->ibuf.w); 19057419a7fSflorian msgbuf_clear(&iev_main->ibuf.w); 19157419a7fSflorian close(iev_main->ibuf.fd); 19257419a7fSflorian 19357419a7fSflorian free(iev_engine); 19457419a7fSflorian free(iev_main); 19557419a7fSflorian 19657419a7fSflorian log_info("frontend exiting"); 19757419a7fSflorian exit(0); 19857419a7fSflorian } 19957419a7fSflorian 20057419a7fSflorian int 20157419a7fSflorian frontend_imsg_compose_main(int type, pid_t pid, void *data, 20257419a7fSflorian uint16_t datalen) 20357419a7fSflorian { 20457419a7fSflorian return (imsg_compose_event(iev_main, type, 0, pid, -1, data, 20557419a7fSflorian datalen)); 20657419a7fSflorian } 20757419a7fSflorian 20857419a7fSflorian int 20957419a7fSflorian frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid, 21057419a7fSflorian void *data, uint16_t datalen) 21157419a7fSflorian { 21257419a7fSflorian return (imsg_compose_event(iev_engine, type, peerid, pid, -1, 21357419a7fSflorian data, datalen)); 21457419a7fSflorian } 21557419a7fSflorian 21657419a7fSflorian void 21757419a7fSflorian frontend_dispatch_main(int fd, short event, void *bula) 21857419a7fSflorian { 21957419a7fSflorian struct imsg imsg; 22057419a7fSflorian struct imsgev *iev = bula; 22157419a7fSflorian struct imsgbuf *ibuf = &iev->ibuf; 22257419a7fSflorian struct iface *iface; 22357419a7fSflorian ssize_t n; 22457419a7fSflorian int shut = 0, bpfsock, if_index, udpsock; 22557419a7fSflorian 22657419a7fSflorian if (event & EV_READ) { 22757419a7fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 22857419a7fSflorian fatal("imsg_read error"); 22957419a7fSflorian if (n == 0) /* Connection closed. */ 23057419a7fSflorian shut = 1; 23157419a7fSflorian } 23257419a7fSflorian if (event & EV_WRITE) { 23357419a7fSflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 23457419a7fSflorian fatal("msgbuf_write"); 23557419a7fSflorian if (n == 0) /* Connection closed. */ 23657419a7fSflorian shut = 1; 23757419a7fSflorian } 23857419a7fSflorian 23957419a7fSflorian for (;;) { 24057419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 24157419a7fSflorian fatal("%s: imsg_get error", __func__); 24257419a7fSflorian if (n == 0) /* No more messages. */ 24357419a7fSflorian break; 24457419a7fSflorian 24557419a7fSflorian switch (imsg.hdr.type) { 24657419a7fSflorian case IMSG_SOCKET_IPC: 24757419a7fSflorian /* 24857419a7fSflorian * Setup pipe and event handler to the engine 24957419a7fSflorian * process. 25057419a7fSflorian */ 25157419a7fSflorian if (iev_engine) 25257419a7fSflorian fatalx("%s: received unexpected imsg fd " 25357419a7fSflorian "to frontend", __func__); 25457419a7fSflorian 25557419a7fSflorian if ((fd = imsg.fd) == -1) 25657419a7fSflorian fatalx("%s: expected to receive imsg fd to " 25757419a7fSflorian "frontend but didn't receive any", 25857419a7fSflorian __func__); 25957419a7fSflorian 26057419a7fSflorian iev_engine = malloc(sizeof(struct imsgev)); 26157419a7fSflorian if (iev_engine == NULL) 26257419a7fSflorian fatal(NULL); 26357419a7fSflorian 26457419a7fSflorian imsg_init(&iev_engine->ibuf, fd); 26557419a7fSflorian iev_engine->handler = frontend_dispatch_engine; 26657419a7fSflorian iev_engine->events = EV_READ; 26757419a7fSflorian 26857419a7fSflorian event_set(&iev_engine->ev, iev_engine->ibuf.fd, 26957419a7fSflorian iev_engine->events, iev_engine->handler, iev_engine); 27057419a7fSflorian event_add(&iev_engine->ev, NULL); 27157419a7fSflorian break; 27257419a7fSflorian case IMSG_BPFSOCK: 27357419a7fSflorian if ((bpfsock = imsg.fd) == -1) 27457419a7fSflorian fatalx("%s: expected to receive imsg " 27557419a7fSflorian "bpf fd but didn't receive any", 27657419a7fSflorian __func__); 27757419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 27857419a7fSflorian fatalx("%s: IMSG_BPFSOCK wrong length: " 27957419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 28057419a7fSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 28157419a7fSflorian set_bpfsock(bpfsock, if_index); 28257419a7fSflorian break; 28357419a7fSflorian case IMSG_UDPSOCK: 28457419a7fSflorian if ((udpsock = imsg.fd) == -1) 28557419a7fSflorian fatalx("%s: expected to receive imsg " 28657419a7fSflorian "udpsocket fd but didn't receive any", 28757419a7fSflorian __func__); 28857419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 28957419a7fSflorian fatalx("%s: IMSG_UDPSOCK wrong length: " 29057419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 29157419a7fSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 29257419a7fSflorian if ((iface = get_iface_by_id(if_index)) == NULL) { 29357419a7fSflorian close(fd); 29457419a7fSflorian break; 29557419a7fSflorian } 29657419a7fSflorian if (iface->udpsock != -1) 29757419a7fSflorian fatalx("%s: received unexpected udpsocket", 29857419a7fSflorian __func__); 29957419a7fSflorian iface->udpsock = udpsock; 30057419a7fSflorian break; 30157419a7fSflorian case IMSG_CLOSE_UDPSOCK: 30257419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 30357419a7fSflorian fatalx("%s: IMSG_UDPSOCK wrong length: " 30457419a7fSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 30557419a7fSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 30657419a7fSflorian if ((iface = get_iface_by_id(if_index)) != NULL && 30757419a7fSflorian iface->udpsock != -1) { 30857419a7fSflorian close(iface->udpsock); 30957419a7fSflorian iface->udpsock = -1; 31057419a7fSflorian } 31157419a7fSflorian break; 31257419a7fSflorian case IMSG_ROUTESOCK: 31357419a7fSflorian if ((fd = imsg.fd) == -1) 31457419a7fSflorian fatalx("%s: expected to receive imsg " 31557419a7fSflorian "routesocket fd but didn't receive any", 31657419a7fSflorian __func__); 31757419a7fSflorian event_set(&ev_route, fd, EV_READ | EV_PERSIST, 31857419a7fSflorian route_receive, NULL); 31957419a7fSflorian break; 32057419a7fSflorian case IMSG_STARTUP: 32157419a7fSflorian frontend_startup(); 32257419a7fSflorian break; 32357419a7fSflorian #ifndef SMALL 32457419a7fSflorian case IMSG_CONTROLFD: 32557419a7fSflorian if ((fd = imsg.fd) == -1) 32657419a7fSflorian fatalx("%s: expected to receive imsg " 32757419a7fSflorian "control fd but didn't receive any", 32857419a7fSflorian __func__); 32957419a7fSflorian /* Listen on control socket. */ 33057419a7fSflorian control_listen(fd); 33157419a7fSflorian break; 33257419a7fSflorian case IMSG_CTL_END: 33357419a7fSflorian control_imsg_relay(&imsg); 33457419a7fSflorian break; 33557419a7fSflorian #endif /* SMALL */ 33657419a7fSflorian default: 33757419a7fSflorian log_debug("%s: error handling imsg %d", __func__, 33857419a7fSflorian imsg.hdr.type); 33957419a7fSflorian break; 34057419a7fSflorian } 34157419a7fSflorian imsg_free(&imsg); 34257419a7fSflorian } 34357419a7fSflorian if (!shut) 34457419a7fSflorian imsg_event_add(iev); 34557419a7fSflorian else { 34657419a7fSflorian /* This pipe is dead. Remove its event handler. */ 34757419a7fSflorian event_del(&iev->ev); 34857419a7fSflorian event_loopexit(NULL); 34957419a7fSflorian } 35057419a7fSflorian } 35157419a7fSflorian 35257419a7fSflorian void 35357419a7fSflorian frontend_dispatch_engine(int fd, short event, void *bula) 35457419a7fSflorian { 35557419a7fSflorian struct imsgev *iev = bula; 35657419a7fSflorian struct imsgbuf *ibuf = &iev->ibuf; 35757419a7fSflorian struct imsg imsg; 35857419a7fSflorian struct iface *iface; 35957419a7fSflorian ssize_t n; 36057419a7fSflorian int shut = 0; 36157419a7fSflorian 36257419a7fSflorian if (event & EV_READ) { 36357419a7fSflorian if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 36457419a7fSflorian fatal("imsg_read error"); 36557419a7fSflorian if (n == 0) /* Connection closed. */ 36657419a7fSflorian shut = 1; 36757419a7fSflorian } 36857419a7fSflorian if (event & EV_WRITE) { 36957419a7fSflorian if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 37057419a7fSflorian fatal("msgbuf_write"); 37157419a7fSflorian if (n == 0) /* Connection closed. */ 37257419a7fSflorian shut = 1; 37357419a7fSflorian } 37457419a7fSflorian 37557419a7fSflorian for (;;) { 37657419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 37757419a7fSflorian fatal("%s: imsg_get error", __func__); 37857419a7fSflorian if (n == 0) /* No more messages. */ 37957419a7fSflorian break; 38057419a7fSflorian 38157419a7fSflorian switch (imsg.hdr.type) { 38257419a7fSflorian #ifndef SMALL 38357419a7fSflorian case IMSG_CTL_END: 38457419a7fSflorian case IMSG_CTL_SHOW_INTERFACE_INFO: 38557419a7fSflorian control_imsg_relay(&imsg); 38657419a7fSflorian break; 38757419a7fSflorian #endif /* SMALL */ 38857419a7fSflorian case IMSG_SEND_DISCOVER: { 38957419a7fSflorian struct imsg_req_discover imsg_req_discover; 39057419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_discover)) 39157419a7fSflorian fatalx("%s: IMSG_SEND_DISCOVER wrong " 39257419a7fSflorian "length: %lu", __func__, 39357419a7fSflorian IMSG_DATA_SIZE(imsg)); 39457419a7fSflorian memcpy(&imsg_req_discover, imsg.data, 39557419a7fSflorian sizeof(imsg_req_discover)); 39657419a7fSflorian iface = get_iface_by_id(imsg_req_discover.if_index); 39757419a7fSflorian if (iface != NULL) { 39857419a7fSflorian iface->xid = imsg_req_discover.xid; 39957419a7fSflorian send_discover(iface); 40057419a7fSflorian } 40157419a7fSflorian break; 40257419a7fSflorian } 40357419a7fSflorian case IMSG_SEND_REQUEST: { 40457419a7fSflorian struct imsg_req_request imsg_req_request; 40557419a7fSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_request)) 40657419a7fSflorian fatalx("%s: IMSG_SEND_REQUEST wrong " 40757419a7fSflorian "length: %lu", __func__, 40857419a7fSflorian IMSG_DATA_SIZE(imsg)); 40957419a7fSflorian memcpy(&imsg_req_request, imsg.data, 41057419a7fSflorian sizeof(imsg_req_request)); 41157419a7fSflorian iface = get_iface_by_id(imsg_req_request.if_index); 41257419a7fSflorian if (iface != NULL) { 41357419a7fSflorian iface->xid = imsg_req_request.xid; 41457419a7fSflorian iface->requested_ip.s_addr = 41557419a7fSflorian imsg_req_request.requested_ip.s_addr; 41657419a7fSflorian iface->server_identifier.s_addr = 41757419a7fSflorian imsg_req_request.server_identifier.s_addr; 41857419a7fSflorian iface->dhcp_server.s_addr = 41957419a7fSflorian imsg_req_request.dhcp_server.s_addr; 42057419a7fSflorian send_request(iface); 42157419a7fSflorian } 42257419a7fSflorian break; 42357419a7fSflorian } 42457419a7fSflorian default: 42557419a7fSflorian log_debug("%s: error handling imsg %d", __func__, 42657419a7fSflorian imsg.hdr.type); 42757419a7fSflorian break; 42857419a7fSflorian } 42957419a7fSflorian imsg_free(&imsg); 43057419a7fSflorian } 43157419a7fSflorian if (!shut) 43257419a7fSflorian imsg_event_add(iev); 43357419a7fSflorian else { 43457419a7fSflorian /* This pipe is dead. Remove its event handler. */ 43557419a7fSflorian event_del(&iev->ev); 43657419a7fSflorian event_loopexit(NULL); 43757419a7fSflorian } 43857419a7fSflorian } 43957419a7fSflorian 44057419a7fSflorian int 44157419a7fSflorian get_flags(char *if_name) 44257419a7fSflorian { 44357419a7fSflorian struct ifreq ifr; 44457419a7fSflorian 44557419a7fSflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 44657419a7fSflorian if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) { 44757419a7fSflorian log_warn("SIOCGIFFLAGS"); 44857419a7fSflorian return -1; 44957419a7fSflorian } 45057419a7fSflorian return ifr.ifr_flags; 45157419a7fSflorian } 45257419a7fSflorian 45357419a7fSflorian int 45457419a7fSflorian get_xflags(char *if_name) 45557419a7fSflorian { 45657419a7fSflorian struct ifreq ifr; 45757419a7fSflorian 45857419a7fSflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 45957419a7fSflorian if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) { 46057419a7fSflorian log_warn("SIOCGIFXFLAGS"); 46157419a7fSflorian return -1; 46257419a7fSflorian } 46357419a7fSflorian return ifr.ifr_flags; 46457419a7fSflorian } 46557419a7fSflorian 46657419a7fSflorian int 46757419a7fSflorian get_ifrdomain(char *if_name) 46857419a7fSflorian { 46957419a7fSflorian struct ifreq ifr; 47057419a7fSflorian 47157419a7fSflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 47257419a7fSflorian if (ioctl(ioctlsock, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1) { 47357419a7fSflorian log_warn("SIOCGIFRDOMAIN"); 47457419a7fSflorian return -1; 47557419a7fSflorian } 47657419a7fSflorian return ifr.ifr_rdomainid; 47757419a7fSflorian } 47857419a7fSflorian 47957419a7fSflorian void 48057419a7fSflorian update_iface(uint32_t if_index, char* if_name) 48157419a7fSflorian { 48257419a7fSflorian struct iface *iface; 48357419a7fSflorian struct imsg_ifinfo imsg_ifinfo; 48457419a7fSflorian struct ifaddrs *ifap, *ifa; 48557419a7fSflorian struct sockaddr_dl *sdl; 48657419a7fSflorian int flags, xflags, ifrdomain; 48757419a7fSflorian 48857419a7fSflorian if ((flags = get_flags(if_name)) == -1 || (xflags = 48957419a7fSflorian get_xflags(if_name)) == -1) 49057419a7fSflorian return; 49157419a7fSflorian 49257419a7fSflorian if (!(xflags & IFXF_AUTOCONF4)) 49357419a7fSflorian return; 49457419a7fSflorian 49557419a7fSflorian if((ifrdomain = get_ifrdomain(if_name)) == -1) 49657419a7fSflorian return; 49757419a7fSflorian 49857419a7fSflorian iface = get_iface_by_id(if_index); 49957419a7fSflorian 50057419a7fSflorian if (iface != NULL) { 50157419a7fSflorian if (iface->rdomain != ifrdomain) { 50257419a7fSflorian iface->rdomain = ifrdomain; 50357419a7fSflorian if (iface->udpsock != -1) { 50457419a7fSflorian close(iface->udpsock); 50557419a7fSflorian iface->udpsock = -1; 50657419a7fSflorian } 50757419a7fSflorian } 50857419a7fSflorian } else { 50957419a7fSflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 51057419a7fSflorian fatal("calloc"); 51157419a7fSflorian iface->if_index = if_index; 51257419a7fSflorian iface->rdomain = ifrdomain; 51357419a7fSflorian iface->udpsock = -1; 51457419a7fSflorian LIST_INSERT_HEAD(&interfaces, iface, entries); 51557419a7fSflorian frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0, 51657419a7fSflorian &if_index, sizeof(if_index)); 51757419a7fSflorian } 51857419a7fSflorian 51957419a7fSflorian memset(&imsg_ifinfo, 0, sizeof(imsg_ifinfo)); 52057419a7fSflorian 52157419a7fSflorian imsg_ifinfo.if_index = if_index; 52257419a7fSflorian imsg_ifinfo.rdomain = ifrdomain; 52357419a7fSflorian 52457419a7fSflorian imsg_ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | 52557419a7fSflorian IFF_RUNNING); 52657419a7fSflorian 52757419a7fSflorian 52857419a7fSflorian if (getifaddrs(&ifap) != 0) 52957419a7fSflorian fatal("getifaddrs"); 53057419a7fSflorian 53157419a7fSflorian for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 53257419a7fSflorian if (strcmp(if_name, ifa->ifa_name) != 0) 53357419a7fSflorian continue; 53457419a7fSflorian if (ifa->ifa_addr == NULL) 53557419a7fSflorian continue; 53657419a7fSflorian 53757419a7fSflorian switch(ifa->ifa_addr->sa_family) { 53857419a7fSflorian case AF_LINK: 53957419a7fSflorian imsg_ifinfo.link_state = 54057419a7fSflorian ((struct if_data *)ifa->ifa_data)->ifi_link_state; 54157419a7fSflorian sdl = (struct sockaddr_dl *)ifa->ifa_addr; 54257419a7fSflorian if (sdl->sdl_type != IFT_ETHER || 54357419a7fSflorian sdl->sdl_alen != ETHER_ADDR_LEN) 54457419a7fSflorian continue; 54557419a7fSflorian memcpy(iface->hw_address.ether_addr_octet, 54657419a7fSflorian LLADDR(sdl), ETHER_ADDR_LEN); 54757419a7fSflorian goto out; 54857419a7fSflorian default: 54957419a7fSflorian break; 55057419a7fSflorian } 55157419a7fSflorian } 55257419a7fSflorian out: 55357419a7fSflorian freeifaddrs(ifap); 55457419a7fSflorian 55557419a7fSflorian memcpy(&imsg_ifinfo.hw_address, &iface->hw_address, 55657419a7fSflorian sizeof(imsg_ifinfo.hw_address)); 55757419a7fSflorian 55857419a7fSflorian frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &imsg_ifinfo, 55957419a7fSflorian sizeof(imsg_ifinfo)); 56057419a7fSflorian } 56157419a7fSflorian 56257419a7fSflorian void 56357419a7fSflorian frontend_startup(void) 56457419a7fSflorian { 56557419a7fSflorian struct if_nameindex *ifnidxp, *ifnidx; 56657419a7fSflorian 56757419a7fSflorian if (!event_initialized(&ev_route)) 56857419a7fSflorian fatalx("%s: did not receive a route socket from the main " 56957419a7fSflorian "process", __func__); 57057419a7fSflorian 57157419a7fSflorian event_add(&ev_route, NULL); 57257419a7fSflorian 57357419a7fSflorian if ((ifnidxp = if_nameindex()) == NULL) 57457419a7fSflorian fatalx("if_nameindex"); 57557419a7fSflorian 57657419a7fSflorian for(ifnidx = ifnidxp; ifnidx->if_index !=0 && ifnidx->if_name != NULL; 57757419a7fSflorian ifnidx++) 57857419a7fSflorian update_iface(ifnidx->if_index, ifnidx->if_name); 57957419a7fSflorian 58057419a7fSflorian if_freenameindex(ifnidxp); 58157419a7fSflorian } 58257419a7fSflorian 58357419a7fSflorian void 58457419a7fSflorian route_receive(int fd, short events, void *arg) 58557419a7fSflorian { 58657419a7fSflorian static uint8_t *buf; 58757419a7fSflorian 58857419a7fSflorian struct rt_msghdr *rtm; 58957419a7fSflorian struct sockaddr *sa, *rti_info[RTAX_MAX]; 59057419a7fSflorian ssize_t n; 59157419a7fSflorian 59257419a7fSflorian if (buf == NULL) { 59357419a7fSflorian buf = malloc(ROUTE_SOCKET_BUF_SIZE); 59457419a7fSflorian if (buf == NULL) 59557419a7fSflorian fatal("malloc"); 59657419a7fSflorian } 59757419a7fSflorian rtm = (struct rt_msghdr *)buf; 59857419a7fSflorian if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) { 59957419a7fSflorian if (errno == EAGAIN || errno == EINTR) 60057419a7fSflorian return; 60157419a7fSflorian log_warn("dispatch_rtmsg: read error"); 60257419a7fSflorian return; 60357419a7fSflorian } 60457419a7fSflorian 60557419a7fSflorian if (n == 0) 60657419a7fSflorian fatal("routing socket closed"); 60757419a7fSflorian 60857419a7fSflorian if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) { 60957419a7fSflorian log_warnx("partial rtm of %zd in buffer", n); 61057419a7fSflorian return; 61157419a7fSflorian } 61257419a7fSflorian 61357419a7fSflorian if (rtm->rtm_version != RTM_VERSION) 61457419a7fSflorian return; 61557419a7fSflorian 61657419a7fSflorian sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen); 61757419a7fSflorian get_rtaddrs(rtm->rtm_addrs, sa, rti_info); 61857419a7fSflorian 61957419a7fSflorian handle_route_message(rtm, rti_info); 62057419a7fSflorian } 62157419a7fSflorian 62257419a7fSflorian void 62357419a7fSflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info) 62457419a7fSflorian { 62557419a7fSflorian struct if_msghdr *ifm; 62657419a7fSflorian int xflags, if_index; 627*e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 62857419a7fSflorian 62957419a7fSflorian switch (rtm->rtm_type) { 63057419a7fSflorian case RTM_IFINFO: 63157419a7fSflorian ifm = (struct if_msghdr *)rtm; 63257419a7fSflorian if_name = if_indextoname(ifm->ifm_index, ifnamebuf); 63357419a7fSflorian if (if_name == NULL) { 63457419a7fSflorian log_debug("RTM_IFINFO: lost if %d", ifm->ifm_index); 63557419a7fSflorian if_index = ifm->ifm_index; 63657419a7fSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 63757419a7fSflorian &if_index, sizeof(if_index)); 63857419a7fSflorian remove_iface(if_index); 63957419a7fSflorian } else { 64057419a7fSflorian xflags = get_xflags(if_name); 64157419a7fSflorian if (xflags == -1 || !(xflags & IFXF_AUTOCONF4)) { 64257419a7fSflorian log_debug("RTM_IFINFO: %s(%d) no(longer) " 64357419a7fSflorian "autoconf4", if_name, ifm->ifm_index); 64457419a7fSflorian if_index = ifm->ifm_index; 64557419a7fSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 64657419a7fSflorian 0, &if_index, sizeof(if_index)); 64757419a7fSflorian } else { 64857419a7fSflorian update_iface(ifm->ifm_index, if_name); 64957419a7fSflorian } 65057419a7fSflorian } 65157419a7fSflorian break; 65257419a7fSflorian case RTM_NEWADDR: 65357419a7fSflorian ifm = (struct if_msghdr *)rtm; 65457419a7fSflorian if_name = if_indextoname(ifm->ifm_index, ifnamebuf); 655*e998cdbeSflorian log_debug("RTM_NEWADDR: %s[%u]", if_name == NULL ? 656*e998cdbeSflorian "?" : if_name, ifm->ifm_index); 65757419a7fSflorian update_iface(ifm->ifm_index, if_name); 65857419a7fSflorian break; 65957419a7fSflorian case RTM_PROPOSAL: 66057419a7fSflorian if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) { 66157419a7fSflorian log_debug("RTP_PROPOSAL_SOLICIT"); 66257419a7fSflorian frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS, 66357419a7fSflorian 0, 0, NULL, 0); 66457419a7fSflorian } 66557419a7fSflorian break; 66657419a7fSflorian default: 66757419a7fSflorian log_debug("unexpected RTM: %d", rtm->rtm_type); 66857419a7fSflorian break; 66957419a7fSflorian } 67057419a7fSflorian 67157419a7fSflorian } 67257419a7fSflorian 67357419a7fSflorian #define ROUNDUP(a) \ 67457419a7fSflorian ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 67557419a7fSflorian 67657419a7fSflorian void 67757419a7fSflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 67857419a7fSflorian { 67957419a7fSflorian int i; 68057419a7fSflorian 68157419a7fSflorian for (i = 0; i < RTAX_MAX; i++) { 68257419a7fSflorian if (addrs & (1 << i)) { 68357419a7fSflorian rti_info[i] = sa; 68457419a7fSflorian sa = (struct sockaddr *)((char *)(sa) + 68557419a7fSflorian ROUNDUP(sa->sa_len)); 68657419a7fSflorian } else 68757419a7fSflorian rti_info[i] = NULL; 68857419a7fSflorian } 68957419a7fSflorian } 69057419a7fSflorian 69157419a7fSflorian void 69257419a7fSflorian bpf_receive(int fd, short events, void *arg) 69357419a7fSflorian { 69457419a7fSflorian struct bpf_hdr *hdr; 69557419a7fSflorian struct imsg_dhcp imsg_dhcp; 69657419a7fSflorian struct iface *iface; 69757419a7fSflorian ssize_t len, rem; 69857419a7fSflorian uint8_t *p; 69957419a7fSflorian 70057419a7fSflorian iface = (struct iface *)arg; 70157419a7fSflorian 70257419a7fSflorian if ((len = read(fd, iface->bpfev.buf, BPFLEN)) == -1) { 70357419a7fSflorian log_warn("read"); 70457419a7fSflorian return; 70557419a7fSflorian } 706*e998cdbeSflorian 707*e998cdbeSflorian if (len == 0) 708*e998cdbeSflorian fatal("%s len == 0", __func__); 70957419a7fSflorian 71057419a7fSflorian memset(&imsg_dhcp, 0, sizeof(imsg_dhcp)); 71157419a7fSflorian imsg_dhcp.if_index = iface->if_index; 71257419a7fSflorian 71357419a7fSflorian rem = len; 71457419a7fSflorian p = iface->bpfev.buf; 71557419a7fSflorian 71657419a7fSflorian while (rem > 0) { 71757419a7fSflorian if ((size_t)rem < sizeof(*hdr)) { 71857419a7fSflorian log_warnx("packet too short"); 71957419a7fSflorian return; 72057419a7fSflorian } 72157419a7fSflorian hdr = (struct bpf_hdr *)p; 72257419a7fSflorian if (hdr->bh_caplen != hdr->bh_datalen) { 72357419a7fSflorian log_warnx("skipping truncated packet"); 72457419a7fSflorian goto cont; 72557419a7fSflorian } 72657419a7fSflorian if (rem < hdr->bh_hdrlen + hdr->bh_caplen) 72757419a7fSflorian /* we are done */ 72857419a7fSflorian break; 72957419a7fSflorian if (hdr->bh_caplen > sizeof(imsg_dhcp.packet)) { 73057419a7fSflorian log_warn("packet too big"); 73157419a7fSflorian goto cont; 73257419a7fSflorian } 73357419a7fSflorian memcpy(&imsg_dhcp.packet, p + hdr->bh_hdrlen, hdr->bh_caplen); 73457419a7fSflorian imsg_dhcp.len = hdr->bh_caplen; 73557419a7fSflorian frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp, 73657419a7fSflorian sizeof(imsg_dhcp)); 73757419a7fSflorian cont: 73857419a7fSflorian p += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 73957419a7fSflorian rem -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 74057419a7fSflorian 74157419a7fSflorian } 74257419a7fSflorian } 74357419a7fSflorian 74457419a7fSflorian ssize_t 74557419a7fSflorian build_packet(uint8_t message_type, uint32_t xid, struct ether_addr *hw_address, 74657419a7fSflorian struct in_addr *requested_ip, struct in_addr *server_identifier) 74757419a7fSflorian { 74857419a7fSflorian static uint8_t dhcp_cookie[] = DHCP_COOKIE; 74957419a7fSflorian static uint8_t dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1, 75057419a7fSflorian DHCPDISCOVER}; 75157419a7fSflorian static uint8_t dhcp_hostname[255] = {DHO_HOST_NAME, 0 /*, ... */}; 75257419a7fSflorian static uint8_t dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7, 75357419a7fSflorian HTYPE_ETHER, 0, 0, 0, 0, 0, 0}; 75457419a7fSflorian static uint8_t dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST, 75557419a7fSflorian 8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS, 75657419a7fSflorian DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS, 75757419a7fSflorian DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES}; 75857419a7fSflorian static uint8_t dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS, 75957419a7fSflorian 4, 0, 0, 0, 0}; 76057419a7fSflorian static uint8_t dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER, 76157419a7fSflorian 4, 0, 0, 0, 0}; 76257419a7fSflorian struct dhcp_hdr *hdr; 76357419a7fSflorian uint8_t *p; 76457419a7fSflorian char *c; 76557419a7fSflorian 76657419a7fSflorian memset(dhcp_packet, 0, sizeof(dhcp_packet)); 76757419a7fSflorian dhcp_message_type[2] = message_type; 76857419a7fSflorian p = dhcp_packet; 76957419a7fSflorian hdr = (struct dhcp_hdr *)p; 77057419a7fSflorian hdr->op = DHCP_BOOTREQUEST; 77157419a7fSflorian hdr->htype = HTYPE_ETHER; 77257419a7fSflorian hdr->hlen = 6; 77357419a7fSflorian hdr->hops = 0; 77457419a7fSflorian hdr->xid = xid; 77557419a7fSflorian hdr->secs = 0; 77657419a7fSflorian memcpy(hdr->chaddr, hw_address, sizeof(*hw_address)); 77757419a7fSflorian p += sizeof(struct dhcp_hdr); 77857419a7fSflorian memcpy(p, dhcp_cookie, sizeof(dhcp_cookie)); 77957419a7fSflorian p += sizeof(dhcp_cookie); 78057419a7fSflorian memcpy(p, dhcp_message_type, sizeof(dhcp_message_type)); 78157419a7fSflorian p += sizeof(dhcp_message_type); 78257419a7fSflorian if (gethostname(dhcp_hostname + 2, sizeof(dhcp_hostname) - 2) == 0) { 78357419a7fSflorian if ((c = strchr(dhcp_hostname + 2, '.')) != NULL) 78457419a7fSflorian *c = '\0'; 78557419a7fSflorian dhcp_hostname[1] = strlen(dhcp_hostname + 2); 78657419a7fSflorian memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2); 78757419a7fSflorian p += dhcp_hostname[1] + 2; 78857419a7fSflorian } 78957419a7fSflorian memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address)); 79057419a7fSflorian memcpy(p, dhcp_client_id, sizeof(dhcp_client_id)); 79157419a7fSflorian p += sizeof(dhcp_client_id); 79257419a7fSflorian memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); 79357419a7fSflorian p += sizeof(dhcp_req_list); 79457419a7fSflorian 79557419a7fSflorian if (message_type == DHCPREQUEST) { 79657419a7fSflorian memcpy(dhcp_requested_address + 2, requested_ip, 79757419a7fSflorian sizeof(*requested_ip)); 79857419a7fSflorian memcpy(p, dhcp_requested_address, 79957419a7fSflorian sizeof(dhcp_requested_address)); 80057419a7fSflorian p += sizeof(dhcp_requested_address); 80157419a7fSflorian 80257419a7fSflorian if (server_identifier->s_addr != INADDR_ANY) { 80357419a7fSflorian memcpy(dhcp_server_identifier + 2, server_identifier, 80457419a7fSflorian sizeof(*server_identifier)); 80557419a7fSflorian memcpy(p, dhcp_server_identifier, 80657419a7fSflorian sizeof(dhcp_server_identifier)); 80757419a7fSflorian p += sizeof(dhcp_server_identifier); 80857419a7fSflorian } 80957419a7fSflorian } 81057419a7fSflorian 81157419a7fSflorian *p = DHO_END; 81257419a7fSflorian p += 1; 81357419a7fSflorian 81457419a7fSflorian return (p - dhcp_packet); 81557419a7fSflorian } 81657419a7fSflorian 81757419a7fSflorian void 81857419a7fSflorian send_discover(struct iface *iface) 81957419a7fSflorian { 82057419a7fSflorian ssize_t pkt_len; 821*e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 82257419a7fSflorian 82357419a7fSflorian if (!event_initialized(&iface->bpfev.ev)) { 82457419a7fSflorian iface->send_discover = 1; 82557419a7fSflorian return; 82657419a7fSflorian } 82757419a7fSflorian iface->send_discover = 0; 828*e998cdbeSflorian 829*e998cdbeSflorian if_name = if_indextoname(iface->if_index, ifnamebuf); 830*e998cdbeSflorian log_debug("DHCPDISCOVER on %s", if_name == NULL ? "?" : if_name); 831*e998cdbeSflorian 83257419a7fSflorian pkt_len = build_packet(DHCPDISCOVER, iface->xid, &iface->hw_address, 83357419a7fSflorian &iface->requested_ip, NULL); 83457419a7fSflorian bpf_send_packet(iface, dhcp_packet, pkt_len); 83557419a7fSflorian } 83657419a7fSflorian 83757419a7fSflorian void 83857419a7fSflorian send_request(struct iface *iface) 83957419a7fSflorian { 84057419a7fSflorian ssize_t pkt_len; 841*e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 842*e998cdbeSflorian 843*e998cdbeSflorian if_name = if_indextoname(iface->if_index, ifnamebuf); 844*e998cdbeSflorian log_debug("DHCPREQUEST on %s", if_name == NULL ? "?" : if_name); 84557419a7fSflorian 84657419a7fSflorian pkt_len = build_packet(DHCPREQUEST, iface->xid, &iface->hw_address, 84757419a7fSflorian &iface->requested_ip, &iface->server_identifier); 84857419a7fSflorian if (iface->dhcp_server.s_addr != INADDR_ANY) 84957419a7fSflorian udp_send_packet(iface, dhcp_packet, pkt_len); 85057419a7fSflorian else 85157419a7fSflorian bpf_send_packet(iface, dhcp_packet, pkt_len); 85257419a7fSflorian } 85357419a7fSflorian 85457419a7fSflorian void 85557419a7fSflorian udp_send_packet(struct iface *iface, uint8_t *packet, ssize_t len) 85657419a7fSflorian { 85757419a7fSflorian struct sockaddr_in to; 85857419a7fSflorian 85957419a7fSflorian memset(&to, 0, sizeof(to)); 86057419a7fSflorian to.sin_family = AF_INET; 86157419a7fSflorian to.sin_len = sizeof(to); 86257419a7fSflorian to.sin_addr.s_addr = iface->dhcp_server.s_addr; 86357419a7fSflorian to.sin_port = ntohs(SERVER_PORT); 86457419a7fSflorian 86557419a7fSflorian if (sendto(iface->udpsock, packet, len, 0, (struct sockaddr *)&to, 86657419a7fSflorian sizeof(to)) == -1) 86757419a7fSflorian log_warn("sendto"); 86857419a7fSflorian } 86957419a7fSflorian void 87057419a7fSflorian bpf_send_packet(struct iface *iface, uint8_t *packet, ssize_t len) 87157419a7fSflorian { 87257419a7fSflorian struct iovec iov[4]; 87357419a7fSflorian struct ether_header eh; 87457419a7fSflorian struct ip ip; 87557419a7fSflorian struct udphdr udp; 87657419a7fSflorian ssize_t total, result; 87757419a7fSflorian int iovcnt = 0, i; 87857419a7fSflorian 87957419a7fSflorian memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost)); 88057419a7fSflorian memcpy(eh.ether_shost, &iface->hw_address, sizeof(eh.ether_dhost)); 88157419a7fSflorian eh.ether_type = htons(ETHERTYPE_IP); 88257419a7fSflorian iov[0].iov_base = &eh; 88357419a7fSflorian iov[0].iov_len = sizeof(eh); 88457419a7fSflorian iovcnt++; 88557419a7fSflorian 88657419a7fSflorian ip.ip_v = 4; 88757419a7fSflorian ip.ip_hl = 5; 88857419a7fSflorian ip.ip_tos = IPTOS_LOWDELAY; 88957419a7fSflorian ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); 89057419a7fSflorian ip.ip_id = 0; 89157419a7fSflorian ip.ip_off = 0; 89257419a7fSflorian ip.ip_ttl = 128; 89357419a7fSflorian ip.ip_p = IPPROTO_UDP; 89457419a7fSflorian ip.ip_sum = 0; 89557419a7fSflorian ip.ip_src.s_addr = 0; 89657419a7fSflorian ip.ip_dst.s_addr = INADDR_BROADCAST; 89757419a7fSflorian ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); 89857419a7fSflorian iov[iovcnt].iov_base = &ip; 89957419a7fSflorian iov[iovcnt].iov_len = sizeof(ip); 90057419a7fSflorian iovcnt++; 90157419a7fSflorian 90257419a7fSflorian udp.uh_sport = htons(CLIENT_PORT); 90357419a7fSflorian udp.uh_dport = htons(SERVER_PORT); 90457419a7fSflorian udp.uh_ulen = htons(sizeof(udp) + len); 90557419a7fSflorian udp.uh_sum = 0; 90657419a7fSflorian udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), 90757419a7fSflorian checksum((unsigned char *)packet, len, 90857419a7fSflorian checksum((unsigned char *)&ip.ip_src, 90957419a7fSflorian 2 * sizeof(ip.ip_src), 91057419a7fSflorian IPPROTO_UDP + (uint32_t)ntohs(udp.uh_ulen))))); 91157419a7fSflorian iov[iovcnt].iov_base = &udp; 91257419a7fSflorian iov[iovcnt].iov_len = sizeof(udp); 91357419a7fSflorian iovcnt++; 91457419a7fSflorian 91557419a7fSflorian iov[iovcnt].iov_base = packet; 91657419a7fSflorian iov[iovcnt].iov_len = len; 91757419a7fSflorian iovcnt++; 91857419a7fSflorian 91957419a7fSflorian total = 0; 92057419a7fSflorian for (i = 0; i < iovcnt; i++) 92157419a7fSflorian total += iov[i].iov_len; 92257419a7fSflorian 92357419a7fSflorian result = writev(EVENT_FD(&iface->bpfev.ev), iov, iovcnt); 92457419a7fSflorian if (result == -1) 92557419a7fSflorian log_warn("%s: writev", __func__); 92657419a7fSflorian else if (result < total) { 92757419a7fSflorian log_warnx("%s, writev: %zd of %zd bytes", __func__, result, 92857419a7fSflorian total); 92957419a7fSflorian } 93057419a7fSflorian } 93157419a7fSflorian 93257419a7fSflorian struct iface* 93357419a7fSflorian get_iface_by_id(uint32_t if_index) 93457419a7fSflorian { 93557419a7fSflorian struct iface *iface; 93657419a7fSflorian 93757419a7fSflorian LIST_FOREACH (iface, &interfaces, entries) { 93857419a7fSflorian if (iface->if_index == if_index) 93957419a7fSflorian return (iface); 94057419a7fSflorian } 94157419a7fSflorian 94257419a7fSflorian return (NULL); 94357419a7fSflorian } 94457419a7fSflorian 94557419a7fSflorian void 94657419a7fSflorian remove_iface(uint32_t if_index) 94757419a7fSflorian { 94857419a7fSflorian struct iface *iface; 94957419a7fSflorian 95057419a7fSflorian iface = get_iface_by_id(if_index); 95157419a7fSflorian 95257419a7fSflorian if (iface == NULL) 95357419a7fSflorian return; 95457419a7fSflorian 95557419a7fSflorian LIST_REMOVE(iface, entries); 95657419a7fSflorian event_del(&iface->bpfev.ev); 95757419a7fSflorian close(EVENT_FD(&iface->bpfev.ev)); 95857419a7fSflorian if (iface->udpsock != -1) 95957419a7fSflorian close(iface->udpsock); 96057419a7fSflorian free(iface); 96157419a7fSflorian } 96257419a7fSflorian 96357419a7fSflorian void 96457419a7fSflorian set_bpfsock(int bpfsock, uint32_t if_index) 96557419a7fSflorian { 96657419a7fSflorian struct iface *iface; 96757419a7fSflorian 96857419a7fSflorian if ((iface = get_iface_by_id(if_index)) == NULL) { 96957419a7fSflorian /* 97057419a7fSflorian * The interface disappeared while we were waiting for the 97157419a7fSflorian * parent process to open the raw socket. 97257419a7fSflorian */ 97357419a7fSflorian close(bpfsock); 97457419a7fSflorian } else { 97557419a7fSflorian event_set(&iface->bpfev.ev, bpfsock, EV_READ | 97657419a7fSflorian EV_PERSIST, bpf_receive, iface); 97757419a7fSflorian event_add(&iface->bpfev.ev, NULL); 97857419a7fSflorian if (iface->send_discover) 97957419a7fSflorian send_discover(iface); 98057419a7fSflorian } 98157419a7fSflorian } 982