1*0e59d0d1Sclaudio /* $OpenBSD: frontend.c,v 1.45 2024/11/21 13:35:20 claudio 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; 7382a5f3d7Sflorian struct in_addr ciaddr; 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 *); 8248e1b614Sflorian void update_iface(struct if_msghdr *, struct sockaddr_dl *); 8357419a7fSflorian void frontend_startup(void); 841bbf741cSflorian void init_ifaces(void); 8557419a7fSflorian void route_receive(int, short, void *); 8657419a7fSflorian void handle_route_message(struct rt_msghdr *, struct sockaddr **); 8757419a7fSflorian void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 8857419a7fSflorian void bpf_receive(int, short, void *); 8957419a7fSflorian int get_flags(char *); 9057419a7fSflorian int get_xflags(char *); 9157419a7fSflorian struct iface *get_iface_by_id(uint32_t); 9257419a7fSflorian void remove_iface(uint32_t); 9357419a7fSflorian void set_bpfsock(int, uint32_t); 9482a5f3d7Sflorian void iface_data_from_imsg(struct iface*, struct imsg_req_dhcp *); 95a41cc082Sflorian ssize_t build_packet(uint8_t, char *, uint32_t, struct ether_addr *, 9682a5f3d7Sflorian struct in_addr *, struct in_addr *, struct in_addr *); 9782a5f3d7Sflorian void send_packet(uint8_t, struct iface *); 9857419a7fSflorian void bpf_send_packet(struct iface *, uint8_t *, ssize_t); 99c645071eSflorian int udp_send_packet(struct iface *, uint8_t *, ssize_t); 1004eb9756fSflorian #ifndef SMALL 101580befd2Sflorian int iface_conf_cmp(struct iface_conf *, struct iface_conf *); 1024eb9756fSflorian #endif /* SMALL */ 10357419a7fSflorian 10457419a7fSflorian LIST_HEAD(, iface) interfaces; 105a41cc082Sflorian struct dhcpleased_conf *frontend_conf; 10657419a7fSflorian static struct imsgev *iev_main; 10757419a7fSflorian static struct imsgev *iev_engine; 10857419a7fSflorian struct event ev_route; 10957419a7fSflorian int ioctlsock; 11057419a7fSflorian 11157419a7fSflorian uint8_t dhcp_packet[1500]; 11257419a7fSflorian 11357419a7fSflorian void 11457419a7fSflorian frontend_sig_handler(int sig, short event, void *bula) 11557419a7fSflorian { 11657419a7fSflorian /* 11757419a7fSflorian * Normal signal handler rules don't apply because libevent 11857419a7fSflorian * decouples for us. 11957419a7fSflorian */ 12057419a7fSflorian 12157419a7fSflorian switch (sig) { 12257419a7fSflorian case SIGINT: 12357419a7fSflorian case SIGTERM: 12457419a7fSflorian frontend_shutdown(); 12557419a7fSflorian default: 12657419a7fSflorian fatalx("unexpected signal"); 12757419a7fSflorian } 12857419a7fSflorian } 12957419a7fSflorian 13057419a7fSflorian void 13157419a7fSflorian frontend(int debug, int verbose) 13257419a7fSflorian { 13357419a7fSflorian struct event ev_sigint, ev_sigterm; 13457419a7fSflorian struct passwd *pw; 13557419a7fSflorian 136a41cc082Sflorian #ifndef SMALL 137a41cc082Sflorian frontend_conf = config_new_empty(); 138a41cc082Sflorian #endif /* SMALL */ 139a41cc082Sflorian 14057419a7fSflorian log_init(debug, LOG_DAEMON); 14157419a7fSflorian log_setverbose(verbose); 14257419a7fSflorian 14357419a7fSflorian if ((pw = getpwnam(DHCPLEASED_USER)) == NULL) 14457419a7fSflorian fatal("getpwnam"); 14557419a7fSflorian 14657419a7fSflorian if (chdir("/") == -1) 14757419a7fSflorian fatal("chdir(\"/\")"); 14857419a7fSflorian 149c7d84514Sflorian if (unveil("/", "") == -1) 150bc5a8259Sbeck fatal("unveil /"); 151c7d84514Sflorian if (unveil(NULL, NULL) == -1) 152bc5a8259Sbeck fatal("unveil"); 153c7d84514Sflorian 15457419a7fSflorian setproctitle("%s", "frontend"); 15557419a7fSflorian log_procinit("frontend"); 15657419a7fSflorian 15757419a7fSflorian if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) 15857419a7fSflorian fatal("socket"); 15957419a7fSflorian 16057419a7fSflorian if (setgroups(1, &pw->pw_gid) || 16157419a7fSflorian setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 16257419a7fSflorian setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 16357419a7fSflorian fatal("can't drop privileges"); 16457419a7fSflorian 16557419a7fSflorian if (pledge("stdio unix recvfd route", NULL) == -1) 16657419a7fSflorian fatal("pledge"); 16757419a7fSflorian event_init(); 16857419a7fSflorian 16957419a7fSflorian /* Setup signal handler. */ 17057419a7fSflorian signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL); 17157419a7fSflorian signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL); 17257419a7fSflorian signal_add(&ev_sigint, NULL); 17357419a7fSflorian signal_add(&ev_sigterm, NULL); 17457419a7fSflorian signal(SIGPIPE, SIG_IGN); 17557419a7fSflorian signal(SIGHUP, SIG_IGN); 17657419a7fSflorian 17757419a7fSflorian /* Setup pipe and event handler to the parent process. */ 17857419a7fSflorian if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) 17957419a7fSflorian fatal(NULL); 180*0e59d0d1Sclaudio if (imsgbuf_init(&iev_main->ibuf, 3) == -1) 181*0e59d0d1Sclaudio fatal(NULL); 182*0e59d0d1Sclaudio imsgbuf_allow_fdpass(&iev_main->ibuf); 18357419a7fSflorian iev_main->handler = frontend_dispatch_main; 18457419a7fSflorian iev_main->events = EV_READ; 18557419a7fSflorian event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, 18657419a7fSflorian iev_main->handler, iev_main); 18757419a7fSflorian event_add(&iev_main->ev, NULL); 18857419a7fSflorian 18957419a7fSflorian LIST_INIT(&interfaces); 19057419a7fSflorian event_dispatch(); 19157419a7fSflorian 19257419a7fSflorian frontend_shutdown(); 19357419a7fSflorian } 19457419a7fSflorian 19557419a7fSflorian __dead void 19657419a7fSflorian frontend_shutdown(void) 19757419a7fSflorian { 19857419a7fSflorian /* Close pipes. */ 199dd7efffeSclaudio imsgbuf_write(&iev_engine->ibuf); 2009cbf9e90Sclaudio imsgbuf_clear(&iev_engine->ibuf); 20157419a7fSflorian close(iev_engine->ibuf.fd); 202dd7efffeSclaudio imsgbuf_write(&iev_main->ibuf); 2039cbf9e90Sclaudio imsgbuf_clear(&iev_main->ibuf); 20457419a7fSflorian close(iev_main->ibuf.fd); 20557419a7fSflorian 206a41cc082Sflorian #ifndef SMALL 207a41cc082Sflorian config_clear(frontend_conf); 208a41cc082Sflorian #endif /* SMALL */ 209a41cc082Sflorian 21057419a7fSflorian free(iev_engine); 21157419a7fSflorian free(iev_main); 21257419a7fSflorian 21357419a7fSflorian log_info("frontend exiting"); 21457419a7fSflorian exit(0); 21557419a7fSflorian } 21657419a7fSflorian 21757419a7fSflorian int 21857419a7fSflorian frontend_imsg_compose_main(int type, pid_t pid, void *data, 21957419a7fSflorian uint16_t datalen) 22057419a7fSflorian { 22157419a7fSflorian return (imsg_compose_event(iev_main, type, 0, pid, -1, data, 22257419a7fSflorian datalen)); 22357419a7fSflorian } 22457419a7fSflorian 22557419a7fSflorian int 22657419a7fSflorian frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid, 22757419a7fSflorian void *data, uint16_t datalen) 22857419a7fSflorian { 22957419a7fSflorian return (imsg_compose_event(iev_engine, type, peerid, pid, -1, 23057419a7fSflorian data, datalen)); 23157419a7fSflorian } 23257419a7fSflorian 23357419a7fSflorian void 23457419a7fSflorian frontend_dispatch_main(int fd, short event, void *bula) 23557419a7fSflorian { 236a41cc082Sflorian static struct dhcpleased_conf *nconf; 237a41cc082Sflorian static struct iface_conf *iface_conf; 23857419a7fSflorian struct imsg imsg; 23957419a7fSflorian struct imsgev *iev = bula; 24057419a7fSflorian struct imsgbuf *ibuf = &iev->ibuf; 24157419a7fSflorian struct iface *iface; 24257419a7fSflorian ssize_t n; 24345c5e5adSflorian uint32_t type; 24457419a7fSflorian int shut = 0, bpfsock, if_index, udpsock; 24557419a7fSflorian 24657419a7fSflorian if (event & EV_READ) { 247668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 248dd7efffeSclaudio fatal("imsgbuf_read error"); 24957419a7fSflorian if (n == 0) /* Connection closed. */ 25057419a7fSflorian shut = 1; 25157419a7fSflorian } 25257419a7fSflorian if (event & EV_WRITE) { 253dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 254e3b6409cSclaudio if (errno == EPIPE) /* Connection closed. */ 25557419a7fSflorian shut = 1; 256e3b6409cSclaudio else 257dd7efffeSclaudio fatal("imsgbuf_write"); 258e3b6409cSclaudio } 25957419a7fSflorian } 26057419a7fSflorian 26157419a7fSflorian for (;;) { 26257419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 26357419a7fSflorian fatal("%s: imsg_get error", __func__); 26457419a7fSflorian if (n == 0) /* No more messages. */ 26557419a7fSflorian break; 26657419a7fSflorian 26745c5e5adSflorian type = imsg_get_type(&imsg); 26845c5e5adSflorian 26945c5e5adSflorian switch (type) { 27057419a7fSflorian case IMSG_SOCKET_IPC: 27157419a7fSflorian /* 27257419a7fSflorian * Setup pipe and event handler to the engine 27357419a7fSflorian * process. 27457419a7fSflorian */ 27557419a7fSflorian if (iev_engine) 27657419a7fSflorian fatalx("%s: received unexpected imsg fd " 27757419a7fSflorian "to frontend", __func__); 27857419a7fSflorian 279dcedc8acSclaudio if ((fd = imsg_get_fd(&imsg)) == -1) 28057419a7fSflorian fatalx("%s: expected to receive imsg fd to " 28157419a7fSflorian "frontend but didn't receive any", 28257419a7fSflorian __func__); 28357419a7fSflorian 28457419a7fSflorian iev_engine = malloc(sizeof(struct imsgev)); 28557419a7fSflorian if (iev_engine == NULL) 28657419a7fSflorian fatal(NULL); 28757419a7fSflorian 288*0e59d0d1Sclaudio if (imsgbuf_init(&iev_engine->ibuf, fd) == -1) 289*0e59d0d1Sclaudio fatal(NULL); 29057419a7fSflorian iev_engine->handler = frontend_dispatch_engine; 29157419a7fSflorian iev_engine->events = EV_READ; 29257419a7fSflorian 29357419a7fSflorian event_set(&iev_engine->ev, iev_engine->ibuf.fd, 29457419a7fSflorian iev_engine->events, iev_engine->handler, iev_engine); 29557419a7fSflorian event_add(&iev_engine->ev, NULL); 29657419a7fSflorian break; 29757419a7fSflorian case IMSG_BPFSOCK: 298dcedc8acSclaudio if ((bpfsock = imsg_get_fd(&imsg)) == -1) 29957419a7fSflorian fatalx("%s: expected to receive imsg " 30057419a7fSflorian "bpf fd but didn't receive any", 30157419a7fSflorian __func__); 30245c5e5adSflorian if (imsg_get_data(&imsg, &if_index, 30345c5e5adSflorian sizeof(if_index)) == -1) 30445c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 30545c5e5adSflorian 30657419a7fSflorian set_bpfsock(bpfsock, if_index); 30757419a7fSflorian break; 30857419a7fSflorian case IMSG_UDPSOCK: 309dcedc8acSclaudio if ((udpsock = imsg_get_fd(&imsg)) == -1) 31057419a7fSflorian fatalx("%s: expected to receive imsg " 31157419a7fSflorian "udpsocket fd but didn't receive any", 31257419a7fSflorian __func__); 31345c5e5adSflorian if (imsg_get_data(&imsg, &if_index, 31445c5e5adSflorian sizeof(if_index)) == -1) 31545c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 31645c5e5adSflorian 31757419a7fSflorian if ((iface = get_iface_by_id(if_index)) == NULL) { 3181a834994Sflorian close(udpsock); 31957419a7fSflorian break; 32057419a7fSflorian } 32157419a7fSflorian if (iface->udpsock != -1) 32257419a7fSflorian fatalx("%s: received unexpected udpsocket", 32357419a7fSflorian __func__); 32457419a7fSflorian iface->udpsock = udpsock; 32557419a7fSflorian break; 32657419a7fSflorian case IMSG_CLOSE_UDPSOCK: 32745c5e5adSflorian if (imsg_get_data(&imsg, &if_index, 32845c5e5adSflorian sizeof(if_index)) == -1) 32945c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 33045c5e5adSflorian 33157419a7fSflorian if ((iface = get_iface_by_id(if_index)) != NULL && 33257419a7fSflorian iface->udpsock != -1) { 33357419a7fSflorian close(iface->udpsock); 33457419a7fSflorian iface->udpsock = -1; 33557419a7fSflorian } 33657419a7fSflorian break; 33757419a7fSflorian case IMSG_ROUTESOCK: 338dcedc8acSclaudio if ((fd = imsg_get_fd(&imsg)) == -1) 33957419a7fSflorian fatalx("%s: expected to receive imsg " 34057419a7fSflorian "routesocket fd but didn't receive any", 34157419a7fSflorian __func__); 34257419a7fSflorian event_set(&ev_route, fd, EV_READ | EV_PERSIST, 34357419a7fSflorian route_receive, NULL); 34457419a7fSflorian break; 34557419a7fSflorian case IMSG_STARTUP: 34657419a7fSflorian frontend_startup(); 34757419a7fSflorian break; 34857419a7fSflorian #ifndef SMALL 349a41cc082Sflorian case IMSG_RECONF_CONF: 350a41cc082Sflorian if (nconf != NULL) 351a41cc082Sflorian fatalx("%s: IMSG_RECONF_CONF already in " 352a41cc082Sflorian "progress", __func__); 353a41cc082Sflorian if ((nconf = malloc(sizeof(struct dhcpleased_conf))) == 354a41cc082Sflorian NULL) 355a41cc082Sflorian fatal(NULL); 356a41cc082Sflorian SIMPLEQ_INIT(&nconf->iface_list); 357a41cc082Sflorian break; 358a41cc082Sflorian case IMSG_RECONF_IFACE: 359a41cc082Sflorian if ((iface_conf = malloc(sizeof(struct iface_conf))) 360a41cc082Sflorian == NULL) 361a41cc082Sflorian fatal(NULL); 36245c5e5adSflorian 36345c5e5adSflorian if (imsg_get_data(&imsg, iface_conf, 36445c5e5adSflorian sizeof(struct iface_conf)) == -1) 36545c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 36645c5e5adSflorian 367a41cc082Sflorian iface_conf->vc_id = NULL; 368a41cc082Sflorian iface_conf->vc_id_len = 0; 369a41cc082Sflorian iface_conf->c_id = NULL; 370a41cc082Sflorian iface_conf->c_id_len = 0; 371b3441518Sflorian iface_conf->h_name = NULL; 372a41cc082Sflorian SIMPLEQ_INSERT_TAIL(&nconf->iface_list, 373a41cc082Sflorian iface_conf, entry); 374a41cc082Sflorian break; 375a41cc082Sflorian case IMSG_RECONF_VC_ID: 376a41cc082Sflorian if (iface_conf == NULL) 377f46577a8Sflorian fatalx("%s: %s without IMSG_RECONF_IFACE", 378f46577a8Sflorian __func__, i2s(type)); 379f46577a8Sflorian if (iface_conf->vc_id != NULL) 380f46577a8Sflorian fatalx("%s: multiple %s for the same interface", 381f46577a8Sflorian __func__, i2s(type)); 38245c5e5adSflorian if ((iface_conf->vc_id_len = imsg_get_len(&imsg)) 38345c5e5adSflorian > 255 + 2 || iface_conf->vc_id_len == 0) 38445c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 38545c5e5adSflorian if ((iface_conf->vc_id = malloc(iface_conf->vc_id_len)) 386a41cc082Sflorian == NULL) 387a41cc082Sflorian fatal(NULL); 38845c5e5adSflorian if (imsg_get_data(&imsg, iface_conf->vc_id, 38945c5e5adSflorian iface_conf->vc_id_len) == -1) 39045c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 391a41cc082Sflorian break; 392a41cc082Sflorian case IMSG_RECONF_C_ID: 393a41cc082Sflorian if (iface_conf == NULL) 394f46577a8Sflorian fatalx("%s: %s without IMSG_RECONF_IFACE", 395f46577a8Sflorian __func__, i2s(type)); 396f46577a8Sflorian if (iface_conf->c_id != NULL) 397f46577a8Sflorian fatalx("%s: multiple %s for the same interface", 398f46577a8Sflorian __func__, i2s(type)); 39945c5e5adSflorian if ((iface_conf->c_id_len = imsg_get_len(&imsg)) 40045c5e5adSflorian > 255 + 2 || iface_conf->c_id_len == 0) 40145c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 40245c5e5adSflorian if ((iface_conf->c_id = malloc(iface_conf->c_id_len)) 403a41cc082Sflorian == NULL) 404a41cc082Sflorian fatal(NULL); 40545c5e5adSflorian if (imsg_get_data(&imsg, iface_conf->c_id, 40645c5e5adSflorian iface_conf->c_id_len) == -1) 40745c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 408a41cc082Sflorian break; 40945c5e5adSflorian case IMSG_RECONF_H_NAME: { 41045c5e5adSflorian size_t len; 41145c5e5adSflorian 412b3441518Sflorian if (iface_conf == NULL) 413f46577a8Sflorian fatalx("%s: %s without IMSG_RECONF_IFACE", 414f46577a8Sflorian __func__, i2s(type)); 415f46577a8Sflorian if (iface_conf->h_name != NULL) 416f46577a8Sflorian fatalx("%s: multiple %s for the same interface", 417f46577a8Sflorian __func__, i2s(type)); 41845c5e5adSflorian if ((len = imsg_get_len(&imsg)) > 256 || len == 0) 41945c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 42045c5e5adSflorian if ((iface_conf->h_name = malloc(len)) == NULL) 421b3441518Sflorian fatal(NULL); 42245c5e5adSflorian if (imsg_get_data(&imsg, iface_conf->h_name, len) == -1) 42345c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 42445c5e5adSflorian if (iface_conf->h_name[len - 1] != '\0') 42545c5e5adSflorian fatalx("Invalid hostname"); 426b3441518Sflorian break; 42745c5e5adSflorian } 428580befd2Sflorian case IMSG_RECONF_END: { 429580befd2Sflorian int i; 430580befd2Sflorian int *ifaces; 431580befd2Sflorian char ifnamebuf[IF_NAMESIZE], *if_name; 432580befd2Sflorian 433a41cc082Sflorian if (nconf == NULL) 4347e5648d1Sflorian fatalx("%s: %s without IMSG_RECONF_CONF", 4357e5648d1Sflorian __func__, i2s(type)); 436580befd2Sflorian 437580befd2Sflorian ifaces = changed_ifaces(frontend_conf, nconf); 438a41cc082Sflorian merge_config(frontend_conf, nconf); 439a41cc082Sflorian nconf = NULL; 440580befd2Sflorian for (i = 0; ifaces[i] != 0; i++) { 441580befd2Sflorian if_index = ifaces[i]; 442580befd2Sflorian if_name = if_indextoname(if_index, ifnamebuf); 443580befd2Sflorian log_debug("changed iface: %s[%d]", if_name != 444580befd2Sflorian NULL ? if_name : "<unknown>", if_index); 445580befd2Sflorian frontend_imsg_compose_engine( 446580befd2Sflorian IMSG_REQUEST_REBOOT, 0, 0, &if_index, 447580befd2Sflorian sizeof(if_index)); 448580befd2Sflorian } 449580befd2Sflorian free(ifaces); 450a41cc082Sflorian break; 451580befd2Sflorian } 45257419a7fSflorian case IMSG_CONTROLFD: 453dcedc8acSclaudio if ((fd = imsg_get_fd(&imsg)) == -1) 45457419a7fSflorian fatalx("%s: expected to receive imsg " 45557419a7fSflorian "control fd but didn't receive any", 45657419a7fSflorian __func__); 45757419a7fSflorian /* Listen on control socket. */ 45857419a7fSflorian control_listen(fd); 45957419a7fSflorian break; 46057419a7fSflorian case IMSG_CTL_END: 46157419a7fSflorian control_imsg_relay(&imsg); 46257419a7fSflorian break; 46357419a7fSflorian #endif /* SMALL */ 46457419a7fSflorian default: 46545c5e5adSflorian log_debug("%s: error handling imsg %d", __func__, type); 46657419a7fSflorian break; 46757419a7fSflorian } 46857419a7fSflorian imsg_free(&imsg); 46957419a7fSflorian } 47057419a7fSflorian if (!shut) 47157419a7fSflorian imsg_event_add(iev); 47257419a7fSflorian else { 47357419a7fSflorian /* This pipe is dead. Remove its event handler. */ 47457419a7fSflorian event_del(&iev->ev); 47557419a7fSflorian event_loopexit(NULL); 47657419a7fSflorian } 47757419a7fSflorian } 47857419a7fSflorian 47957419a7fSflorian void 48057419a7fSflorian frontend_dispatch_engine(int fd, short event, void *bula) 48157419a7fSflorian { 48257419a7fSflorian struct imsgev *iev = bula; 48357419a7fSflorian struct imsgbuf *ibuf = &iev->ibuf; 48457419a7fSflorian struct imsg imsg; 48557419a7fSflorian struct iface *iface; 48657419a7fSflorian ssize_t n; 48745c5e5adSflorian uint32_t type; 48857419a7fSflorian int shut = 0; 48957419a7fSflorian 49057419a7fSflorian if (event & EV_READ) { 491668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 492dd7efffeSclaudio fatal("imsgbuf_read error"); 49357419a7fSflorian if (n == 0) /* Connection closed. */ 49457419a7fSflorian shut = 1; 49557419a7fSflorian } 49657419a7fSflorian if (event & EV_WRITE) { 497dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 498e3b6409cSclaudio if (errno == EPIPE) /* Connection closed. */ 49957419a7fSflorian shut = 1; 500e3b6409cSclaudio else 501dd7efffeSclaudio fatal("imsgbuf_write"); 502e3b6409cSclaudio } 50357419a7fSflorian } 50457419a7fSflorian 50557419a7fSflorian for (;;) { 50657419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 50757419a7fSflorian fatal("%s: imsg_get error", __func__); 50857419a7fSflorian if (n == 0) /* No more messages. */ 50957419a7fSflorian break; 51057419a7fSflorian 51145c5e5adSflorian type = imsg_get_type(&imsg); 51245c5e5adSflorian 51345c5e5adSflorian switch (type) { 51457419a7fSflorian #ifndef SMALL 51557419a7fSflorian case IMSG_CTL_END: 51657419a7fSflorian case IMSG_CTL_SHOW_INTERFACE_INFO: 51757419a7fSflorian control_imsg_relay(&imsg); 51857419a7fSflorian break; 51957419a7fSflorian #endif /* SMALL */ 52057419a7fSflorian case IMSG_SEND_DISCOVER: { 52182a5f3d7Sflorian struct imsg_req_dhcp imsg_req_dhcp; 52245c5e5adSflorian 52345c5e5adSflorian if (imsg_get_data(&imsg, &imsg_req_dhcp, 52445c5e5adSflorian sizeof(imsg_req_dhcp)) == -1) 52545c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 52682a5f3d7Sflorian 52782a5f3d7Sflorian iface = get_iface_by_id(imsg_req_dhcp.if_index); 52882a5f3d7Sflorian 52982a5f3d7Sflorian if (iface == NULL) 53082a5f3d7Sflorian break; 53182a5f3d7Sflorian 53282a5f3d7Sflorian iface_data_from_imsg(iface, &imsg_req_dhcp); 53382a5f3d7Sflorian send_packet(DHCPDISCOVER, iface); 53457419a7fSflorian break; 53557419a7fSflorian } 53657419a7fSflorian case IMSG_SEND_REQUEST: { 53782a5f3d7Sflorian struct imsg_req_dhcp imsg_req_dhcp; 53845c5e5adSflorian 53945c5e5adSflorian if (imsg_get_data(&imsg, &imsg_req_dhcp, 54045c5e5adSflorian sizeof(imsg_req_dhcp)) == -1) 54145c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 54282a5f3d7Sflorian 54382a5f3d7Sflorian iface = get_iface_by_id(imsg_req_dhcp.if_index); 54482a5f3d7Sflorian 54582a5f3d7Sflorian if (iface == NULL) 54682a5f3d7Sflorian break; 54782a5f3d7Sflorian 54882a5f3d7Sflorian iface_data_from_imsg(iface, &imsg_req_dhcp); 54982a5f3d7Sflorian send_packet(DHCPREQUEST, iface); 55057419a7fSflorian break; 55157419a7fSflorian } 55257419a7fSflorian default: 55345c5e5adSflorian log_debug("%s: error handling imsg %d", __func__, type); 55457419a7fSflorian break; 55557419a7fSflorian } 55657419a7fSflorian imsg_free(&imsg); 55757419a7fSflorian } 55857419a7fSflorian if (!shut) 55957419a7fSflorian imsg_event_add(iev); 56057419a7fSflorian else { 56157419a7fSflorian /* This pipe is dead. Remove its event handler. */ 56257419a7fSflorian event_del(&iev->ev); 56357419a7fSflorian event_loopexit(NULL); 56457419a7fSflorian } 56557419a7fSflorian } 56657419a7fSflorian 56757419a7fSflorian int 56857419a7fSflorian get_flags(char *if_name) 56957419a7fSflorian { 57057419a7fSflorian struct ifreq ifr; 57157419a7fSflorian 57257419a7fSflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 57357419a7fSflorian if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) { 57457419a7fSflorian log_warn("SIOCGIFFLAGS"); 57557419a7fSflorian return -1; 57657419a7fSflorian } 57757419a7fSflorian return ifr.ifr_flags; 57857419a7fSflorian } 57957419a7fSflorian 58057419a7fSflorian int 58157419a7fSflorian get_xflags(char *if_name) 58257419a7fSflorian { 58357419a7fSflorian struct ifreq ifr; 58457419a7fSflorian 58557419a7fSflorian strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 58657419a7fSflorian if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) { 58757419a7fSflorian log_warn("SIOCGIFXFLAGS"); 58857419a7fSflorian return -1; 58957419a7fSflorian } 59057419a7fSflorian return ifr.ifr_flags; 59157419a7fSflorian } 59257419a7fSflorian 59357419a7fSflorian void 59448e1b614Sflorian update_iface(struct if_msghdr *ifm, struct sockaddr_dl *sdl) 59557419a7fSflorian { 59657419a7fSflorian struct iface *iface; 5971bbf741cSflorian struct imsg_ifinfo ifinfo; 5981bbf741cSflorian uint32_t if_index; 59948e1b614Sflorian int flags, xflags; 6001bbf741cSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 60157419a7fSflorian 6021bbf741cSflorian if_index = ifm->ifm_index; 6031bbf741cSflorian 6041bbf741cSflorian flags = ifm->ifm_flags; 6051bbf741cSflorian xflags = ifm->ifm_xflags; 6061bbf741cSflorian 6071bbf741cSflorian iface = get_iface_by_id(if_index); 6081bbf741cSflorian if_name = if_indextoname(if_index, ifnamebuf); 6091bbf741cSflorian 6101bbf741cSflorian if (if_name == NULL) { 6111bbf741cSflorian if (iface != NULL) { 6121bbf741cSflorian log_debug("interface with idx %d removed", if_index); 6131bbf741cSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 6141bbf741cSflorian &if_index, sizeof(if_index)); 6151bbf741cSflorian remove_iface(if_index); 6161bbf741cSflorian } 61757419a7fSflorian return; 6181bbf741cSflorian } 61957419a7fSflorian 6201bbf741cSflorian if (!(xflags & IFXF_AUTOCONF4)) { 6211bbf741cSflorian if (iface != NULL) { 6221bbf741cSflorian log_info("Removed autoconf flag from %s", if_name); 6231bbf741cSflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 6241bbf741cSflorian &if_index, sizeof(if_index)); 6251bbf741cSflorian remove_iface(if_index); 6261bbf741cSflorian } 62757419a7fSflorian return; 6281bbf741cSflorian } 62957419a7fSflorian 6301bbf741cSflorian memset(&ifinfo, 0, sizeof(ifinfo)); 6311bbf741cSflorian ifinfo.if_index = if_index; 6321bbf741cSflorian ifinfo.link_state = ifm->ifm_data.ifi_link_state; 6331bbf741cSflorian ifinfo.rdomain = ifm->ifm_tableid; 6341bbf741cSflorian ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == 6351bbf741cSflorian (IFF_UP | IFF_RUNNING); 63657419a7fSflorian 637a13fc78dSflorian if (sdl != NULL && (sdl->sdl_type == IFT_ETHER || 638a13fc78dSflorian sdl->sdl_type == IFT_CARP) && sdl->sdl_alen == ETHER_ADDR_LEN) 6391bbf741cSflorian memcpy(ifinfo.hw_address.ether_addr_octet, LLADDR(sdl), 6401bbf741cSflorian ETHER_ADDR_LEN); 64148e1b614Sflorian else if (iface == NULL) { 6421bbf741cSflorian log_warnx("Could not find AF_LINK address for %s.", if_name); 6431bbf741cSflorian return; 6444fd2b0e0Sflorian } 6451bbf741cSflorian 6461bbf741cSflorian if (iface == NULL) { 6474fd2b0e0Sflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 6484fd2b0e0Sflorian fatal("calloc"); 6494fd2b0e0Sflorian iface->udpsock = -1; 6504fd2b0e0Sflorian LIST_INSERT_HEAD(&interfaces, iface, entries); 6514fd2b0e0Sflorian frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0, 6524fd2b0e0Sflorian &if_index, sizeof(if_index)); 6531bbf741cSflorian } else { 6541bbf741cSflorian if (iface->ifinfo.rdomain != ifinfo.rdomain && 6551bbf741cSflorian iface->udpsock != -1) { 6561bbf741cSflorian close(iface->udpsock); 6571bbf741cSflorian iface->udpsock = -1; 6581bbf741cSflorian } 6594fd2b0e0Sflorian } 6604fd2b0e0Sflorian 6611bbf741cSflorian if (memcmp(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)) != 0) { 6621bbf741cSflorian memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); 6631bbf741cSflorian frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo, 6641bbf741cSflorian sizeof(iface->ifinfo)); 6651bbf741cSflorian } 66657419a7fSflorian } 66757419a7fSflorian 66857419a7fSflorian void 66957419a7fSflorian frontend_startup(void) 67057419a7fSflorian { 67157419a7fSflorian if (!event_initialized(&ev_route)) 67257419a7fSflorian fatalx("%s: did not receive a route socket from the main " 67357419a7fSflorian "process", __func__); 67457419a7fSflorian 6751bbf741cSflorian init_ifaces(); 67648e1b614Sflorian if (pledge("stdio unix recvfd", NULL) == -1) 67748e1b614Sflorian fatal("pledge"); 67857419a7fSflorian event_add(&ev_route, NULL); 6791bbf741cSflorian } 6801bbf741cSflorian 6811bbf741cSflorian void 6821bbf741cSflorian init_ifaces(void) 6831bbf741cSflorian { 6841bbf741cSflorian struct iface *iface; 6851bbf741cSflorian struct imsg_ifinfo ifinfo; 6861bbf741cSflorian struct if_nameindex *ifnidxp, *ifnidx; 6871bbf741cSflorian struct ifaddrs *ifap, *ifa; 6881bbf741cSflorian uint32_t if_index; 6891bbf741cSflorian int flags, xflags; 6901bbf741cSflorian char *if_name; 69157419a7fSflorian 69257419a7fSflorian if ((ifnidxp = if_nameindex()) == NULL) 69357419a7fSflorian fatalx("if_nameindex"); 69457419a7fSflorian 6951bbf741cSflorian if (getifaddrs(&ifap) != 0) 6961bbf741cSflorian fatal("getifaddrs"); 69757419a7fSflorian 6981bbf741cSflorian for (ifnidx = ifnidxp; ifnidx->if_index != 0 && ifnidx->if_name != NULL; 6991bbf741cSflorian ifnidx++) { 7001bbf741cSflorian if_index = ifnidx->if_index; 7011bbf741cSflorian if_name = ifnidx->if_name; 7021bbf741cSflorian if ((flags = get_flags(if_name)) == -1) 7031bbf741cSflorian continue; 7041bbf741cSflorian if ((xflags = get_xflags(if_name)) == -1) 7051bbf741cSflorian continue; 7061bbf741cSflorian if (!(xflags & IFXF_AUTOCONF4)) 7071bbf741cSflorian continue; 7081bbf741cSflorian 7091bbf741cSflorian memset(&ifinfo, 0, sizeof(ifinfo)); 7101bbf741cSflorian ifinfo.if_index = if_index; 7111bbf741cSflorian ifinfo.link_state = -1; 7121bbf741cSflorian ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == 7131bbf741cSflorian (IFF_UP | IFF_RUNNING); 7141bbf741cSflorian 7151bbf741cSflorian for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 7161bbf741cSflorian if (strcmp(if_name, ifa->ifa_name) != 0) 7171bbf741cSflorian continue; 7181bbf741cSflorian if (ifa->ifa_addr == NULL) 7191bbf741cSflorian continue; 7201bbf741cSflorian 7211bbf741cSflorian switch (ifa->ifa_addr->sa_family) { 7221bbf741cSflorian case AF_LINK: { 7231bbf741cSflorian struct if_data *if_data; 7241bbf741cSflorian struct sockaddr_dl *sdl; 7251bbf741cSflorian 7261bbf741cSflorian sdl = (struct sockaddr_dl *)ifa->ifa_addr; 727a13fc78dSflorian if ((sdl->sdl_type != IFT_ETHER && 728a13fc78dSflorian sdl->sdl_type != IFT_CARP) || 7291bbf741cSflorian sdl->sdl_alen != ETHER_ADDR_LEN) 7301bbf741cSflorian continue; 7311bbf741cSflorian memcpy(ifinfo.hw_address.ether_addr_octet, 7321bbf741cSflorian LLADDR(sdl), ETHER_ADDR_LEN); 7331bbf741cSflorian 7341bbf741cSflorian if_data = (struct if_data *)ifa->ifa_data; 7351bbf741cSflorian ifinfo.link_state = if_data->ifi_link_state; 7361bbf741cSflorian ifinfo.rdomain = if_data->ifi_rdomain; 7371bbf741cSflorian goto out; 7381bbf741cSflorian } 7391bbf741cSflorian default: 7401bbf741cSflorian break; 7411bbf741cSflorian } 7421bbf741cSflorian } 7431bbf741cSflorian out: 7441bbf741cSflorian if (ifinfo.link_state == -1) 7451bbf741cSflorian /* no AF_LINK found */ 7461bbf741cSflorian continue; 7471bbf741cSflorian 7481bbf741cSflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 7491bbf741cSflorian fatal("calloc"); 7501bbf741cSflorian iface->udpsock = -1; 7511bbf741cSflorian memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); 7521bbf741cSflorian LIST_INSERT_HEAD(&interfaces, iface, entries); 7531bbf741cSflorian frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0, 7541bbf741cSflorian &if_index, sizeof(if_index)); 7551bbf741cSflorian frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo, 7561bbf741cSflorian sizeof(iface->ifinfo)); 7571bbf741cSflorian } 7581bbf741cSflorian 7591bbf741cSflorian freeifaddrs(ifap); 76057419a7fSflorian if_freenameindex(ifnidxp); 76157419a7fSflorian } 76257419a7fSflorian 76357419a7fSflorian void 76457419a7fSflorian route_receive(int fd, short events, void *arg) 76557419a7fSflorian { 76657419a7fSflorian static uint8_t *buf; 76757419a7fSflorian 76857419a7fSflorian struct rt_msghdr *rtm; 76957419a7fSflorian struct sockaddr *sa, *rti_info[RTAX_MAX]; 77057419a7fSflorian ssize_t n; 77157419a7fSflorian 77257419a7fSflorian if (buf == NULL) { 77357419a7fSflorian buf = malloc(ROUTE_SOCKET_BUF_SIZE); 77457419a7fSflorian if (buf == NULL) 77557419a7fSflorian fatal("malloc"); 77657419a7fSflorian } 77757419a7fSflorian rtm = (struct rt_msghdr *)buf; 77857419a7fSflorian if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) { 77957419a7fSflorian if (errno == EAGAIN || errno == EINTR) 78057419a7fSflorian return; 78157419a7fSflorian log_warn("dispatch_rtmsg: read error"); 78257419a7fSflorian return; 78357419a7fSflorian } 78457419a7fSflorian 78557419a7fSflorian if (n == 0) 78657419a7fSflorian fatal("routing socket closed"); 78757419a7fSflorian 78857419a7fSflorian if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) { 78957419a7fSflorian log_warnx("partial rtm of %zd in buffer", n); 79057419a7fSflorian return; 79157419a7fSflorian } 79257419a7fSflorian 79357419a7fSflorian if (rtm->rtm_version != RTM_VERSION) 79457419a7fSflorian return; 79557419a7fSflorian 79657419a7fSflorian sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen); 79757419a7fSflorian get_rtaddrs(rtm->rtm_addrs, sa, rti_info); 79857419a7fSflorian 79957419a7fSflorian handle_route_message(rtm, rti_info); 80057419a7fSflorian } 80157419a7fSflorian 80257419a7fSflorian void 80357419a7fSflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info) 80457419a7fSflorian { 80548e1b614Sflorian struct sockaddr_dl *sdl = NULL; 806d3097762Sflorian struct if_announcemsghdr *ifan; 807d3097762Sflorian uint32_t if_index; 808d3097762Sflorian 80957419a7fSflorian switch (rtm->rtm_type) { 81057419a7fSflorian case RTM_IFINFO: 81148e1b614Sflorian if (rtm->rtm_addrs & RTA_IFP && rti_info[RTAX_IFP]->sa_family 81248e1b614Sflorian == AF_LINK) 81348e1b614Sflorian sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP]; 81448e1b614Sflorian update_iface((struct if_msghdr *)rtm, sdl); 81557419a7fSflorian break; 816d3097762Sflorian case RTM_IFANNOUNCE: 817d3097762Sflorian ifan = (struct if_announcemsghdr *)rtm; 818d3097762Sflorian if_index = ifan->ifan_index; 819d3097762Sflorian if (ifan->ifan_what == IFAN_DEPARTURE) { 820d3097762Sflorian frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 821d3097762Sflorian &if_index, sizeof(if_index)); 822d3097762Sflorian remove_iface(if_index); 823d3097762Sflorian } 824d3097762Sflorian break; 82557419a7fSflorian case RTM_PROPOSAL: 82657419a7fSflorian if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) { 82757419a7fSflorian log_debug("RTP_PROPOSAL_SOLICIT"); 82857419a7fSflorian frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS, 82957419a7fSflorian 0, 0, NULL, 0); 830c7313d44Sflorian } 83157419a7fSflorian break; 83257419a7fSflorian default: 83357419a7fSflorian log_debug("unexpected RTM: %d", rtm->rtm_type); 83457419a7fSflorian break; 83557419a7fSflorian } 83657419a7fSflorian } 83757419a7fSflorian 83857419a7fSflorian #define ROUNDUP(a) \ 83957419a7fSflorian ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 84057419a7fSflorian 84157419a7fSflorian void 84257419a7fSflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 84357419a7fSflorian { 84457419a7fSflorian int i; 84557419a7fSflorian 84657419a7fSflorian for (i = 0; i < RTAX_MAX; i++) { 84757419a7fSflorian if (addrs & (1 << i)) { 84857419a7fSflorian rti_info[i] = sa; 84957419a7fSflorian sa = (struct sockaddr *)((char *)(sa) + 85057419a7fSflorian ROUNDUP(sa->sa_len)); 85157419a7fSflorian } else 85257419a7fSflorian rti_info[i] = NULL; 85357419a7fSflorian } 85457419a7fSflorian } 85557419a7fSflorian 85657419a7fSflorian void 85757419a7fSflorian bpf_receive(int fd, short events, void *arg) 85857419a7fSflorian { 85957419a7fSflorian struct bpf_hdr *hdr; 86057419a7fSflorian struct imsg_dhcp imsg_dhcp; 86157419a7fSflorian struct iface *iface; 86257419a7fSflorian ssize_t len, rem; 86357419a7fSflorian uint8_t *p; 86457419a7fSflorian 86557419a7fSflorian iface = (struct iface *)arg; 86657419a7fSflorian 86757419a7fSflorian if ((len = read(fd, iface->bpfev.buf, BPFLEN)) == -1) { 86837fb58f6Sflorian log_warn("%s: read", __func__); 86957419a7fSflorian return; 87057419a7fSflorian } 871e998cdbeSflorian 872e998cdbeSflorian if (len == 0) 873e998cdbeSflorian fatal("%s len == 0", __func__); 87457419a7fSflorian 87557419a7fSflorian memset(&imsg_dhcp, 0, sizeof(imsg_dhcp)); 8761bbf741cSflorian imsg_dhcp.if_index = iface->ifinfo.if_index; 87757419a7fSflorian 87857419a7fSflorian rem = len; 87957419a7fSflorian p = iface->bpfev.buf; 88057419a7fSflorian 88157419a7fSflorian while (rem > 0) { 88257419a7fSflorian if ((size_t)rem < sizeof(*hdr)) { 88357419a7fSflorian log_warnx("packet too short"); 88457419a7fSflorian return; 88557419a7fSflorian } 88657419a7fSflorian hdr = (struct bpf_hdr *)p; 88757419a7fSflorian if (hdr->bh_caplen != hdr->bh_datalen) { 88857419a7fSflorian log_warnx("skipping truncated packet"); 88957419a7fSflorian goto cont; 89057419a7fSflorian } 89157419a7fSflorian if (rem < hdr->bh_hdrlen + hdr->bh_caplen) 89257419a7fSflorian /* we are done */ 89357419a7fSflorian break; 89457419a7fSflorian if (hdr->bh_caplen > sizeof(imsg_dhcp.packet)) { 89557419a7fSflorian log_warn("packet too big"); 89657419a7fSflorian goto cont; 89757419a7fSflorian } 89857419a7fSflorian memcpy(&imsg_dhcp.packet, p + hdr->bh_hdrlen, hdr->bh_caplen); 89957419a7fSflorian imsg_dhcp.len = hdr->bh_caplen; 9002b86dc95Sjan imsg_dhcp.csumflags = hdr->bh_csumflags; 90157419a7fSflorian frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp, 90257419a7fSflorian sizeof(imsg_dhcp)); 90357419a7fSflorian cont: 90457419a7fSflorian p += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 90557419a7fSflorian rem -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 90657419a7fSflorian 90757419a7fSflorian } 90857419a7fSflorian } 90957419a7fSflorian 91082a5f3d7Sflorian void 91182a5f3d7Sflorian iface_data_from_imsg(struct iface* iface, struct imsg_req_dhcp *imsg) 91282a5f3d7Sflorian { 91382a5f3d7Sflorian iface->xid = imsg->xid; 914b2b40540Sflorian iface->ciaddr = imsg->ciaddr; 915b2b40540Sflorian iface->requested_ip = imsg->requested_ip; 916b2b40540Sflorian iface->server_identifier = imsg->server_identifier; 917b2b40540Sflorian iface->dhcp_server = imsg->dhcp_server; 91882a5f3d7Sflorian } 91982a5f3d7Sflorian 92057419a7fSflorian ssize_t 921a41cc082Sflorian build_packet(uint8_t message_type, char *if_name, uint32_t xid, 92282a5f3d7Sflorian struct ether_addr *hw_address, struct in_addr *ciaddr, struct in_addr 92382a5f3d7Sflorian *requested_ip, struct in_addr *server_identifier) 92457419a7fSflorian { 92557419a7fSflorian static uint8_t dhcp_cookie[] = DHCP_COOKIE; 92657419a7fSflorian static uint8_t dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1, 92757419a7fSflorian DHCPDISCOVER}; 928b3441518Sflorian static uint8_t dhcp_hostname[255 + 2] = {DHO_HOST_NAME, 0 /*, ... */}; 92957419a7fSflorian static uint8_t dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7, 93057419a7fSflorian HTYPE_ETHER, 0, 0, 0, 0, 0, 0}; 93157419a7fSflorian static uint8_t dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST, 932ea86c963Sflorian 8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS, 93357419a7fSflorian DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS, 934ea86c963Sflorian DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES}; 9356d7478f0Sflorian static uint8_t dhcp_req_list_v6[] = {DHO_DHCP_PARAMETER_REQUEST_LIST, 9366d7478f0Sflorian 9, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS, 9376d7478f0Sflorian DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS, 9386d7478f0Sflorian DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES, 9396d7478f0Sflorian DHO_IPV6_ONLY_PREFERRED}; 94057419a7fSflorian static uint8_t dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS, 94157419a7fSflorian 4, 0, 0, 0, 0}; 94257419a7fSflorian static uint8_t dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER, 94357419a7fSflorian 4, 0, 0, 0, 0}; 944a41cc082Sflorian #ifndef SMALL 945a41cc082Sflorian struct iface_conf *iface_conf; 946a41cc082Sflorian #endif /* SMALL */ 94757419a7fSflorian struct dhcp_hdr *hdr; 9482d2edb0eSflorian ssize_t len; 94957419a7fSflorian uint8_t *p; 95057419a7fSflorian char *c; 95157419a7fSflorian 952a41cc082Sflorian #ifndef SMALL 953a41cc082Sflorian iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name); 954a41cc082Sflorian #endif /* SMALL */ 955a41cc082Sflorian 95657419a7fSflorian memset(dhcp_packet, 0, sizeof(dhcp_packet)); 95757419a7fSflorian dhcp_message_type[2] = message_type; 95857419a7fSflorian p = dhcp_packet; 95957419a7fSflorian hdr = (struct dhcp_hdr *)p; 96057419a7fSflorian hdr->op = DHCP_BOOTREQUEST; 96157419a7fSflorian hdr->htype = HTYPE_ETHER; 96257419a7fSflorian hdr->hlen = 6; 96357419a7fSflorian hdr->hops = 0; 964c2da98a6Sflorian hdr->xid = htonl(xid); 96557419a7fSflorian hdr->secs = 0; 966b2b40540Sflorian hdr->ciaddr = *ciaddr; 96757419a7fSflorian memcpy(hdr->chaddr, hw_address, sizeof(*hw_address)); 96857419a7fSflorian p += sizeof(struct dhcp_hdr); 96957419a7fSflorian memcpy(p, dhcp_cookie, sizeof(dhcp_cookie)); 97057419a7fSflorian p += sizeof(dhcp_cookie); 97157419a7fSflorian memcpy(p, dhcp_message_type, sizeof(dhcp_message_type)); 97257419a7fSflorian p += sizeof(dhcp_message_type); 973b3441518Sflorian 974b3441518Sflorian #ifndef SMALL 975b3441518Sflorian if (iface_conf != NULL && iface_conf->h_name != NULL) { 976b3441518Sflorian if (iface_conf->h_name[0] != '\0') { 977b3441518Sflorian dhcp_hostname[1] = strlen(iface_conf->h_name); 978b3441518Sflorian memcpy(dhcp_hostname + 2, iface_conf->h_name, 979b3441518Sflorian strlen(iface_conf->h_name)); 980b3441518Sflorian memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2); 981b3441518Sflorian p += dhcp_hostname[1] + 2; 982b3441518Sflorian } 983b3441518Sflorian } else 984b3441518Sflorian #endif /* SMALL */ 985b3441518Sflorian { 986b3441518Sflorian if (gethostname(dhcp_hostname + 2, 9878491c0abSflorian sizeof(dhcp_hostname) - 2) == 0 && 9888491c0abSflorian dhcp_hostname[2] != '\0') { 98957419a7fSflorian if ((c = strchr(dhcp_hostname + 2, '.')) != NULL) 99057419a7fSflorian *c = '\0'; 99157419a7fSflorian dhcp_hostname[1] = strlen(dhcp_hostname + 2); 99257419a7fSflorian memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2); 99357419a7fSflorian p += dhcp_hostname[1] + 2; 99457419a7fSflorian } 995b3441518Sflorian } 996a41cc082Sflorian 997a41cc082Sflorian #ifndef SMALL 998a41cc082Sflorian if (iface_conf != NULL) { 999a41cc082Sflorian if (iface_conf->c_id_len > 0) { 1000a41cc082Sflorian /* XXX check space */ 1001a41cc082Sflorian memcpy(p, iface_conf->c_id, iface_conf->c_id_len); 1002a41cc082Sflorian p += iface_conf->c_id_len; 100390c58abaSflorian } else { 100490c58abaSflorian memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address)); 100590c58abaSflorian memcpy(p, dhcp_client_id, sizeof(dhcp_client_id)); 100690c58abaSflorian p += sizeof(dhcp_client_id); 1007a41cc082Sflorian } 1008a41cc082Sflorian if (iface_conf->vc_id_len > 0) { 1009a41cc082Sflorian /* XXX check space */ 1010a41cc082Sflorian memcpy(p, iface_conf->vc_id, iface_conf->vc_id_len); 1011a41cc082Sflorian p += iface_conf->vc_id_len; 1012a41cc082Sflorian } 10136d7478f0Sflorian if (iface_conf->prefer_ipv6) { 10146d7478f0Sflorian memcpy(p, dhcp_req_list_v6, sizeof(dhcp_req_list_v6)); 10156d7478f0Sflorian p += sizeof(dhcp_req_list_v6); 10166d7478f0Sflorian 10176d7478f0Sflorian } else { 10186d7478f0Sflorian memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); 10196d7478f0Sflorian p += sizeof(dhcp_req_list); 10206d7478f0Sflorian } 1021a41cc082Sflorian } else 1022a41cc082Sflorian #endif /* SMALL */ 1023a41cc082Sflorian { 102457419a7fSflorian memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address)); 102557419a7fSflorian memcpy(p, dhcp_client_id, sizeof(dhcp_client_id)); 102657419a7fSflorian p += sizeof(dhcp_client_id); 102757419a7fSflorian memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); 102857419a7fSflorian p += sizeof(dhcp_req_list); 10296d7478f0Sflorian } 103057419a7fSflorian 103182a5f3d7Sflorian if (requested_ip->s_addr != INADDR_ANY) { 103257419a7fSflorian memcpy(dhcp_requested_address + 2, requested_ip, 103357419a7fSflorian sizeof(*requested_ip)); 103457419a7fSflorian memcpy(p, dhcp_requested_address, 103557419a7fSflorian sizeof(dhcp_requested_address)); 103657419a7fSflorian p += sizeof(dhcp_requested_address); 103782a5f3d7Sflorian } 103857419a7fSflorian 103957419a7fSflorian if (server_identifier->s_addr != INADDR_ANY) { 104057419a7fSflorian memcpy(dhcp_server_identifier + 2, server_identifier, 104157419a7fSflorian sizeof(*server_identifier)); 104257419a7fSflorian memcpy(p, dhcp_server_identifier, 104357419a7fSflorian sizeof(dhcp_server_identifier)); 104457419a7fSflorian p += sizeof(dhcp_server_identifier); 104557419a7fSflorian } 104657419a7fSflorian 104757419a7fSflorian *p = DHO_END; 104857419a7fSflorian p += 1; 104957419a7fSflorian 10502d2edb0eSflorian len = p - dhcp_packet; 10512d2edb0eSflorian 10522d2edb0eSflorian /* dhcp_packet is initialized with DHO_PADs */ 10532d2edb0eSflorian if (len < BOOTP_MIN_LEN) 10542d2edb0eSflorian len = BOOTP_MIN_LEN; 10552d2edb0eSflorian 10562d2edb0eSflorian return (len); 105757419a7fSflorian } 105857419a7fSflorian 105957419a7fSflorian void 106082a5f3d7Sflorian send_packet(uint8_t message_type, struct iface *iface) 106157419a7fSflorian { 106257419a7fSflorian ssize_t pkt_len; 1063e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 106457419a7fSflorian 106557419a7fSflorian if (!event_initialized(&iface->bpfev.ev)) { 106657419a7fSflorian iface->send_discover = 1; 106757419a7fSflorian return; 106857419a7fSflorian } 106982a5f3d7Sflorian 107057419a7fSflorian iface->send_discover = 0; 1071e998cdbeSflorian 107282a5f3d7Sflorian if ((if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf)) == NULL) 107382a5f3d7Sflorian return; /* iface went away, nothing to do */ 1074e998cdbeSflorian 107582a5f3d7Sflorian log_debug("%s on %s", message_type == DHCPDISCOVER ? "DHCPDISCOVER" : 107682a5f3d7Sflorian "DHCPREQUEST", if_name); 107757419a7fSflorian 107882a5f3d7Sflorian pkt_len = build_packet(message_type, if_name, iface->xid, 107982a5f3d7Sflorian &iface->ifinfo.hw_address, &iface->ciaddr, &iface->requested_ip, 10801bbf741cSflorian &iface->server_identifier); 1081c645071eSflorian if (iface->dhcp_server.s_addr != INADDR_ANY) { 1082c645071eSflorian if (udp_send_packet(iface, dhcp_packet, pkt_len) == -1) 1083c645071eSflorian bpf_send_packet(iface, dhcp_packet, pkt_len); 1084c645071eSflorian } else 108557419a7fSflorian bpf_send_packet(iface, dhcp_packet, pkt_len); 108657419a7fSflorian } 108757419a7fSflorian 1088c645071eSflorian int 108957419a7fSflorian udp_send_packet(struct iface *iface, uint8_t *packet, ssize_t len) 109057419a7fSflorian { 109157419a7fSflorian struct sockaddr_in to; 109257419a7fSflorian 109357419a7fSflorian memset(&to, 0, sizeof(to)); 109457419a7fSflorian to.sin_family = AF_INET; 109557419a7fSflorian to.sin_len = sizeof(to); 1096b2b40540Sflorian to.sin_addr = iface->dhcp_server; 109757419a7fSflorian to.sin_port = ntohs(SERVER_PORT); 109857419a7fSflorian 109957419a7fSflorian if (sendto(iface->udpsock, packet, len, 0, (struct sockaddr *)&to, 1100c645071eSflorian sizeof(to)) == -1) { 110157419a7fSflorian log_warn("sendto"); 1102c645071eSflorian return -1; 1103c645071eSflorian } 1104c645071eSflorian return 0; 110557419a7fSflorian } 110657419a7fSflorian void 110757419a7fSflorian bpf_send_packet(struct iface *iface, uint8_t *packet, ssize_t len) 110857419a7fSflorian { 110957419a7fSflorian struct iovec iov[4]; 111057419a7fSflorian struct ether_header eh; 111157419a7fSflorian struct ip ip; 111257419a7fSflorian struct udphdr udp; 111357419a7fSflorian ssize_t total, result; 111457419a7fSflorian int iovcnt = 0, i; 111557419a7fSflorian 111657419a7fSflorian memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost)); 11171bbf741cSflorian memcpy(eh.ether_shost, &iface->ifinfo.hw_address, 11181bbf741cSflorian sizeof(eh.ether_dhost)); 111957419a7fSflorian eh.ether_type = htons(ETHERTYPE_IP); 112057419a7fSflorian iov[0].iov_base = &eh; 112157419a7fSflorian iov[0].iov_len = sizeof(eh); 112257419a7fSflorian iovcnt++; 112357419a7fSflorian 112457419a7fSflorian ip.ip_v = 4; 112557419a7fSflorian ip.ip_hl = 5; 112657419a7fSflorian ip.ip_tos = IPTOS_LOWDELAY; 112757419a7fSflorian ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); 112857419a7fSflorian ip.ip_id = 0; 112957419a7fSflorian ip.ip_off = 0; 113057419a7fSflorian ip.ip_ttl = 128; 113157419a7fSflorian ip.ip_p = IPPROTO_UDP; 113257419a7fSflorian ip.ip_sum = 0; 1133b2b40540Sflorian ip.ip_src.s_addr = INADDR_ANY; 113457419a7fSflorian ip.ip_dst.s_addr = INADDR_BROADCAST; 113557419a7fSflorian ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); 113657419a7fSflorian iov[iovcnt].iov_base = &ip; 113757419a7fSflorian iov[iovcnt].iov_len = sizeof(ip); 113857419a7fSflorian iovcnt++; 113957419a7fSflorian 114057419a7fSflorian udp.uh_sport = htons(CLIENT_PORT); 114157419a7fSflorian udp.uh_dport = htons(SERVER_PORT); 114257419a7fSflorian udp.uh_ulen = htons(sizeof(udp) + len); 114357419a7fSflorian udp.uh_sum = 0; 114457419a7fSflorian udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), 114557419a7fSflorian checksum((unsigned char *)packet, len, 114657419a7fSflorian checksum((unsigned char *)&ip.ip_src, 114757419a7fSflorian 2 * sizeof(ip.ip_src), 114857419a7fSflorian IPPROTO_UDP + (uint32_t)ntohs(udp.uh_ulen))))); 114957419a7fSflorian iov[iovcnt].iov_base = &udp; 115057419a7fSflorian iov[iovcnt].iov_len = sizeof(udp); 115157419a7fSflorian iovcnt++; 115257419a7fSflorian 115357419a7fSflorian iov[iovcnt].iov_base = packet; 115457419a7fSflorian iov[iovcnt].iov_len = len; 115557419a7fSflorian iovcnt++; 115657419a7fSflorian 115757419a7fSflorian total = 0; 115857419a7fSflorian for (i = 0; i < iovcnt; i++) 115957419a7fSflorian total += iov[i].iov_len; 116057419a7fSflorian 116157419a7fSflorian result = writev(EVENT_FD(&iface->bpfev.ev), iov, iovcnt); 116257419a7fSflorian if (result == -1) 116357419a7fSflorian log_warn("%s: writev", __func__); 116457419a7fSflorian else if (result < total) { 116557419a7fSflorian log_warnx("%s, writev: %zd of %zd bytes", __func__, result, 116657419a7fSflorian total); 116757419a7fSflorian } 116857419a7fSflorian } 116957419a7fSflorian 117057419a7fSflorian struct iface* 117157419a7fSflorian get_iface_by_id(uint32_t if_index) 117257419a7fSflorian { 117357419a7fSflorian struct iface *iface; 117457419a7fSflorian 117557419a7fSflorian LIST_FOREACH (iface, &interfaces, entries) { 11761bbf741cSflorian if (iface->ifinfo.if_index == if_index) 117757419a7fSflorian return (iface); 117857419a7fSflorian } 117957419a7fSflorian 118057419a7fSflorian return (NULL); 118157419a7fSflorian } 118257419a7fSflorian 118357419a7fSflorian void 118457419a7fSflorian remove_iface(uint32_t if_index) 118557419a7fSflorian { 118657419a7fSflorian struct iface *iface; 118757419a7fSflorian 118857419a7fSflorian iface = get_iface_by_id(if_index); 118957419a7fSflorian 119057419a7fSflorian if (iface == NULL) 119157419a7fSflorian return; 119257419a7fSflorian 119357419a7fSflorian LIST_REMOVE(iface, entries); 1194c4c62a46Sflorian if (event_initialized(&iface->bpfev.ev)) { 119557419a7fSflorian event_del(&iface->bpfev.ev); 119657419a7fSflorian close(EVENT_FD(&iface->bpfev.ev)); 1197c4c62a46Sflorian } 119857419a7fSflorian if (iface->udpsock != -1) 119957419a7fSflorian close(iface->udpsock); 120057419a7fSflorian free(iface); 120157419a7fSflorian } 120257419a7fSflorian 120357419a7fSflorian void 120457419a7fSflorian set_bpfsock(int bpfsock, uint32_t if_index) 120557419a7fSflorian { 120657419a7fSflorian struct iface *iface; 120757419a7fSflorian 1208c4c62a46Sflorian iface = get_iface_by_id(if_index); 1209c4c62a46Sflorian 1210c4c62a46Sflorian if (iface == NULL) { 121157419a7fSflorian /* 121257419a7fSflorian * The interface disappeared while we were waiting for the 1213c4c62a46Sflorian * parent process to open the bpf socket. 1214c4c62a46Sflorian */ 1215c4c62a46Sflorian close(bpfsock); 1216c4c62a46Sflorian } else if (event_initialized(&iface->bpfev.ev)) { 1217c4c62a46Sflorian /* 1218c4c62a46Sflorian * The autoconf flag is flapping and we have multiple bpf sockets in 1219c4c62a46Sflorian * flight. We don't need this one because we already got one. 122057419a7fSflorian */ 122157419a7fSflorian close(bpfsock); 122257419a7fSflorian } else { 122357419a7fSflorian event_set(&iface->bpfev.ev, bpfsock, EV_READ | 122457419a7fSflorian EV_PERSIST, bpf_receive, iface); 122557419a7fSflorian event_add(&iface->bpfev.ev, NULL); 122657419a7fSflorian if (iface->send_discover) 122782a5f3d7Sflorian send_packet(DHCPDISCOVER, iface); 122857419a7fSflorian } 122957419a7fSflorian } 1230a41cc082Sflorian 1231a41cc082Sflorian #ifndef SMALL 1232a41cc082Sflorian struct iface_conf* 1233a41cc082Sflorian find_iface_conf(struct iface_conf_head *head, char *if_name) 1234a41cc082Sflorian { 1235a41cc082Sflorian struct iface_conf *iface_conf; 1236a41cc082Sflorian 1237a41cc082Sflorian if (if_name == NULL) 1238a41cc082Sflorian return (NULL); 1239a41cc082Sflorian 1240a41cc082Sflorian SIMPLEQ_FOREACH(iface_conf, head, entry) { 1241a41cc082Sflorian if (strcmp(iface_conf->name, if_name) == 0) 1242a41cc082Sflorian return iface_conf; 1243a41cc082Sflorian } 1244a41cc082Sflorian return (NULL); 1245a41cc082Sflorian } 1246580befd2Sflorian 1247580befd2Sflorian int* 1248580befd2Sflorian changed_ifaces(struct dhcpleased_conf *oconf, struct dhcpleased_conf *nconf) 1249580befd2Sflorian { 1250580befd2Sflorian struct iface_conf *iface_conf, *oiface_conf; 1251580befd2Sflorian int *ret, if_index, count = 0, i = 0; 1252580befd2Sflorian 1253580befd2Sflorian /* 1254580befd2Sflorian * Worst case: All old interfaces replaced with new interfaces. 1255580befd2Sflorian * This should still be a small number 1256580befd2Sflorian */ 1257580befd2Sflorian SIMPLEQ_FOREACH(iface_conf, &oconf->iface_list, entry) 1258580befd2Sflorian count++; 1259580befd2Sflorian SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) 1260580befd2Sflorian count++; 1261580befd2Sflorian 1262580befd2Sflorian ret = calloc(count + 1, sizeof(int)); 1263580befd2Sflorian 1264580befd2Sflorian SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) { 1265580befd2Sflorian if ((if_index = if_nametoindex(iface_conf->name)) == 0) 1266580befd2Sflorian continue; 1267580befd2Sflorian oiface_conf = find_iface_conf(&oconf->iface_list, 1268580befd2Sflorian iface_conf->name); 1269580befd2Sflorian if (oiface_conf == NULL) { 1270580befd2Sflorian /* new interface added to config */ 1271580befd2Sflorian ret[i++] = if_index; 1272580befd2Sflorian } else if (iface_conf_cmp(iface_conf, oiface_conf) != 0) { 1273580befd2Sflorian /* interface conf changed */ 1274580befd2Sflorian ret[i++] = if_index; 1275580befd2Sflorian } 1276580befd2Sflorian } 1277580befd2Sflorian SIMPLEQ_FOREACH(oiface_conf, &oconf->iface_list, entry) { 1278580befd2Sflorian if ((if_index = if_nametoindex(oiface_conf->name)) == 0) 1279580befd2Sflorian continue; 1280580befd2Sflorian if (find_iface_conf(&nconf->iface_list, oiface_conf->name) == 1281580befd2Sflorian NULL) { 1282580befd2Sflorian /* interface removed from config */ 1283580befd2Sflorian ret[i++] = if_index; 1284580befd2Sflorian } 1285580befd2Sflorian } 1286580befd2Sflorian return ret; 1287580befd2Sflorian } 1288580befd2Sflorian 1289580befd2Sflorian int 1290580befd2Sflorian iface_conf_cmp(struct iface_conf *a, struct iface_conf *b) 1291580befd2Sflorian { 1292580befd2Sflorian if (a->vc_id_len != b->vc_id_len) 1293580befd2Sflorian return 1; 1294580befd2Sflorian if (memcmp(a->vc_id, b->vc_id, a->vc_id_len) != 0) 1295580befd2Sflorian return 1; 1296580befd2Sflorian if (a->c_id_len != b->c_id_len) 1297580befd2Sflorian return 1; 1298580befd2Sflorian if (memcmp(a->c_id, b->c_id, a->c_id_len) != 0) 1299580befd2Sflorian return 1; 1300b3441518Sflorian if (a->h_name == NULL || b->h_name == NULL) 1301b3441518Sflorian return 1; 1302b3441518Sflorian if (strcmp(a->h_name, b->h_name) != 0) 1303b3441518Sflorian return 1; 1304c2bc6c6dSflorian if (a->ignore != b->ignore) 1305c2bc6c6dSflorian return 1; 1306c2bc6c6dSflorian if (a->ignore_servers_len != b->ignore_servers_len) 1307c2bc6c6dSflorian return 1; 1308c2bc6c6dSflorian if (memcmp(a->ignore_servers, b->ignore_servers, 1309c2bc6c6dSflorian a->ignore_servers_len * sizeof (struct in_addr)) != 0) 1310c2bc6c6dSflorian return 1; 1311580befd2Sflorian return 0; 1312580befd2Sflorian } 1313a41cc082Sflorian #endif /* SMALL */ 1314