1*0e59d0d1Sclaudio /* $OpenBSD: engine.c,v 1.55 2024/11/21 13:35:20 claudio Exp $ */ 257419a7fSflorian 357419a7fSflorian /* 457419a7fSflorian * Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org> 557419a7fSflorian * Copyright (c) 2004, 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 2257419a7fSflorian #include <sys/types.h> 2357419a7fSflorian #include <sys/queue.h> 2457419a7fSflorian #include <sys/socket.h> 2557419a7fSflorian #include <sys/syslog.h> 2657419a7fSflorian #include <sys/uio.h> 272b86dc95Sjan #include <sys/mbuf.h> 2857419a7fSflorian 2957419a7fSflorian #include <net/if.h> 3057419a7fSflorian #include <net/route.h> 3157419a7fSflorian #include <arpa/inet.h> 3257419a7fSflorian #include <netinet/in.h> 3357419a7fSflorian #include <netinet/if_ether.h> 3457419a7fSflorian #include <netinet/ip.h> 3557419a7fSflorian #include <netinet/udp.h> 3657419a7fSflorian 3757419a7fSflorian #include <errno.h> 3857419a7fSflorian #include <event.h> 3957419a7fSflorian #include <imsg.h> 4057419a7fSflorian #include <pwd.h> 4157419a7fSflorian #include <signal.h> 4257419a7fSflorian #include <stddef.h> 430bc729e2Sflorian #include <stdio.h> 4457419a7fSflorian #include <stdlib.h> 4557419a7fSflorian #include <string.h> 4657419a7fSflorian #include <time.h> 4757419a7fSflorian #include <unistd.h> 4857419a7fSflorian #include <vis.h> 4957419a7fSflorian 5057419a7fSflorian #include "checksum.h" 5157419a7fSflorian #include "log.h" 5257419a7fSflorian #include "dhcpleased.h" 5357419a7fSflorian #include "engine.h" 5457419a7fSflorian 55acf27097Sflorian /* 56acf27097Sflorian * RFC 2131 4.1 p23 has "SHOULD be 4 seconds", we are a bit more aggressive, 57acf27097Sflorian * networks are faster these days. 58acf27097Sflorian */ 59acf27097Sflorian #define START_EXP_BACKOFF 1 608baf56acSflorian #define MAX_EXP_BACKOFF_SLOW 64 /* RFC 2131 4.1 p23 */ 618baf56acSflorian #define MAX_EXP_BACKOFF_FAST 2 6257419a7fSflorian #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 6357419a7fSflorian 6457419a7fSflorian enum if_state { 6557419a7fSflorian IF_DOWN, 6657419a7fSflorian IF_INIT, 6757419a7fSflorian /* IF_SELECTING, */ 6857419a7fSflorian IF_REQUESTING, 6957419a7fSflorian IF_BOUND, 7057419a7fSflorian IF_RENEWING, 7157419a7fSflorian IF_REBINDING, 7257419a7fSflorian /* IF_INIT_REBOOT, */ 7357419a7fSflorian IF_REBOOTING, 746d7478f0Sflorian IF_IPV6_ONLY, 7557419a7fSflorian }; 7657419a7fSflorian 7757419a7fSflorian const char* if_state_name[] = { 7857419a7fSflorian "Down", 7957419a7fSflorian "Init", 8057419a7fSflorian /* "Selecting", */ 8157419a7fSflorian "Requesting", 8257419a7fSflorian "Bound", 8357419a7fSflorian "Renewing", 8457419a7fSflorian "Rebinding", 8557419a7fSflorian /* "Init-Reboot", */ 8657419a7fSflorian "Rebooting", 876d7478f0Sflorian "IPv6 only", 8857419a7fSflorian }; 8957419a7fSflorian 9057419a7fSflorian struct dhcpleased_iface { 9157419a7fSflorian LIST_ENTRY(dhcpleased_iface) entries; 9257419a7fSflorian enum if_state state; 9357419a7fSflorian struct event timer; 9457419a7fSflorian struct timeval timo; 9557419a7fSflorian uint32_t if_index; 9657419a7fSflorian int rdomain; 9757419a7fSflorian int running; 9857419a7fSflorian struct ether_addr hw_address; 9957419a7fSflorian int link_state; 10057419a7fSflorian uint32_t cur_mtu; 10157419a7fSflorian uint32_t xid; 10257419a7fSflorian struct timespec request_time; 10357419a7fSflorian struct in_addr server_identifier; 10457419a7fSflorian struct in_addr dhcp_server; /* for unicast */ 10557419a7fSflorian struct in_addr requested_ip; 10657419a7fSflorian struct in_addr mask; 107ebace80cSflorian struct in_addr siaddr; 108ebace80cSflorian char file[4 * DHCP_FILE_LEN + 1]; 109ebace80cSflorian char hostname[4 * 255 + 1]; 110ebace80cSflorian char domainname[4 * 255 + 1]; 1116aeacae8Sflorian struct dhcp_route prev_routes[MAX_DHCP_ROUTES]; 1126aeacae8Sflorian int prev_routes_len; 113351dd593Sflorian struct dhcp_route routes[MAX_DHCP_ROUTES]; 114351dd593Sflorian int routes_len; 11557419a7fSflorian struct in_addr nameservers[MAX_RDNS_COUNT]; 11657419a7fSflorian uint32_t lease_time; 11757419a7fSflorian uint32_t renewal_time; 11857419a7fSflorian uint32_t rebinding_time; 1196d7478f0Sflorian uint32_t ipv6_only_time; 12057419a7fSflorian }; 12157419a7fSflorian 12257419a7fSflorian LIST_HEAD(, dhcpleased_iface) dhcpleased_interfaces; 12357419a7fSflorian 12457419a7fSflorian __dead void engine_shutdown(void); 12557419a7fSflorian void engine_sig_handler(int sig, short, void *); 12657419a7fSflorian void engine_dispatch_frontend(int, short, void *); 12757419a7fSflorian void engine_dispatch_main(int, short, void *); 12857419a7fSflorian #ifndef SMALL 12957419a7fSflorian void send_interface_info(struct dhcpleased_iface *, pid_t); 13045c5e5adSflorian void engine_showinfo_ctl(pid_t, uint32_t); 13157419a7fSflorian #endif /* SMALL */ 1326e93e3e9Sflorian void engine_update_iface(struct imsg_ifinfo *); 13357419a7fSflorian struct dhcpleased_iface *get_dhcpleased_iface_by_id(uint32_t); 13457419a7fSflorian void remove_dhcpleased_iface(uint32_t); 13557419a7fSflorian void parse_dhcp(struct dhcpleased_iface *, 13657419a7fSflorian struct imsg_dhcp *); 13757419a7fSflorian void state_transition(struct dhcpleased_iface *, enum 13857419a7fSflorian if_state); 13957419a7fSflorian void iface_timeout(int, short, void *); 14057419a7fSflorian void request_dhcp_discover(struct dhcpleased_iface *); 14157419a7fSflorian void request_dhcp_request(struct dhcpleased_iface *); 1420bc729e2Sflorian void log_lease(struct dhcpleased_iface *, int); 1430bc729e2Sflorian void log_rdns(struct dhcpleased_iface *, int); 14457419a7fSflorian void send_configure_interface(struct dhcpleased_iface *); 14557419a7fSflorian void send_rdns_proposal(struct dhcpleased_iface *); 14657419a7fSflorian void send_deconfigure_interface(struct dhcpleased_iface *); 14757419a7fSflorian void send_rdns_withdraw(struct dhcpleased_iface *); 148c2bc6c6dSflorian void send_routes_withdraw(struct dhcpleased_iface *); 1496e93e3e9Sflorian void parse_lease(struct dhcpleased_iface *, 1506e93e3e9Sflorian struct imsg_ifinfo *); 15157419a7fSflorian int engine_imsg_compose_main(int, pid_t, void *, uint16_t); 152e998cdbeSflorian void log_dhcp_hdr(struct dhcp_hdr *); 153e998cdbeSflorian const char *dhcp_message_type2str(uint8_t); 15457419a7fSflorian 155a41cc082Sflorian #ifndef SMALL 156a41cc082Sflorian struct dhcpleased_conf *engine_conf; 157a41cc082Sflorian #endif /* SMALL */ 158a41cc082Sflorian 15957419a7fSflorian static struct imsgev *iev_frontend; 16057419a7fSflorian static struct imsgev *iev_main; 16157419a7fSflorian int64_t proposal_id; 16257419a7fSflorian 16357419a7fSflorian void 16457419a7fSflorian engine_sig_handler(int sig, short event, void *arg) 16557419a7fSflorian { 16657419a7fSflorian /* 16757419a7fSflorian * Normal signal handler rules don't apply because libevent 16857419a7fSflorian * decouples for us. 16957419a7fSflorian */ 17057419a7fSflorian 17157419a7fSflorian switch (sig) { 17257419a7fSflorian case SIGINT: 17357419a7fSflorian case SIGTERM: 17457419a7fSflorian engine_shutdown(); 17557419a7fSflorian default: 17657419a7fSflorian fatalx("unexpected signal"); 17757419a7fSflorian } 17857419a7fSflorian } 17957419a7fSflorian 18057419a7fSflorian void 18157419a7fSflorian engine(int debug, int verbose) 18257419a7fSflorian { 18357419a7fSflorian struct event ev_sigint, ev_sigterm; 18457419a7fSflorian struct passwd *pw; 18557419a7fSflorian 186a41cc082Sflorian #ifndef SMALL 187a41cc082Sflorian engine_conf = config_new_empty(); 188a41cc082Sflorian #endif /* SMALL */ 189a41cc082Sflorian 19057419a7fSflorian log_init(debug, LOG_DAEMON); 19157419a7fSflorian log_setverbose(verbose); 19257419a7fSflorian 19357419a7fSflorian if ((pw = getpwnam(DHCPLEASED_USER)) == NULL) 19457419a7fSflorian fatal("getpwnam"); 19557419a7fSflorian 19657419a7fSflorian if (chdir("/") == -1) 19757419a7fSflorian fatal("chdir(\"/\")"); 19857419a7fSflorian 199c7d84514Sflorian if (unveil("/", "") == -1) 200bc5a8259Sbeck fatal("unveil /"); 201c7d84514Sflorian if (unveil(NULL, NULL) == -1) 202bc5a8259Sbeck fatal("unveil"); 203c7d84514Sflorian 20457419a7fSflorian setproctitle("%s", "engine"); 20557419a7fSflorian log_procinit("engine"); 20657419a7fSflorian 20757419a7fSflorian if (setgroups(1, &pw->pw_gid) || 20857419a7fSflorian setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 20957419a7fSflorian setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 21057419a7fSflorian fatal("can't drop privileges"); 21157419a7fSflorian 21257419a7fSflorian if (pledge("stdio recvfd", NULL) == -1) 21357419a7fSflorian fatal("pledge"); 21457419a7fSflorian 21557419a7fSflorian event_init(); 21657419a7fSflorian 21757419a7fSflorian /* Setup signal handler(s). */ 21857419a7fSflorian signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL); 21957419a7fSflorian signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL); 22057419a7fSflorian signal_add(&ev_sigint, NULL); 22157419a7fSflorian signal_add(&ev_sigterm, NULL); 22257419a7fSflorian signal(SIGPIPE, SIG_IGN); 22357419a7fSflorian signal(SIGHUP, SIG_IGN); 22457419a7fSflorian 22557419a7fSflorian /* Setup pipe and event handler to the main process. */ 22657419a7fSflorian if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) 22757419a7fSflorian fatal(NULL); 22857419a7fSflorian 229*0e59d0d1Sclaudio if (imsgbuf_init(&iev_main->ibuf, 3) == -1) 230*0e59d0d1Sclaudio fatal(NULL); 231*0e59d0d1Sclaudio imsgbuf_allow_fdpass(&iev_main->ibuf); 23257419a7fSflorian iev_main->handler = engine_dispatch_main; 23357419a7fSflorian 23457419a7fSflorian /* Setup event handlers. */ 23557419a7fSflorian iev_main->events = EV_READ; 23657419a7fSflorian event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, 23757419a7fSflorian iev_main->handler, iev_main); 23857419a7fSflorian event_add(&iev_main->ev, NULL); 23957419a7fSflorian 24057419a7fSflorian LIST_INIT(&dhcpleased_interfaces); 24157419a7fSflorian 24257419a7fSflorian event_dispatch(); 24357419a7fSflorian 24457419a7fSflorian engine_shutdown(); 24557419a7fSflorian } 24657419a7fSflorian 24757419a7fSflorian __dead void 24857419a7fSflorian engine_shutdown(void) 24957419a7fSflorian { 25057419a7fSflorian /* Close pipes. */ 2519cbf9e90Sclaudio imsgbuf_clear(&iev_frontend->ibuf); 25257419a7fSflorian close(iev_frontend->ibuf.fd); 2539cbf9e90Sclaudio imsgbuf_clear(&iev_main->ibuf); 25457419a7fSflorian close(iev_main->ibuf.fd); 25557419a7fSflorian 25657419a7fSflorian free(iev_frontend); 25757419a7fSflorian free(iev_main); 25857419a7fSflorian 25957419a7fSflorian log_info("engine exiting"); 26057419a7fSflorian exit(0); 26157419a7fSflorian } 26257419a7fSflorian 26357419a7fSflorian int 26457419a7fSflorian engine_imsg_compose_frontend(int type, pid_t pid, void *data, 26557419a7fSflorian uint16_t datalen) 26657419a7fSflorian { 26757419a7fSflorian return (imsg_compose_event(iev_frontend, type, 0, pid, -1, 26857419a7fSflorian data, datalen)); 26957419a7fSflorian } 27057419a7fSflorian 27157419a7fSflorian int 27257419a7fSflorian engine_imsg_compose_main(int type, pid_t pid, void *data, 27357419a7fSflorian uint16_t datalen) 27457419a7fSflorian { 27557419a7fSflorian return (imsg_compose_event(iev_main, type, 0, pid, -1, 27657419a7fSflorian data, datalen)); 27757419a7fSflorian } 27857419a7fSflorian 27957419a7fSflorian void 28057419a7fSflorian engine_dispatch_frontend(int fd, short event, void *bula) 28157419a7fSflorian { 28257419a7fSflorian struct imsgev *iev = bula; 28357419a7fSflorian struct imsgbuf *ibuf = &iev->ibuf; 28457419a7fSflorian struct imsg imsg; 28557419a7fSflorian struct dhcpleased_iface *iface; 28657419a7fSflorian ssize_t n; 28757419a7fSflorian int shut = 0; 28857419a7fSflorian #ifndef SMALL 28957419a7fSflorian int verbose; 29057419a7fSflorian #endif /* SMALL */ 29145c5e5adSflorian uint32_t if_index, type; 29257419a7fSflorian 29357419a7fSflorian if (event & EV_READ) { 294668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 295dd7efffeSclaudio fatal("imsgbuf_read error"); 29657419a7fSflorian if (n == 0) /* Connection closed. */ 29757419a7fSflorian shut = 1; 29857419a7fSflorian } 29957419a7fSflorian if (event & EV_WRITE) { 300dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 301e3b6409cSclaudio if (errno == EPIPE) /* Connection closed. */ 30257419a7fSflorian shut = 1; 303e3b6409cSclaudio else 304dd7efffeSclaudio fatal("imsgbuf_write"); 305e3b6409cSclaudio } 30657419a7fSflorian } 30757419a7fSflorian 30857419a7fSflorian for (;;) { 30957419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 31057419a7fSflorian fatal("%s: imsg_get error", __func__); 31157419a7fSflorian if (n == 0) /* No more messages. */ 31257419a7fSflorian break; 31357419a7fSflorian 31445c5e5adSflorian type = imsg_get_type(&imsg); 31545c5e5adSflorian 31645c5e5adSflorian switch (type) { 31757419a7fSflorian #ifndef SMALL 31857419a7fSflorian case IMSG_CTL_LOG_VERBOSE: 31945c5e5adSflorian if (imsg_get_data(&imsg, &verbose, 32045c5e5adSflorian sizeof(verbose)) == -1) 32145c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 32245c5e5adSflorian 32357419a7fSflorian log_setverbose(verbose); 32457419a7fSflorian break; 32557419a7fSflorian case IMSG_CTL_SHOW_INTERFACE_INFO: 32645c5e5adSflorian if (imsg_get_data(&imsg, &if_index, 32745c5e5adSflorian sizeof(if_index)) == -1) 32845c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 32945c5e5adSflorian 33045c5e5adSflorian engine_showinfo_ctl(imsg_get_pid(&imsg), if_index); 33157419a7fSflorian break; 332c7313d44Sflorian case IMSG_REQUEST_REBOOT: 33345c5e5adSflorian if (imsg_get_data(&imsg, &if_index, 33445c5e5adSflorian sizeof(if_index)) == -1) 33545c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 33645c5e5adSflorian 33757419a7fSflorian iface = get_dhcpleased_iface_by_id(if_index); 33857419a7fSflorian if (iface != NULL) { 33957419a7fSflorian switch (iface->state) { 34057419a7fSflorian case IF_DOWN: 34157419a7fSflorian break; 34257419a7fSflorian case IF_INIT: 34357419a7fSflorian case IF_REQUESTING: 3441c0be873Sflorian state_transition(iface, iface->state); 3451c0be873Sflorian break; 34657419a7fSflorian case IF_RENEWING: 34757419a7fSflorian case IF_REBINDING: 34857419a7fSflorian case IF_REBOOTING: 34957419a7fSflorian case IF_BOUND: 3506d7478f0Sflorian case IF_IPV6_ONLY: 3511c0be873Sflorian state_transition(iface, IF_REBOOTING); 35257419a7fSflorian break; 35357419a7fSflorian } 35457419a7fSflorian } 35557419a7fSflorian break; 35657419a7fSflorian #endif /* SMALL */ 35757419a7fSflorian case IMSG_REMOVE_IF: 35845c5e5adSflorian if (imsg_get_data(&imsg, &if_index, 35945c5e5adSflorian sizeof(if_index)) == -1) 36045c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 36145c5e5adSflorian 36257419a7fSflorian remove_dhcpleased_iface(if_index); 36357419a7fSflorian break; 36457419a7fSflorian case IMSG_DHCP: { 36557419a7fSflorian struct imsg_dhcp imsg_dhcp; 36645c5e5adSflorian 36745c5e5adSflorian if (imsg_get_data(&imsg, &imsg_dhcp, 36845c5e5adSflorian sizeof(imsg_dhcp)) == -1) 36945c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 37045c5e5adSflorian 37157419a7fSflorian iface = get_dhcpleased_iface_by_id(imsg_dhcp.if_index); 37257419a7fSflorian if (iface != NULL) 37357419a7fSflorian parse_dhcp(iface, &imsg_dhcp); 37457419a7fSflorian break; 37557419a7fSflorian } 37657419a7fSflorian case IMSG_REPROPOSE_RDNS: 37757419a7fSflorian LIST_FOREACH (iface, &dhcpleased_interfaces, entries) 37857419a7fSflorian send_rdns_proposal(iface); 37957419a7fSflorian break; 38057419a7fSflorian default: 38145c5e5adSflorian log_debug("%s: unexpected imsg %d", __func__, type); 38257419a7fSflorian break; 38357419a7fSflorian } 38457419a7fSflorian imsg_free(&imsg); 38557419a7fSflorian } 38657419a7fSflorian if (!shut) 38757419a7fSflorian imsg_event_add(iev); 38857419a7fSflorian else { 38957419a7fSflorian /* This pipe is dead. Remove its event handler. */ 39057419a7fSflorian event_del(&iev->ev); 39157419a7fSflorian event_loopexit(NULL); 39257419a7fSflorian } 39357419a7fSflorian } 39457419a7fSflorian 39557419a7fSflorian void 39657419a7fSflorian engine_dispatch_main(int fd, short event, void *bula) 39757419a7fSflorian { 398a41cc082Sflorian #ifndef SMALL 399a41cc082Sflorian static struct dhcpleased_conf *nconf; 400a41cc082Sflorian static struct iface_conf *iface_conf; 401a41cc082Sflorian #endif /* SMALL */ 40257419a7fSflorian struct imsg imsg; 40357419a7fSflorian struct imsgev *iev = bula; 40457419a7fSflorian struct imsgbuf *ibuf = &iev->ibuf; 40557419a7fSflorian struct imsg_ifinfo imsg_ifinfo; 40657419a7fSflorian ssize_t n; 40745c5e5adSflorian uint32_t type; 40857419a7fSflorian int shut = 0; 40957419a7fSflorian 41057419a7fSflorian if (event & EV_READ) { 411668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 412dd7efffeSclaudio fatal("imsgbuf_read error"); 41357419a7fSflorian if (n == 0) /* Connection closed. */ 41457419a7fSflorian shut = 1; 41557419a7fSflorian } 41657419a7fSflorian if (event & EV_WRITE) { 417dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 418e3b6409cSclaudio if (errno == EPIPE) /* Connection closed. */ 41957419a7fSflorian shut = 1; 420e3b6409cSclaudio else 421dd7efffeSclaudio fatal("imsgbuf_write"); 422e3b6409cSclaudio } 42357419a7fSflorian } 42457419a7fSflorian 42557419a7fSflorian for (;;) { 42657419a7fSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 42757419a7fSflorian fatal("%s: imsg_get error", __func__); 42857419a7fSflorian if (n == 0) /* No more messages. */ 42957419a7fSflorian break; 43057419a7fSflorian 43145c5e5adSflorian type = imsg_get_type(&imsg); 43245c5e5adSflorian 43345c5e5adSflorian switch (type) { 43457419a7fSflorian case IMSG_SOCKET_IPC: 43557419a7fSflorian /* 43657419a7fSflorian * Setup pipe and event handler to the frontend 43757419a7fSflorian * process. 43857419a7fSflorian */ 43957419a7fSflorian if (iev_frontend) 44057419a7fSflorian fatalx("%s: received unexpected imsg fd " 44157419a7fSflorian "to engine", __func__); 44257419a7fSflorian 443dcedc8acSclaudio if ((fd = imsg_get_fd(&imsg)) == -1) 44457419a7fSflorian fatalx("%s: expected to receive imsg fd to " 44557419a7fSflorian "engine but didn't receive any", __func__); 44657419a7fSflorian 44757419a7fSflorian iev_frontend = malloc(sizeof(struct imsgev)); 44857419a7fSflorian if (iev_frontend == NULL) 44957419a7fSflorian fatal(NULL); 45057419a7fSflorian 451*0e59d0d1Sclaudio if (imsgbuf_init(&iev_frontend->ibuf, fd) == -1) 452*0e59d0d1Sclaudio fatal(NULL); 45357419a7fSflorian iev_frontend->handler = engine_dispatch_frontend; 45457419a7fSflorian iev_frontend->events = EV_READ; 45557419a7fSflorian 45657419a7fSflorian event_set(&iev_frontend->ev, iev_frontend->ibuf.fd, 45757419a7fSflorian iev_frontend->events, iev_frontend->handler, 45857419a7fSflorian iev_frontend); 45957419a7fSflorian event_add(&iev_frontend->ev, NULL); 4606e93e3e9Sflorian 4616e93e3e9Sflorian if (pledge("stdio", NULL) == -1) 4626e93e3e9Sflorian fatal("pledge"); 4636e93e3e9Sflorian 46457419a7fSflorian break; 46557419a7fSflorian case IMSG_UPDATE_IF: 46645c5e5adSflorian if (imsg_get_data(&imsg, &imsg_ifinfo, 46745c5e5adSflorian sizeof(imsg_ifinfo)) == -1) 46845c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 469159ce6f4Sflorian if (imsg_ifinfo.lease[LEASE_SIZE - 1] != '\0') 470159ce6f4Sflorian fatalx("Invalid lease"); 47145c5e5adSflorian 4726e93e3e9Sflorian engine_update_iface(&imsg_ifinfo); 47357419a7fSflorian break; 474a41cc082Sflorian #ifndef SMALL 475a41cc082Sflorian case IMSG_RECONF_CONF: 476a41cc082Sflorian if (nconf != NULL) 477a41cc082Sflorian fatalx("%s: IMSG_RECONF_CONF already in " 478a41cc082Sflorian "progress", __func__); 479a41cc082Sflorian if ((nconf = malloc(sizeof(struct dhcpleased_conf))) == 480a41cc082Sflorian NULL) 481a41cc082Sflorian fatal(NULL); 482a41cc082Sflorian SIMPLEQ_INIT(&nconf->iface_list); 483a41cc082Sflorian break; 484a41cc082Sflorian case IMSG_RECONF_IFACE: 485a41cc082Sflorian if ((iface_conf = malloc(sizeof(struct iface_conf))) 486a41cc082Sflorian == NULL) 487a41cc082Sflorian fatal(NULL); 48845c5e5adSflorian 48945c5e5adSflorian if (imsg_get_data(&imsg, iface_conf, 49045c5e5adSflorian sizeof(struct iface_conf)) == -1) 49145c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 49245c5e5adSflorian 493a41cc082Sflorian iface_conf->vc_id = NULL; 494a41cc082Sflorian iface_conf->vc_id_len = 0; 495a41cc082Sflorian iface_conf->c_id = NULL; 496a41cc082Sflorian iface_conf->c_id_len = 0; 497b3441518Sflorian iface_conf->h_name = NULL; 498a41cc082Sflorian SIMPLEQ_INSERT_TAIL(&nconf->iface_list, 499a41cc082Sflorian iface_conf, entry); 500a41cc082Sflorian break; 501a41cc082Sflorian case IMSG_RECONF_VC_ID: 502a41cc082Sflorian if (iface_conf == NULL) 503f46577a8Sflorian fatalx("%s: %s without IMSG_RECONF_IFACE", 504f46577a8Sflorian __func__, i2s(type)); 505f46577a8Sflorian if (iface_conf->vc_id != NULL) 506f46577a8Sflorian fatalx("%s: multiple %s for the same interface", 507f46577a8Sflorian __func__, i2s(type)); 50845c5e5adSflorian if ((iface_conf->vc_id_len = imsg_get_len(&imsg)) 50945c5e5adSflorian > 255 + 2 || iface_conf->vc_id_len == 0) 51045c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 51145c5e5adSflorian if ((iface_conf->vc_id = malloc(iface_conf->vc_id_len)) 512a41cc082Sflorian == NULL) 513a41cc082Sflorian fatal(NULL); 51445c5e5adSflorian if (imsg_get_data(&imsg, iface_conf->vc_id, 51545c5e5adSflorian iface_conf->vc_id_len) == -1) 51645c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 517a41cc082Sflorian break; 518a41cc082Sflorian case IMSG_RECONF_C_ID: 519a41cc082Sflorian if (iface_conf == NULL) 520f46577a8Sflorian fatalx("%s: %s without IMSG_RECONF_IFACE", 521f46577a8Sflorian __func__, i2s(type)); 522f46577a8Sflorian if (iface_conf->c_id != NULL) 523f46577a8Sflorian fatalx("%s: multiple %s for the same interface", 524f46577a8Sflorian __func__, i2s(type)); 52545c5e5adSflorian if ((iface_conf->c_id_len = imsg_get_len(&imsg)) 52645c5e5adSflorian > 255 + 2 || iface_conf->c_id_len == 0) 52745c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 52845c5e5adSflorian if ((iface_conf->c_id = malloc(iface_conf->c_id_len)) 529a41cc082Sflorian == NULL) 530a41cc082Sflorian fatal(NULL); 53145c5e5adSflorian if (imsg_get_data(&imsg, iface_conf->c_id, 53245c5e5adSflorian iface_conf->c_id_len) == -1) 53345c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 534a41cc082Sflorian break; 53545c5e5adSflorian case IMSG_RECONF_H_NAME: { 53645c5e5adSflorian size_t len; 53745c5e5adSflorian 538b3441518Sflorian if (iface_conf == NULL) 539f46577a8Sflorian fatalx("%s: %s without IMSG_RECONF_IFACE", 540f46577a8Sflorian __func__, i2s(type)); 541f46577a8Sflorian if (iface_conf->h_name != NULL) 542f46577a8Sflorian fatalx("%s: multiple %s for the same interface", 543f46577a8Sflorian __func__, i2s(type)); 54445c5e5adSflorian if ((len = imsg_get_len(&imsg)) > 256 || len == 0) 54545c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 54645c5e5adSflorian if ((iface_conf->h_name = malloc(len)) == NULL) 547b3441518Sflorian fatal(NULL); 54845c5e5adSflorian if (imsg_get_data(&imsg, iface_conf->h_name, len) == -1) 54945c5e5adSflorian fatalx("%s: invalid %s", __func__, i2s(type)); 55045c5e5adSflorian if (iface_conf->h_name[len - 1] != '\0') 55145c5e5adSflorian fatalx("Invalid hostname"); 552b3441518Sflorian break; 55345c5e5adSflorian } 554c2bc6c6dSflorian case IMSG_RECONF_END: { 555c2bc6c6dSflorian struct dhcpleased_iface *iface; 556c2bc6c6dSflorian int *ifaces; 557c2bc6c6dSflorian int i, if_index; 558c2bc6c6dSflorian char *if_name; 559c2bc6c6dSflorian char ifnamebuf[IF_NAMESIZE]; 560c2bc6c6dSflorian 561a41cc082Sflorian if (nconf == NULL) 5627e5648d1Sflorian fatalx("%s: %s without IMSG_RECONF_CONF", 5637e5648d1Sflorian __func__, i2s(type)); 5647e5648d1Sflorian 565c2bc6c6dSflorian ifaces = changed_ifaces(engine_conf, nconf); 566a41cc082Sflorian merge_config(engine_conf, nconf); 567a41cc082Sflorian nconf = NULL; 568c2bc6c6dSflorian for (i = 0; ifaces[i] != 0; i++) { 569c2bc6c6dSflorian if_index = ifaces[i]; 570c2bc6c6dSflorian if_name = if_indextoname(if_index, ifnamebuf); 571c2bc6c6dSflorian iface = get_dhcpleased_iface_by_id(if_index); 572c2bc6c6dSflorian if (if_name == NULL || iface == NULL) 573c2bc6c6dSflorian continue; 574c2bc6c6dSflorian iface_conf = find_iface_conf( 575c2bc6c6dSflorian &engine_conf->iface_list, if_name); 576c2bc6c6dSflorian if (iface_conf == NULL) 577c2bc6c6dSflorian continue; 578c2bc6c6dSflorian if (iface_conf->ignore & IGN_DNS) 579c2bc6c6dSflorian send_rdns_withdraw(iface); 580c2bc6c6dSflorian if (iface_conf->ignore & IGN_ROUTES) 581c2bc6c6dSflorian send_routes_withdraw(iface); 582c2bc6c6dSflorian } 583c2bc6c6dSflorian free(ifaces); 584a41cc082Sflorian break; 585c2bc6c6dSflorian } 586a41cc082Sflorian #endif /* SMALL */ 58757419a7fSflorian default: 58845c5e5adSflorian log_debug("%s: unexpected imsg %d", __func__, type); 58957419a7fSflorian break; 59057419a7fSflorian } 59157419a7fSflorian imsg_free(&imsg); 59257419a7fSflorian } 59357419a7fSflorian if (!shut) 59457419a7fSflorian imsg_event_add(iev); 59557419a7fSflorian else { 59657419a7fSflorian /* This pipe is dead. Remove its event handler. */ 59757419a7fSflorian event_del(&iev->ev); 59857419a7fSflorian event_loopexit(NULL); 59957419a7fSflorian } 60057419a7fSflorian } 60157419a7fSflorian 60257419a7fSflorian #ifndef SMALL 60357419a7fSflorian void 60457419a7fSflorian send_interface_info(struct dhcpleased_iface *iface, pid_t pid) 60557419a7fSflorian { 60657419a7fSflorian struct ctl_engine_info cei; 60757419a7fSflorian 60857419a7fSflorian memset(&cei, 0, sizeof(cei)); 60957419a7fSflorian cei.if_index = iface->if_index; 61057419a7fSflorian cei.running = iface->running; 61157419a7fSflorian cei.link_state = iface->link_state; 61257419a7fSflorian strlcpy(cei.state, if_state_name[iface->state], sizeof(cei.state)); 61357419a7fSflorian memcpy(&cei.request_time, &iface->request_time, 61457419a7fSflorian sizeof(cei.request_time)); 615b2b40540Sflorian cei.server_identifier = iface->server_identifier; 616b2b40540Sflorian cei.dhcp_server = iface->dhcp_server; 617b2b40540Sflorian cei.requested_ip = iface->requested_ip; 618b2b40540Sflorian cei.mask = iface->mask; 619351dd593Sflorian cei.routes_len = iface->routes_len; 620351dd593Sflorian memcpy(cei.routes, iface->routes, sizeof(cei.routes)); 62157419a7fSflorian memcpy(cei.nameservers, iface->nameservers, sizeof(cei.nameservers)); 62257419a7fSflorian cei.lease_time = iface->lease_time; 62357419a7fSflorian cei.renewal_time = iface->renewal_time; 62457419a7fSflorian cei.rebinding_time = iface->rebinding_time; 62557419a7fSflorian engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei, 62657419a7fSflorian sizeof(cei)); 62757419a7fSflorian } 62857419a7fSflorian 62957419a7fSflorian void 63045c5e5adSflorian engine_showinfo_ctl(pid_t pid, uint32_t if_index) 63157419a7fSflorian { 63257419a7fSflorian struct dhcpleased_iface *iface; 63357419a7fSflorian 634a86df30dSflorian if ((iface = get_dhcpleased_iface_by_id(if_index)) != NULL) 63545c5e5adSflorian send_interface_info(iface, pid); 636a86df30dSflorian else 63745c5e5adSflorian engine_imsg_compose_frontend(IMSG_CTL_END, pid, NULL, 0); 63857419a7fSflorian } 63957419a7fSflorian #endif /* SMALL */ 640e998cdbeSflorian 64157419a7fSflorian void 6426e93e3e9Sflorian engine_update_iface(struct imsg_ifinfo *imsg_ifinfo) 64357419a7fSflorian { 64457419a7fSflorian struct dhcpleased_iface *iface; 64557419a7fSflorian int need_refresh = 0; 64657419a7fSflorian 64757419a7fSflorian iface = get_dhcpleased_iface_by_id(imsg_ifinfo->if_index); 64857419a7fSflorian 64957419a7fSflorian if (iface == NULL) { 65057419a7fSflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 65157419a7fSflorian fatal("calloc"); 65257419a7fSflorian iface->state = IF_DOWN; 6539e257558Sflorian iface->xid = arc4random(); 65457419a7fSflorian iface->timo.tv_usec = arc4random_uniform(1000000); 65557419a7fSflorian evtimer_set(&iface->timer, iface_timeout, iface); 65657419a7fSflorian iface->if_index = imsg_ifinfo->if_index; 65757419a7fSflorian iface->rdomain = imsg_ifinfo->rdomain; 65857419a7fSflorian iface->running = imsg_ifinfo->running; 65957419a7fSflorian iface->link_state = imsg_ifinfo->link_state; 66057419a7fSflorian iface->requested_ip.s_addr = INADDR_ANY; 66157419a7fSflorian memcpy(&iface->hw_address, &imsg_ifinfo->hw_address, 66257419a7fSflorian sizeof(struct ether_addr)); 66357419a7fSflorian LIST_INSERT_HEAD(&dhcpleased_interfaces, iface, entries); 66457419a7fSflorian need_refresh = 1; 66557419a7fSflorian } else { 66657419a7fSflorian if (memcmp(&iface->hw_address, &imsg_ifinfo->hw_address, 66757419a7fSflorian sizeof(struct ether_addr)) != 0) { 66857419a7fSflorian memcpy(&iface->hw_address, &imsg_ifinfo->hw_address, 66957419a7fSflorian sizeof(struct ether_addr)); 67057419a7fSflorian need_refresh = 1; 67157419a7fSflorian } 67257419a7fSflorian if (imsg_ifinfo->rdomain != iface->rdomain) { 67357419a7fSflorian iface->rdomain = imsg_ifinfo->rdomain; 67457419a7fSflorian need_refresh = 1; 67557419a7fSflorian } 67657419a7fSflorian if (imsg_ifinfo->running != iface->running) { 67757419a7fSflorian iface->running = imsg_ifinfo->running; 67857419a7fSflorian need_refresh = 1; 67957419a7fSflorian } 68057419a7fSflorian 68157419a7fSflorian if (imsg_ifinfo->link_state != iface->link_state) { 68257419a7fSflorian iface->link_state = imsg_ifinfo->link_state; 68357419a7fSflorian need_refresh = 1; 68457419a7fSflorian } 68557419a7fSflorian } 68657419a7fSflorian 68757419a7fSflorian if (!need_refresh) 68857419a7fSflorian return; 68957419a7fSflorian 69057419a7fSflorian if (iface->running && LINK_STATE_IS_UP(iface->link_state)) { 69157419a7fSflorian if (iface->requested_ip.s_addr == INADDR_ANY) 692b7f83d9aSflorian parse_lease(iface, imsg_ifinfo); 693b7f83d9aSflorian 694b7f83d9aSflorian if (iface->requested_ip.s_addr == INADDR_ANY) 69557419a7fSflorian state_transition(iface, IF_INIT); 69657419a7fSflorian else 69757419a7fSflorian state_transition(iface, IF_REBOOTING); 69857419a7fSflorian } else 69957419a7fSflorian state_transition(iface, IF_DOWN); 70057419a7fSflorian } 70157419a7fSflorian struct dhcpleased_iface* 70257419a7fSflorian get_dhcpleased_iface_by_id(uint32_t if_index) 70357419a7fSflorian { 70457419a7fSflorian struct dhcpleased_iface *iface; 70557419a7fSflorian LIST_FOREACH (iface, &dhcpleased_interfaces, entries) { 70657419a7fSflorian if (iface->if_index == if_index) 70757419a7fSflorian return (iface); 70857419a7fSflorian } 70957419a7fSflorian 71057419a7fSflorian return (NULL); 71157419a7fSflorian } 71257419a7fSflorian 71357419a7fSflorian void 71457419a7fSflorian remove_dhcpleased_iface(uint32_t if_index) 71557419a7fSflorian { 71657419a7fSflorian struct dhcpleased_iface *iface; 71757419a7fSflorian 71857419a7fSflorian iface = get_dhcpleased_iface_by_id(if_index); 71957419a7fSflorian 72057419a7fSflorian if (iface == NULL) 72157419a7fSflorian return; 72257419a7fSflorian 72357419a7fSflorian send_rdns_withdraw(iface); 724b7f83d9aSflorian send_deconfigure_interface(iface); 72557419a7fSflorian LIST_REMOVE(iface, entries); 72657419a7fSflorian evtimer_del(&iface->timer); 72757419a7fSflorian free(iface); 72857419a7fSflorian } 72957419a7fSflorian 73057419a7fSflorian void 73157419a7fSflorian parse_dhcp(struct dhcpleased_iface *iface, struct imsg_dhcp *dhcp) 73257419a7fSflorian { 73357419a7fSflorian static uint8_t cookie[] = DHCP_COOKIE; 73457419a7fSflorian static struct ether_addr bcast_mac; 735a41cc082Sflorian #ifndef SMALL 736a41cc082Sflorian struct iface_conf *iface_conf; 737a41cc082Sflorian #endif /* SMALL */ 73857419a7fSflorian struct ether_header *eh; 73957419a7fSflorian struct ether_addr ether_src, ether_dst; 74057419a7fSflorian struct ip *ip; 74157419a7fSflorian struct udphdr *udp; 74257419a7fSflorian struct dhcp_hdr *dhcp_hdr; 743351dd593Sflorian struct in_addr server_identifier, subnet_mask; 74457419a7fSflorian struct in_addr nameservers[MAX_RDNS_COUNT]; 745351dd593Sflorian struct dhcp_route routes[MAX_DHCP_ROUTES]; 74657419a7fSflorian size_t rem, i; 74757419a7fSflorian uint32_t sum, usum, lease_time = 0, renewal_time = 0; 74857419a7fSflorian uint32_t rebinding_time = 0; 7496d7478f0Sflorian uint32_t ipv6_only_time = 0; 750945a7accSflorian uint8_t *p, dho = DHO_PAD, dho_len, slen; 75157419a7fSflorian uint8_t dhcp_message_type = 0; 752bef7dfb5Sflorian int routes_len = 0, routers = 0, csr = 0; 75357419a7fSflorian char from[sizeof("xx:xx:xx:xx:xx:xx")]; 75457419a7fSflorian char to[sizeof("xx:xx:xx:xx:xx:xx")]; 75557419a7fSflorian char hbuf_src[INET_ADDRSTRLEN]; 75657419a7fSflorian char hbuf_dst[INET_ADDRSTRLEN]; 75757419a7fSflorian char hbuf[INET_ADDRSTRLEN]; 758ebace80cSflorian char domainname[4 * 255 + 1]; 759ebace80cSflorian char hostname[4 * 255 + 1]; 760e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 76157419a7fSflorian 76257419a7fSflorian if (bcast_mac.ether_addr_octet[0] == 0) 76357419a7fSflorian memset(bcast_mac.ether_addr_octet, 0xff, ETHER_ADDR_LEN); 76457419a7fSflorian 765a41cc082Sflorian if_name = if_indextoname(iface->if_index, ifnamebuf); 766a41cc082Sflorian 767a41cc082Sflorian #ifndef SMALL 768a41cc082Sflorian iface_conf = find_iface_conf(&engine_conf->iface_list, if_name); 769a41cc082Sflorian #endif /* SMALL*/ 770a41cc082Sflorian 77157419a7fSflorian memset(hbuf_src, 0, sizeof(hbuf_src)); 77257419a7fSflorian memset(hbuf_dst, 0, sizeof(hbuf_dst)); 77357419a7fSflorian 77457419a7fSflorian p = dhcp->packet; 77557419a7fSflorian rem = dhcp->len; 77657419a7fSflorian 77757419a7fSflorian if (rem < sizeof(*eh)) { 77857419a7fSflorian log_warnx("%s: message too short", __func__); 77957419a7fSflorian return; 78057419a7fSflorian } 78157419a7fSflorian eh = (struct ether_header *)p; 78257419a7fSflorian memcpy(ether_src.ether_addr_octet, eh->ether_shost, 78357419a7fSflorian sizeof(ether_src.ether_addr_octet)); 78457419a7fSflorian strlcpy(from, ether_ntoa(ðer_src), sizeof(from)); 78557419a7fSflorian memcpy(ether_dst.ether_addr_octet, eh->ether_dhost, 78657419a7fSflorian sizeof(ether_dst.ether_addr_octet)); 78757419a7fSflorian strlcpy(to, ether_ntoa(ðer_dst), sizeof(to)); 78857419a7fSflorian p += sizeof(*eh); 78957419a7fSflorian rem -= sizeof(*eh); 79057419a7fSflorian 79157419a7fSflorian if (memcmp(ðer_dst, &iface->hw_address, sizeof(ether_dst)) != 0 && 79257419a7fSflorian memcmp(ðer_dst, &bcast_mac, sizeof(ether_dst)) != 0) 79357419a7fSflorian return ; /* silently ignore packet not for us */ 79457419a7fSflorian 79557419a7fSflorian if (rem < sizeof(*ip)) 79657419a7fSflorian goto too_short; 79757419a7fSflorian 798e998cdbeSflorian if (log_getverbose() > 1) 79957419a7fSflorian log_debug("%s, from: %s, to: %s", __func__, from, to); 80057419a7fSflorian 80157419a7fSflorian ip = (struct ip *)p; 80257419a7fSflorian 80357419a7fSflorian if (rem < (size_t)ip->ip_hl << 2) 80457419a7fSflorian goto too_short; 80557419a7fSflorian 8062b86dc95Sjan if ((dhcp->csumflags & M_IPV4_CSUM_IN_OK) == 0 && 8072b86dc95Sjan wrapsum(checksum((uint8_t *)ip, ip->ip_hl << 2, 0)) != 0) { 80857419a7fSflorian log_warnx("%s: bad IP checksum", __func__); 80957419a7fSflorian return; 81057419a7fSflorian } 81157419a7fSflorian if (rem < ntohs(ip->ip_len)) 81257419a7fSflorian goto too_short; 81357419a7fSflorian 81457419a7fSflorian p += ip->ip_hl << 2; 81557419a7fSflorian rem -= ip->ip_hl << 2; 81657419a7fSflorian 81757419a7fSflorian if (inet_ntop(AF_INET, &ip->ip_src, hbuf_src, sizeof(hbuf_src)) == NULL) 81857419a7fSflorian hbuf_src[0] = '\0'; 81957419a7fSflorian if (inet_ntop(AF_INET, &ip->ip_dst, hbuf_dst, sizeof(hbuf_dst)) == NULL) 820e998cdbeSflorian hbuf_dst[0] = '\0'; 82157419a7fSflorian 822c2bc6c6dSflorian #ifndef SMALL 823c2bc6c6dSflorian if (iface_conf != NULL) { 824c2bc6c6dSflorian for (i = 0; (int)i < iface_conf->ignore_servers_len; i++) { 825c2bc6c6dSflorian if (iface_conf->ignore_servers[i].s_addr == 826c2bc6c6dSflorian ip->ip_src.s_addr) { 827c2bc6c6dSflorian log_debug("ignoring server %s", hbuf_src); 828c2bc6c6dSflorian return; 829c2bc6c6dSflorian } 830c2bc6c6dSflorian } 831c2bc6c6dSflorian } 832c2bc6c6dSflorian #endif /* SMALL */ 833c2bc6c6dSflorian 83457419a7fSflorian if (rem < sizeof(*udp)) 83557419a7fSflorian goto too_short; 83657419a7fSflorian 83757419a7fSflorian udp = (struct udphdr *)p; 83857419a7fSflorian if (rem < ntohs(udp->uh_ulen)) 83957419a7fSflorian goto too_short; 84057419a7fSflorian 84157419a7fSflorian if (rem > ntohs(udp->uh_ulen)) { 842e998cdbeSflorian if (log_getverbose() > 1) { 843e998cdbeSflorian log_debug("%s: accepting packet with %lu bytes of data" 844e998cdbeSflorian " after udp payload", __func__, rem - 845e998cdbeSflorian ntohs(udp->uh_ulen)); 846e998cdbeSflorian } 84757419a7fSflorian rem = ntohs(udp->uh_ulen); 84857419a7fSflorian } 84957419a7fSflorian 85057419a7fSflorian p += sizeof(*udp); 85157419a7fSflorian rem -= sizeof(*udp); 85257419a7fSflorian 8532b86dc95Sjan if ((dhcp->csumflags & M_UDP_CSUM_IN_OK) == 0) { 85457419a7fSflorian usum = udp->uh_sum; 85557419a7fSflorian udp->uh_sum = 0; 85657419a7fSflorian 8572b86dc95Sjan sum = wrapsum(checksum((uint8_t *)udp, sizeof(*udp), 8582b86dc95Sjan checksum(p, rem, 85957419a7fSflorian checksum((uint8_t *)&ip->ip_src, 2 * sizeof(ip->ip_src), 86057419a7fSflorian IPPROTO_UDP + ntohs(udp->uh_ulen))))); 86157419a7fSflorian 86257419a7fSflorian if (usum != 0 && usum != sum) { 86357419a7fSflorian log_warnx("%s: bad UDP checksum", __func__); 86457419a7fSflorian return; 86557419a7fSflorian } 8662b86dc95Sjan } 86757419a7fSflorian 868e998cdbeSflorian if (log_getverbose() > 1) { 869e998cdbeSflorian log_debug("%s: %s:%d -> %s:%d", __func__, hbuf_src, 870e998cdbeSflorian ntohs(udp->uh_sport), hbuf_dst, ntohs(udp->uh_dport)); 871e998cdbeSflorian } 87257419a7fSflorian 87357419a7fSflorian if (rem < sizeof(*dhcp_hdr)) 87457419a7fSflorian goto too_short; 87557419a7fSflorian 87657419a7fSflorian dhcp_hdr = (struct dhcp_hdr *)p; 87757419a7fSflorian p += sizeof(*dhcp_hdr); 87857419a7fSflorian rem -= sizeof(*dhcp_hdr); 87957419a7fSflorian 88057419a7fSflorian dhcp_hdr->sname[DHCP_SNAME_LEN -1 ] = '\0'; /* ensure it's a string */ 88157419a7fSflorian dhcp_hdr->file[DHCP_FILE_LEN -1 ] = '\0'; /* ensure it's a string */ 882e998cdbeSflorian 883e998cdbeSflorian if (log_getverbose() > 1) 884e998cdbeSflorian log_dhcp_hdr(dhcp_hdr); 88557419a7fSflorian 88657419a7fSflorian if (dhcp_hdr->op != DHCP_BOOTREPLY) { 8876d06d422Stb log_warnx("%s: ignoring non-reply packet", __func__); 88857419a7fSflorian return; 88957419a7fSflorian } 89057419a7fSflorian 891c2da98a6Sflorian if (ntohl(dhcp_hdr->xid) != iface->xid) 89257419a7fSflorian return; /* silently ignore wrong xid */ 89357419a7fSflorian 89457419a7fSflorian if (rem < sizeof(cookie)) 89557419a7fSflorian goto too_short; 89657419a7fSflorian 89757419a7fSflorian if (memcmp(p, cookie, sizeof(cookie)) != 0) { 89857419a7fSflorian log_warnx("%s: no dhcp cookie in packet from %s", __func__, 89957419a7fSflorian from); 90057419a7fSflorian return; 90157419a7fSflorian } 90257419a7fSflorian p += sizeof(cookie); 90357419a7fSflorian rem -= sizeof(cookie); 90457419a7fSflorian 90557419a7fSflorian memset(&server_identifier, 0, sizeof(server_identifier)); 90657419a7fSflorian memset(&subnet_mask, 0, sizeof(subnet_mask)); 907351dd593Sflorian memset(&routes, 0, sizeof(routes)); 90857419a7fSflorian memset(&nameservers, 0, sizeof(nameservers)); 909ef603d85Sflorian memset(hostname, 0, sizeof(hostname)); 910ef603d85Sflorian memset(domainname, 0, sizeof(domainname)); 91157419a7fSflorian 91257419a7fSflorian while (rem > 0 && dho != DHO_END) { 91357419a7fSflorian dho = *p; 91457419a7fSflorian p += 1; 91557419a7fSflorian rem -= 1; 91657419a7fSflorian /* only DHO_END and DHO_PAD are 1 byte long without length */ 91757419a7fSflorian if (dho == DHO_PAD || dho == DHO_END) 91857419a7fSflorian dho_len = 0; 91957419a7fSflorian else { 92057419a7fSflorian if (rem == 0) 92157419a7fSflorian goto too_short; /* missing option length */ 92257419a7fSflorian dho_len = *p; 92357419a7fSflorian p += 1; 92457419a7fSflorian rem -= 1; 92557419a7fSflorian if (rem < dho_len) 92657419a7fSflorian goto too_short; 92757419a7fSflorian } 92857419a7fSflorian 92957419a7fSflorian switch (dho) { 93057419a7fSflorian case DHO_PAD: 931e998cdbeSflorian if (log_getverbose() > 1) 93257419a7fSflorian log_debug("DHO_PAD"); 93357419a7fSflorian break; 93457419a7fSflorian case DHO_END: 935e998cdbeSflorian if (log_getverbose() > 1) 93657419a7fSflorian log_debug("DHO_END"); 93757419a7fSflorian break; 93857419a7fSflorian case DHO_DHCP_MESSAGE_TYPE: 93957419a7fSflorian if (dho_len != 1) 94057419a7fSflorian goto wrong_length; 94157419a7fSflorian dhcp_message_type = *p; 942e998cdbeSflorian if (log_getverbose() > 1) { 943e998cdbeSflorian log_debug("DHO_DHCP_MESSAGE_TYPE: %s", 944e998cdbeSflorian dhcp_message_type2str(dhcp_message_type)); 94557419a7fSflorian } 94657419a7fSflorian p += dho_len; 94757419a7fSflorian rem -= dho_len; 94857419a7fSflorian break; 94957419a7fSflorian case DHO_DHCP_SERVER_IDENTIFIER: 95057419a7fSflorian if (dho_len != sizeof(server_identifier)) 95157419a7fSflorian goto wrong_length; 95257419a7fSflorian memcpy(&server_identifier, p, 95357419a7fSflorian sizeof(server_identifier)); 954e998cdbeSflorian if (log_getverbose() > 1) { 95557419a7fSflorian log_debug("DHO_DHCP_SERVER_IDENTIFIER: %s", 95657419a7fSflorian inet_ntop(AF_INET, &server_identifier, 95757419a7fSflorian hbuf, sizeof(hbuf))); 958e998cdbeSflorian } 95957419a7fSflorian p += dho_len; 96057419a7fSflorian rem -= dho_len; 96157419a7fSflorian break; 96257419a7fSflorian case DHO_DHCP_LEASE_TIME: 96357419a7fSflorian if (dho_len != sizeof(lease_time)) 96457419a7fSflorian goto wrong_length; 96557419a7fSflorian memcpy(&lease_time, p, sizeof(lease_time)); 96657419a7fSflorian lease_time = ntohl(lease_time); 967e998cdbeSflorian if (log_getverbose() > 1) { 968e998cdbeSflorian log_debug("DHO_DHCP_LEASE_TIME %us", 969e998cdbeSflorian lease_time); 970e998cdbeSflorian } 97157419a7fSflorian p += dho_len; 97257419a7fSflorian rem -= dho_len; 97357419a7fSflorian break; 97457419a7fSflorian case DHO_SUBNET_MASK: 97557419a7fSflorian if (dho_len != sizeof(subnet_mask)) 97657419a7fSflorian goto wrong_length; 97757419a7fSflorian memcpy(&subnet_mask, p, sizeof(subnet_mask)); 978e998cdbeSflorian if (log_getverbose() > 1) { 979e998cdbeSflorian log_debug("DHO_SUBNET_MASK: %s", 980e998cdbeSflorian inet_ntop(AF_INET, &subnet_mask, hbuf, 981e998cdbeSflorian sizeof(hbuf))); 982e998cdbeSflorian } 98357419a7fSflorian p += dho_len; 98457419a7fSflorian rem -= dho_len; 98557419a7fSflorian break; 98657419a7fSflorian case DHO_ROUTERS: 987351dd593Sflorian if (dho_len < sizeof(routes[routes_len].gw)) 98857419a7fSflorian goto wrong_length; 989351dd593Sflorian if (dho_len % sizeof(routes[routes_len].gw) != 0) 99057419a7fSflorian goto wrong_length; 991351dd593Sflorian 992bef7dfb5Sflorian /* 993bef7dfb5Sflorian * Ignore routers option if classless static routes 994bef7dfb5Sflorian * are present (RFC3442). 995bef7dfb5Sflorian */ 996bef7dfb5Sflorian if (!csr) { 997bef7dfb5Sflorian routers = 1; 998bef7dfb5Sflorian while (routes_len < MAX_DHCP_ROUTES && 999bef7dfb5Sflorian dho_len > 0) { 1000351dd593Sflorian memcpy(&routes[routes_len].gw, p, 1001351dd593Sflorian sizeof(routes[routes_len].gw)); 1002e998cdbeSflorian if (log_getverbose() > 1) { 1003351dd593Sflorian log_debug("DHO_ROUTER: %s", 1004351dd593Sflorian inet_ntop(AF_INET, 1005bef7dfb5Sflorian &routes[routes_len].gw, 1006bef7dfb5Sflorian hbuf, sizeof(hbuf))); 1007e998cdbeSflorian } 1008351dd593Sflorian p += sizeof(routes[routes_len].gw); 1009351dd593Sflorian rem -= sizeof(routes[routes_len].gw); 1010bef7dfb5Sflorian dho_len -= 1011bef7dfb5Sflorian sizeof(routes[routes_len].gw); 1012351dd593Sflorian routes_len++; 1013351dd593Sflorian } 1014bef7dfb5Sflorian } 1015351dd593Sflorian if (dho_len != 0) { 1016351dd593Sflorian /* ignore > MAX_DHCP_ROUTES routes */ 101757419a7fSflorian p += dho_len; 101857419a7fSflorian rem -= dho_len; 1019351dd593Sflorian } 102057419a7fSflorian break; 102157419a7fSflorian case DHO_DOMAIN_NAME_SERVERS: 102257419a7fSflorian if (dho_len < sizeof(nameservers[0])) 102357419a7fSflorian goto wrong_length; 102457419a7fSflorian if (dho_len % sizeof(nameservers[0]) != 0) 102557419a7fSflorian goto wrong_length; 102657419a7fSflorian /* we limit ourself to 8 nameservers for proposals */ 102757419a7fSflorian memcpy(&nameservers, p, MINIMUM(sizeof(nameservers), 102857419a7fSflorian dho_len)); 1029e998cdbeSflorian if (log_getverbose() > 1) { 1030e998cdbeSflorian for (i = 0; i < MINIMUM(sizeof(nameservers), 1031e998cdbeSflorian dho_len / sizeof(nameservers[0])); i++) { 103257419a7fSflorian log_debug("DHO_DOMAIN_NAME_SERVERS: %s " 103357419a7fSflorian "(%lu/%lu)", inet_ntop(AF_INET, 1034e998cdbeSflorian &nameservers[i], hbuf, 1035e998cdbeSflorian sizeof(hbuf)), i + 1, 103657419a7fSflorian dho_len / sizeof(nameservers[0])); 103757419a7fSflorian } 1038e998cdbeSflorian } 103957419a7fSflorian p += dho_len; 104057419a7fSflorian rem -= dho_len; 104157419a7fSflorian break; 1042ebace80cSflorian case DHO_HOST_NAME: 1043ef603d85Sflorian if (dho_len < 1) { 1044ef603d85Sflorian /* 1045ef603d85Sflorian * Protocol violation: minimum length is 1; 1046ef603d85Sflorian * pretend the option is not there 1047ef603d85Sflorian */ 1048ef603d85Sflorian break; 1049ef603d85Sflorian } 1050945a7accSflorian /* MUST delete trailing NUL, per RFC 2132 */ 1051945a7accSflorian slen = dho_len; 1052945a7accSflorian while (slen > 0 && p[slen - 1] == '\0') 1053945a7accSflorian slen--; 1054ef603d85Sflorian /* slen might be 0 here, pretend option is not there. */ 1055945a7accSflorian strvisx(hostname, p, slen, VIS_SAFE); 1056ebace80cSflorian if (log_getverbose() > 1) 1057ebace80cSflorian log_debug("DHO_HOST_NAME: %s", hostname); 1058ebace80cSflorian p += dho_len; 1059ebace80cSflorian rem -= dho_len; 1060ebace80cSflorian break; 106157419a7fSflorian case DHO_DOMAIN_NAME: 1062ef603d85Sflorian if (dho_len < 1) { 1063ef603d85Sflorian /* 1064ef603d85Sflorian * Protocol violation: minimum length is 1; 1065ef603d85Sflorian * pretend the option is not there 1066ef603d85Sflorian */ 1067ef603d85Sflorian break; 1068ef603d85Sflorian } 1069945a7accSflorian /* MUST delete trailing NUL, per RFC 2132 */ 1070945a7accSflorian slen = dho_len; 1071945a7accSflorian while (slen > 0 && p[slen - 1] == '\0') 1072945a7accSflorian slen--; 1073ef603d85Sflorian /* slen might be 0 here, pretend option is not there. */ 1074945a7accSflorian strvisx(domainname, p, slen, VIS_SAFE); 1075ebace80cSflorian if (log_getverbose() > 1) 1076ebace80cSflorian log_debug("DHO_DOMAIN_NAME: %s", domainname); 107757419a7fSflorian p += dho_len; 107857419a7fSflorian rem -= dho_len; 107957419a7fSflorian break; 108057419a7fSflorian case DHO_DHCP_RENEWAL_TIME: 108157419a7fSflorian if (dho_len != sizeof(renewal_time)) 108257419a7fSflorian goto wrong_length; 108357419a7fSflorian memcpy(&renewal_time, p, sizeof(renewal_time)); 108457419a7fSflorian renewal_time = ntohl(renewal_time); 1085e998cdbeSflorian if (log_getverbose() > 1) { 1086e998cdbeSflorian log_debug("DHO_DHCP_RENEWAL_TIME %us", 1087e998cdbeSflorian renewal_time); 1088e998cdbeSflorian } 108957419a7fSflorian p += dho_len; 109057419a7fSflorian rem -= dho_len; 109157419a7fSflorian break; 109257419a7fSflorian case DHO_DHCP_REBINDING_TIME: 109357419a7fSflorian if (dho_len != sizeof(rebinding_time)) 109457419a7fSflorian goto wrong_length; 109557419a7fSflorian memcpy(&rebinding_time, p, sizeof(rebinding_time)); 109657419a7fSflorian rebinding_time = ntohl(rebinding_time); 1097e998cdbeSflorian if (log_getverbose() > 1) { 109857419a7fSflorian log_debug("DHO_DHCP_REBINDING_TIME %us", 109957419a7fSflorian rebinding_time); 1100e998cdbeSflorian } 110157419a7fSflorian p += dho_len; 110257419a7fSflorian rem -= dho_len; 110357419a7fSflorian break; 110457419a7fSflorian case DHO_DHCP_CLIENT_IDENTIFIER: 110557419a7fSflorian /* the server is supposed to echo this back to us */ 1106a41cc082Sflorian #ifndef SMALL 1107a41cc082Sflorian if (iface_conf != NULL && iface_conf->c_id_len > 0) { 1108a41cc082Sflorian if (dho_len != iface_conf->c_id[1]) { 1109a41cc082Sflorian log_warnx("wrong " 1110a41cc082Sflorian "DHO_DHCP_CLIENT_IDENTIFIER"); 1111a41cc082Sflorian return; 1112a41cc082Sflorian } 1113a41cc082Sflorian if (memcmp(p, &iface_conf->c_id[2], dho_len) != 1114a41cc082Sflorian 0) { 1115a41cc082Sflorian log_warnx("wrong " 1116a41cc082Sflorian "DHO_DHCP_CLIENT_IDENTIFIER"); 1117a41cc082Sflorian return; 1118a41cc082Sflorian } 1119a41cc082Sflorian } else 1120a41cc082Sflorian #endif /* SMALL */ 1121a41cc082Sflorian { 112257419a7fSflorian if (dho_len != 1 + sizeof(iface->hw_address)) 112357419a7fSflorian goto wrong_length; 112457419a7fSflorian if (*p != HTYPE_ETHER) { 1125a41cc082Sflorian log_warnx("DHO_DHCP_CLIENT_IDENTIFIER: " 1126a41cc082Sflorian "wrong type"); 112757419a7fSflorian return; 112857419a7fSflorian } 112957419a7fSflorian if (memcmp(p + 1, &iface->hw_address, 113057419a7fSflorian sizeof(iface->hw_address)) != 0) { 1131a41cc082Sflorian log_warnx("wrong " 1132a41cc082Sflorian "DHO_DHCP_CLIENT_IDENTIFIER"); 113357419a7fSflorian return; 113457419a7fSflorian } 1135a41cc082Sflorian } 113657419a7fSflorian p += dho_len; 113757419a7fSflorian rem -= dho_len; 113857419a7fSflorian break; 1139351dd593Sflorian case DHO_CLASSLESS_STATIC_ROUTES: { 1140351dd593Sflorian int prefixlen, compressed_prefixlen; 1141351dd593Sflorian 1142bef7dfb5Sflorian csr = 1; 1143bef7dfb5Sflorian if (routers) { 1144bef7dfb5Sflorian /* 1145bef7dfb5Sflorian * Ignore routers option if classless static 1146bef7dfb5Sflorian * routes are present (RFC3442). 1147bef7dfb5Sflorian */ 1148bef7dfb5Sflorian routers = 0; 1149bef7dfb5Sflorian routes_len = 0; 1150bef7dfb5Sflorian } 1151351dd593Sflorian while (routes_len < MAX_DHCP_ROUTES && dho_len > 0) { 1152351dd593Sflorian prefixlen = *p; 1153351dd593Sflorian p += 1; 1154351dd593Sflorian rem -= 1; 1155351dd593Sflorian dho_len -= 1; 1156351dd593Sflorian 1157351dd593Sflorian if (prefixlen < 0 || prefixlen > 32) { 1158351dd593Sflorian log_warnx("%s: invalid prefixlen: %d", 1159351dd593Sflorian __func__, prefixlen); 1160351dd593Sflorian return; 1161351dd593Sflorian } 1162351dd593Sflorian 1163351dd593Sflorian if (prefixlen > 0) 1164351dd593Sflorian routes[routes_len].mask.s_addr = 1165351dd593Sflorian htonl(0xffffffff << (32 - 1166351dd593Sflorian prefixlen)); 1167351dd593Sflorian else 1168351dd593Sflorian routes[routes_len].mask.s_addr = 1169351dd593Sflorian INADDR_ANY; 1170351dd593Sflorian 1171351dd593Sflorian compressed_prefixlen = (prefixlen + 7) / 8; 1172351dd593Sflorian if (dho_len < compressed_prefixlen) 1173351dd593Sflorian goto wrong_length; 1174351dd593Sflorian 1175351dd593Sflorian memcpy(&routes[routes_len].dst, p, 1176351dd593Sflorian compressed_prefixlen); 1177351dd593Sflorian p += compressed_prefixlen; 1178351dd593Sflorian rem -= compressed_prefixlen; 1179351dd593Sflorian dho_len -= compressed_prefixlen; 1180351dd593Sflorian 1181351dd593Sflorian if (dho_len < sizeof(routes[routes_len].gw)) 1182351dd593Sflorian goto wrong_length; 1183351dd593Sflorian 1184351dd593Sflorian memcpy(&routes[routes_len].gw, p, 1185351dd593Sflorian sizeof(routes[routes_len].gw)); 1186351dd593Sflorian p += sizeof(routes[routes_len].gw); 1187351dd593Sflorian rem -= sizeof(routes[routes_len].gw); 1188351dd593Sflorian dho_len -= sizeof(routes[routes_len].gw); 1189351dd593Sflorian 1190351dd593Sflorian routes_len++; 1191351dd593Sflorian } 1192351dd593Sflorian 1193351dd593Sflorian if (dho_len != 0) { 1194351dd593Sflorian /* ignore > MAX_DHCP_ROUTES routes */ 1195351dd593Sflorian p += dho_len; 1196351dd593Sflorian rem -= dho_len; 1197351dd593Sflorian } 1198351dd593Sflorian break; 1199351dd593Sflorian } 12006d7478f0Sflorian case DHO_IPV6_ONLY_PREFERRED: 12016d7478f0Sflorian if (dho_len != sizeof(ipv6_only_time)) 12026d7478f0Sflorian goto wrong_length; 12036d7478f0Sflorian memcpy(&ipv6_only_time, p, sizeof(ipv6_only_time)); 12046d7478f0Sflorian ipv6_only_time = ntohl(ipv6_only_time); 12056d7478f0Sflorian if (log_getverbose() > 1) { 12066d7478f0Sflorian log_debug("DHO_IPV6_ONLY_PREFERRED %us", 12076d7478f0Sflorian ipv6_only_time); 12086d7478f0Sflorian } 12096d7478f0Sflorian p += dho_len; 12106d7478f0Sflorian rem -= dho_len; 12116d7478f0Sflorian break; 121257419a7fSflorian default: 1213e998cdbeSflorian if (log_getverbose() > 1) 121457419a7fSflorian log_debug("DHO_%u, len: %u", dho, dho_len); 121557419a7fSflorian p += dho_len; 121657419a7fSflorian rem -= dho_len; 121757419a7fSflorian } 121857419a7fSflorian 121957419a7fSflorian } 122057419a7fSflorian while (rem != 0) { 122157419a7fSflorian if (*p != DHO_PAD) 122257419a7fSflorian break; 122357419a7fSflorian p++; 122457419a7fSflorian rem--; 122557419a7fSflorian } 122657419a7fSflorian if (rem != 0) 12273efa2e2dStb log_debug("%s: %lu bytes garbage data from %s", __func__, rem, 122857419a7fSflorian from); 122957419a7fSflorian 1230e998cdbeSflorian log_debug("%s on %s from %s/%s to %s/%s", 1231e998cdbeSflorian dhcp_message_type2str(dhcp_message_type), if_name == NULL ? "?" : 1232e998cdbeSflorian if_name, from, hbuf_src, to, hbuf_dst); 1233e998cdbeSflorian 123457419a7fSflorian switch (dhcp_message_type) { 123557419a7fSflorian case DHCPOFFER: 123657419a7fSflorian if (iface->state != IF_INIT) { 123757419a7fSflorian log_debug("ignoring unexpected DHCPOFFER"); 123857419a7fSflorian return; 123957419a7fSflorian } 124057419a7fSflorian if (server_identifier.s_addr == INADDR_ANY && 124157419a7fSflorian dhcp_hdr->yiaddr.s_addr == INADDR_ANY) { 124257419a7fSflorian log_warnx("%s: did not receive server identifier or " 124357419a7fSflorian "offered IP address", __func__); 124457419a7fSflorian return; 124557419a7fSflorian } 12466d7478f0Sflorian #ifndef SMALL 12476d7478f0Sflorian if (iface_conf != NULL && iface_conf->prefer_ipv6 && 12486d7478f0Sflorian ipv6_only_time > 0) { 12496d7478f0Sflorian iface->ipv6_only_time = ipv6_only_time; 12506d7478f0Sflorian state_transition(iface, IF_IPV6_ONLY); 12516d7478f0Sflorian break; 12526d7478f0Sflorian } 12536d7478f0Sflorian #endif 1254b2b40540Sflorian iface->server_identifier = server_identifier; 1255b2b40540Sflorian iface->dhcp_server = server_identifier; 1256b2b40540Sflorian iface->requested_ip = dhcp_hdr->yiaddr; 125757419a7fSflorian state_transition(iface, IF_REQUESTING); 125857419a7fSflorian break; 125957419a7fSflorian case DHCPACK: 126057419a7fSflorian switch (iface->state) { 126157419a7fSflorian case IF_REQUESTING: 126257419a7fSflorian case IF_RENEWING: 126357419a7fSflorian case IF_REBINDING: 126457419a7fSflorian case IF_REBOOTING: 126557419a7fSflorian break; 126657419a7fSflorian default: 126757419a7fSflorian log_debug("ignoring unexpected DHCPACK"); 126857419a7fSflorian return; 126957419a7fSflorian } 127057419a7fSflorian if (server_identifier.s_addr == INADDR_ANY && 127157419a7fSflorian dhcp_hdr->yiaddr.s_addr == INADDR_ANY) { 127257419a7fSflorian log_warnx("%s: did not receive server identifier or " 127357419a7fSflorian "offered IP address", __func__); 127457419a7fSflorian return; 127557419a7fSflorian } 127657419a7fSflorian if (lease_time == 0) { 127757419a7fSflorian log_warnx("%s no lease time from %s", __func__, from); 127857419a7fSflorian return; 127957419a7fSflorian } 128057419a7fSflorian if (subnet_mask.s_addr == INADDR_ANY) { 128157419a7fSflorian log_warnx("%s: no subnetmask received from %s", 128257419a7fSflorian __func__, from); 128357419a7fSflorian return; 128457419a7fSflorian } 128557419a7fSflorian 12865fbfa9a9Sflorian /* Defaults if we didn't receive renewal or rebinding time. */ 12875fbfa9a9Sflorian if (renewal_time == 0) 12885fbfa9a9Sflorian renewal_time = lease_time / 2; 12895fbfa9a9Sflorian if (rebinding_time == 0) 12905fbfa9a9Sflorian rebinding_time = lease_time - (lease_time / 8); 12915fbfa9a9Sflorian 129257419a7fSflorian /* RFC 2131 4.4.5 */ 12932edea3cbSmartijn /* Ignore invalid T1/T2 options */ 12942edea3cbSmartijn if (renewal_time >= rebinding_time) { 12952edea3cbSmartijn log_warnx("%s: renewal_time(%u) >= rebinding_time(%u) " 12962edea3cbSmartijn "from %s: using defaults", 12972edea3cbSmartijn __func__, renewal_time, rebinding_time, from); 12982edea3cbSmartijn renewal_time = rebinding_time = 0; 12992edea3cbSmartijn } else if (rebinding_time >= lease_time) { 13002edea3cbSmartijn log_warnx("%s: rebinding_time(%u) >= lease_time(%u) " 13012edea3cbSmartijn "from %s: using defaults", 13022edea3cbSmartijn __func__, rebinding_time, lease_time, from); 13032edea3cbSmartijn renewal_time = rebinding_time = 0; 13042edea3cbSmartijn } 13052edea3cbSmartijn 13065fbfa9a9Sflorian /* Defaults if we received wrong renewal or rebinding time. */ 130757419a7fSflorian if (renewal_time == 0) 130857419a7fSflorian renewal_time = lease_time / 2; 130957419a7fSflorian if (rebinding_time == 0) 131007355dbdSotto rebinding_time = lease_time - (lease_time / 8); 131157419a7fSflorian 131257419a7fSflorian clock_gettime(CLOCK_MONOTONIC, &iface->request_time); 1313b2b40540Sflorian iface->server_identifier = server_identifier; 1314b2b40540Sflorian iface->dhcp_server = server_identifier; 1315b2b40540Sflorian iface->requested_ip = dhcp_hdr->yiaddr; 1316b2b40540Sflorian iface->mask = subnet_mask; 1317c2bc6c6dSflorian #ifndef SMALL 1318c2bc6c6dSflorian if (iface_conf != NULL && iface_conf->ignore & IGN_ROUTES) { 1319c2bc6c6dSflorian iface->routes_len = 0; 1320c2bc6c6dSflorian memset(iface->routes, 0, sizeof(iface->routes)); 1321c2bc6c6dSflorian } else 1322c2bc6c6dSflorian #endif /* SMALL */ 1323c2bc6c6dSflorian { 13246aeacae8Sflorian iface->prev_routes_len = iface->routes_len; 13256aeacae8Sflorian memcpy(iface->prev_routes, iface->routes, 13266aeacae8Sflorian sizeof(iface->prev_routes)); 1327351dd593Sflorian iface->routes_len = routes_len; 1328351dd593Sflorian memcpy(iface->routes, routes, sizeof(iface->routes)); 1329c2bc6c6dSflorian } 133057419a7fSflorian iface->lease_time = lease_time; 133157419a7fSflorian iface->renewal_time = renewal_time; 133257419a7fSflorian iface->rebinding_time = rebinding_time; 1333c2bc6c6dSflorian 1334c2bc6c6dSflorian #ifndef SMALL 1335c2bc6c6dSflorian if (iface_conf != NULL && iface_conf->ignore & IGN_DNS) { 1336c2bc6c6dSflorian memset(iface->nameservers, 0, 1337c2bc6c6dSflorian sizeof(iface->nameservers)); 1338c2bc6c6dSflorian } else 1339c2bc6c6dSflorian #endif /* SMALL */ 1340c2bc6c6dSflorian { 134157419a7fSflorian memcpy(iface->nameservers, nameservers, 134257419a7fSflorian sizeof(iface->nameservers)); 1343c2bc6c6dSflorian } 1344ebace80cSflorian 1345b2b40540Sflorian iface->siaddr = dhcp_hdr->siaddr; 1346ebace80cSflorian 1347ebace80cSflorian /* we made sure this is a string futher up */ 1348ebace80cSflorian strnvis(iface->file, dhcp_hdr->file, sizeof(iface->file), 1349ebace80cSflorian VIS_SAFE); 1350ebace80cSflorian 1351ebace80cSflorian strlcpy(iface->domainname, domainname, 1352ebace80cSflorian sizeof(iface->domainname)); 1353ebace80cSflorian strlcpy(iface->hostname, hostname, sizeof(iface->hostname)); 13546d7478f0Sflorian #ifndef SMALL 13556d7478f0Sflorian if (iface_conf != NULL && iface_conf->prefer_ipv6 && 13566d7478f0Sflorian ipv6_only_time > 0) { 13576d7478f0Sflorian iface->ipv6_only_time = ipv6_only_time; 13586d7478f0Sflorian state_transition(iface, IF_IPV6_ONLY); 13596d7478f0Sflorian break; 13606d7478f0Sflorian } 13616d7478f0Sflorian #endif 136257419a7fSflorian state_transition(iface, IF_BOUND); 136357419a7fSflorian break; 136457419a7fSflorian case DHCPNAK: 136557419a7fSflorian switch (iface->state) { 136657419a7fSflorian case IF_REQUESTING: 136757419a7fSflorian case IF_RENEWING: 136857419a7fSflorian case IF_REBINDING: 136957419a7fSflorian case IF_REBOOTING: 137057419a7fSflorian break; 137157419a7fSflorian default: 137257419a7fSflorian log_debug("ignoring unexpected DHCPNAK"); 137357419a7fSflorian return; 137457419a7fSflorian } 137557419a7fSflorian 137657419a7fSflorian state_transition(iface, IF_INIT); 137757419a7fSflorian break; 137857419a7fSflorian default: 137957419a7fSflorian log_warnx("%s: unimplemented message type %d", __func__, 138057419a7fSflorian dhcp_message_type); 138157419a7fSflorian break; 138257419a7fSflorian } 138357419a7fSflorian return; 138457419a7fSflorian too_short: 138557419a7fSflorian log_warnx("%s: message from %s too short", __func__, from); 138657419a7fSflorian return; 138757419a7fSflorian wrong_length: 138857419a7fSflorian log_warnx("%s: received option %d with wrong length: %d", __func__, 138957419a7fSflorian dho, dho_len); 139057419a7fSflorian return; 139157419a7fSflorian } 139257419a7fSflorian 139357419a7fSflorian /* XXX check valid transitions */ 139457419a7fSflorian void 139557419a7fSflorian state_transition(struct dhcpleased_iface *iface, enum if_state new_state) 139657419a7fSflorian { 139757419a7fSflorian enum if_state old_state = iface->state; 139857419a7fSflorian struct timespec now, res; 1399e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 140057419a7fSflorian 140157419a7fSflorian iface->state = new_state; 14029e257558Sflorian 140357419a7fSflorian switch (new_state) { 140457419a7fSflorian case IF_DOWN: 140557419a7fSflorian if (iface->requested_ip.s_addr == INADDR_ANY) { 140657419a7fSflorian /* nothing to do until iface comes up */ 140757419a7fSflorian iface->timo.tv_sec = -1; 140857419a7fSflorian break; 140957419a7fSflorian } 141057419a7fSflorian if (old_state == IF_DOWN) { 1411b7f83d9aSflorian /* nameservers already withdrawn when if went down */ 141257419a7fSflorian send_deconfigure_interface(iface); 141357419a7fSflorian /* nothing more to do until iface comes back */ 141457419a7fSflorian iface->timo.tv_sec = -1; 141557419a7fSflorian } else { 141657419a7fSflorian send_rdns_withdraw(iface); 141757419a7fSflorian clock_gettime(CLOCK_MONOTONIC, &now); 141857419a7fSflorian timespecsub(&now, &iface->request_time, &res); 141957419a7fSflorian iface->timo.tv_sec = iface->lease_time - res.tv_sec; 142057419a7fSflorian if (iface->timo.tv_sec < 0) 142157419a7fSflorian iface->timo.tv_sec = 0; /* deconfigure now */ 142257419a7fSflorian } 142357419a7fSflorian break; 142457419a7fSflorian case IF_INIT: 14258baf56acSflorian switch (old_state) { 14268baf56acSflorian case IF_INIT: 14278baf56acSflorian if (iface->timo.tv_sec < MAX_EXP_BACKOFF_SLOW) 14288baf56acSflorian iface->timo.tv_sec *= 2; 14298baf56acSflorian break; 14308baf56acSflorian case IF_REQUESTING: 14318baf56acSflorian case IF_RENEWING: 14328baf56acSflorian case IF_REBINDING: 14338baf56acSflorian case IF_REBOOTING: 14348baf56acSflorian /* lease expired, got DHCPNAK or timeout: delete IP */ 143557419a7fSflorian send_rdns_withdraw(iface); 1436b7f83d9aSflorian send_deconfigure_interface(iface); 14378baf56acSflorian /* fall through */ 14388baf56acSflorian case IF_DOWN: 14396d7478f0Sflorian case IF_IPV6_ONLY: 1440acf27097Sflorian iface->timo.tv_sec = START_EXP_BACKOFF; 1441ee9c6200Sflorian iface->xid = arc4random(); 14428baf56acSflorian break; 14438baf56acSflorian case IF_BOUND: 1444c7074a52Sflorian fatalx("invalid transition Bound -> Init"); 14458baf56acSflorian break; 14468baf56acSflorian } 144757419a7fSflorian request_dhcp_discover(iface); 144857419a7fSflorian break; 144957419a7fSflorian case IF_REBOOTING: 1450af5ab751Sflorian if (old_state == IF_REBOOTING) 145157419a7fSflorian iface->timo.tv_sec *= 2; 1452ee9c6200Sflorian else { 1453acf27097Sflorian iface->timo.tv_sec = START_EXP_BACKOFF; 1454ee9c6200Sflorian iface->xid = arc4random(); 1455ee9c6200Sflorian } 145657419a7fSflorian request_dhcp_request(iface); 145757419a7fSflorian break; 145857419a7fSflorian case IF_REQUESTING: 1459af5ab751Sflorian if (old_state == IF_REQUESTING) 146057419a7fSflorian iface->timo.tv_sec *= 2; 1461af5ab751Sflorian else 1462acf27097Sflorian iface->timo.tv_sec = START_EXP_BACKOFF; 146357419a7fSflorian request_dhcp_request(iface); 146457419a7fSflorian break; 146557419a7fSflorian case IF_BOUND: 146657419a7fSflorian iface->timo.tv_sec = iface->renewal_time; 146757419a7fSflorian if (old_state == IF_REQUESTING || old_state == IF_REBOOTING) { 146857419a7fSflorian send_configure_interface(iface); 146957419a7fSflorian send_rdns_proposal(iface); 147057419a7fSflorian } 147157419a7fSflorian break; 147257419a7fSflorian case IF_RENEWING: 147357419a7fSflorian if (old_state == IF_BOUND) { 147457419a7fSflorian iface->timo.tv_sec = (iface->rebinding_time - 147557419a7fSflorian iface->renewal_time) / 2; /* RFC 2131 4.4.5 */ 1476ee9c6200Sflorian iface->xid = arc4random(); 147757419a7fSflorian } else 147857419a7fSflorian iface->timo.tv_sec /= 2; 147957419a7fSflorian 148057419a7fSflorian if (iface->timo.tv_sec < 60) 148157419a7fSflorian iface->timo.tv_sec = 60; 148257419a7fSflorian request_dhcp_request(iface); 148357419a7fSflorian break; 148457419a7fSflorian case IF_REBINDING: 148557419a7fSflorian if (old_state == IF_RENEWING) { 148657419a7fSflorian iface->timo.tv_sec = (iface->lease_time - 1487aefb9fc2Sflorian iface->rebinding_time) / 2; /* RFC 2131 4.4.5 */ 148857419a7fSflorian } else 148957419a7fSflorian iface->timo.tv_sec /= 2; 149057419a7fSflorian request_dhcp_request(iface); 149157419a7fSflorian break; 14926d7478f0Sflorian case IF_IPV6_ONLY: 14936d7478f0Sflorian switch (old_state) { 14946d7478f0Sflorian case IF_REQUESTING: 14956d7478f0Sflorian case IF_RENEWING: 14966d7478f0Sflorian case IF_REBINDING: 14976d7478f0Sflorian case IF_REBOOTING: 14986d7478f0Sflorian /* going IPv6 only: delete legacy IP */ 14996d7478f0Sflorian send_rdns_withdraw(iface); 15006d7478f0Sflorian send_deconfigure_interface(iface); 15016d7478f0Sflorian /* fall through */ 15026d7478f0Sflorian case IF_INIT: 15036d7478f0Sflorian case IF_DOWN: 15046d7478f0Sflorian case IF_IPV6_ONLY: 15056d7478f0Sflorian iface->timo.tv_sec = iface->ipv6_only_time; 15066d7478f0Sflorian break; 15076d7478f0Sflorian case IF_BOUND: 1508c7074a52Sflorian fatalx("invalid transition Bound -> IPv6 only"); 15096d7478f0Sflorian break; 15106d7478f0Sflorian } 151157419a7fSflorian } 1512e998cdbeSflorian 1513e998cdbeSflorian if_name = if_indextoname(iface->if_index, ifnamebuf); 1514e998cdbeSflorian log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL ? 1515e998cdbeSflorian "?" : if_name, if_state_name[old_state], if_state_name[new_state], 1516e998cdbeSflorian iface->timo.tv_sec); 1517e998cdbeSflorian 151857419a7fSflorian if (iface->timo.tv_sec == -1) { 151957419a7fSflorian if (evtimer_pending(&iface->timer, NULL)) 152057419a7fSflorian evtimer_del(&iface->timer); 152157419a7fSflorian } else 152257419a7fSflorian evtimer_add(&iface->timer, &iface->timo); 152357419a7fSflorian } 152457419a7fSflorian 152557419a7fSflorian void 152657419a7fSflorian iface_timeout(int fd, short events, void *arg) 152757419a7fSflorian { 152857419a7fSflorian struct dhcpleased_iface *iface = (struct dhcpleased_iface *)arg; 152957419a7fSflorian struct timespec now, res; 153057419a7fSflorian 153157419a7fSflorian log_debug("%s[%d]: %s", __func__, iface->if_index, 153257419a7fSflorian if_state_name[iface->state]); 153357419a7fSflorian 153457419a7fSflorian switch (iface->state) { 153557419a7fSflorian case IF_DOWN: 153657419a7fSflorian state_transition(iface, IF_DOWN); 153757419a7fSflorian break; 153857419a7fSflorian case IF_INIT: 153957419a7fSflorian state_transition(iface, IF_INIT); 154057419a7fSflorian break; 154157419a7fSflorian case IF_REBOOTING: 15428baf56acSflorian if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_FAST) 154357419a7fSflorian state_transition(iface, IF_INIT); 154457419a7fSflorian else 154557419a7fSflorian state_transition(iface, IF_REBOOTING); 154657419a7fSflorian break; 154757419a7fSflorian case IF_REQUESTING: 1548c6b4c3c3Sflorian if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_SLOW) 154957419a7fSflorian state_transition(iface, IF_INIT); 155057419a7fSflorian else 155157419a7fSflorian state_transition(iface, IF_REQUESTING); 155257419a7fSflorian break; 155357419a7fSflorian case IF_BOUND: 155457419a7fSflorian state_transition(iface, IF_RENEWING); 155557419a7fSflorian break; 155657419a7fSflorian case IF_RENEWING: 155757419a7fSflorian clock_gettime(CLOCK_MONOTONIC, &now); 155857419a7fSflorian timespecsub(&now, &iface->request_time, &res); 155957419a7fSflorian log_debug("%s: res.tv_sec: %lld, rebinding_time: %u", __func__, 156057419a7fSflorian res.tv_sec, iface->rebinding_time); 1561c5d4eb4eSflorian if (res.tv_sec >= iface->rebinding_time) 156257419a7fSflorian state_transition(iface, IF_REBINDING); 156357419a7fSflorian else 156457419a7fSflorian state_transition(iface, IF_RENEWING); 156557419a7fSflorian break; 156657419a7fSflorian case IF_REBINDING: 156757419a7fSflorian clock_gettime(CLOCK_MONOTONIC, &now); 156857419a7fSflorian timespecsub(&now, &iface->request_time, &res); 156957419a7fSflorian log_debug("%s: res.tv_sec: %lld, lease_time: %u", __func__, 157057419a7fSflorian res.tv_sec, iface->lease_time); 157157419a7fSflorian if (res.tv_sec > iface->lease_time) 157257419a7fSflorian state_transition(iface, IF_INIT); 157357419a7fSflorian else 157457419a7fSflorian state_transition(iface, IF_REBINDING); 157557419a7fSflorian break; 15766d7478f0Sflorian case IF_IPV6_ONLY: 15776d7478f0Sflorian state_transition(iface, IF_REQUESTING); 15786d7478f0Sflorian break; 157957419a7fSflorian } 158057419a7fSflorian } 158157419a7fSflorian 158257419a7fSflorian void 158357419a7fSflorian request_dhcp_discover(struct dhcpleased_iface *iface) 158457419a7fSflorian { 158582a5f3d7Sflorian struct imsg_req_dhcp imsg; 158657419a7fSflorian 158782a5f3d7Sflorian memset(&imsg, 0, sizeof(imsg)); 158882a5f3d7Sflorian 158982a5f3d7Sflorian imsg.if_index = iface->if_index; 159082a5f3d7Sflorian imsg.xid = iface->xid; 159182a5f3d7Sflorian 159282a5f3d7Sflorian /* 159382a5f3d7Sflorian * similar to RFC 2131 4.3.6, Table 4 for DHCPDISCOVER 159482a5f3d7Sflorian * ------------------------------ 159582a5f3d7Sflorian * | | INIT | 159682a5f3d7Sflorian * ------------------------------ 159782a5f3d7Sflorian * |broad/unicast | broadcast | 159882a5f3d7Sflorian * |server-ip | MUST NOT | 159982a5f3d7Sflorian * |requested-ip | MAY | 160082a5f3d7Sflorian * |ciaddr | zero | 160182a5f3d7Sflorian * ------------------------------ 160282a5f3d7Sflorian * 160382a5f3d7Sflorian * Leaving everything at 0 from the memset results in this table with 160482a5f3d7Sflorian * requested-ip not set. 160582a5f3d7Sflorian */ 160682a5f3d7Sflorian 160782a5f3d7Sflorian engine_imsg_compose_frontend(IMSG_SEND_DISCOVER, 0, &imsg, sizeof(imsg)); 160857419a7fSflorian } 160957419a7fSflorian 161057419a7fSflorian void 161157419a7fSflorian request_dhcp_request(struct dhcpleased_iface *iface) 161257419a7fSflorian { 161382a5f3d7Sflorian struct imsg_req_dhcp imsg; 161457419a7fSflorian 161582a5f3d7Sflorian imsg.if_index = iface->if_index; 161682a5f3d7Sflorian imsg.xid = iface->xid; 161782a5f3d7Sflorian 161882a5f3d7Sflorian /* 161982a5f3d7Sflorian * RFC 2131 4.3.6, Table 4 162082a5f3d7Sflorian * --------------------------------------------------------------------- 162182a5f3d7Sflorian * | |REBOOTING |REQUESTING |RENEWING |REBINDING | 162282a5f3d7Sflorian * --------------------------------------------------------------------- 162382a5f3d7Sflorian * |broad/unicast |broadcast |broadcast |unicast |broadcast | 162482a5f3d7Sflorian * |server-ip |MUST NOT |MUST |MUST NOT |MUST NOT | 162582a5f3d7Sflorian * |requested-ip |MUST |MUST |MUST NOT |MUST NOT | 162682a5f3d7Sflorian * |ciaddr |zero |zero |IP address |IP address| 162782a5f3d7Sflorian * --------------------------------------------------------------------- 162882a5f3d7Sflorian */ 162982a5f3d7Sflorian switch (iface->state) { 163082a5f3d7Sflorian case IF_DOWN: 163182a5f3d7Sflorian fatalx("invalid state IF_DOWN in %s", __func__); 163282a5f3d7Sflorian break; 163382a5f3d7Sflorian case IF_INIT: 163482a5f3d7Sflorian fatalx("invalid state IF_INIT in %s", __func__); 163582a5f3d7Sflorian break; 163682a5f3d7Sflorian case IF_BOUND: 163782a5f3d7Sflorian fatalx("invalid state IF_BOUND in %s", __func__); 163882a5f3d7Sflorian break; 163982a5f3d7Sflorian case IF_REBOOTING: 164082a5f3d7Sflorian imsg.dhcp_server.s_addr = INADDR_ANY; /* broadcast */ 164182a5f3d7Sflorian imsg.server_identifier.s_addr = INADDR_ANY; /* MUST NOT */ 164282a5f3d7Sflorian imsg.requested_ip = iface->requested_ip; /* MUST */ 164382a5f3d7Sflorian imsg.ciaddr.s_addr = INADDR_ANY; /* zero */ 164482a5f3d7Sflorian break; 164582a5f3d7Sflorian case IF_REQUESTING: 164682a5f3d7Sflorian imsg.dhcp_server.s_addr = INADDR_ANY; /* broadcast */ 164782a5f3d7Sflorian imsg.server_identifier = 164882a5f3d7Sflorian iface->server_identifier; /* MUST */ 164982a5f3d7Sflorian imsg.requested_ip = iface->requested_ip; /* MUST */ 165082a5f3d7Sflorian imsg.ciaddr.s_addr = INADDR_ANY; /* zero */ 165182a5f3d7Sflorian break; 165282a5f3d7Sflorian case IF_RENEWING: 165382a5f3d7Sflorian imsg.dhcp_server = iface->dhcp_server; /* unicast */ 165482a5f3d7Sflorian imsg.server_identifier.s_addr = INADDR_ANY; /* MUST NOT */ 165582a5f3d7Sflorian imsg.requested_ip.s_addr = INADDR_ANY; /* MUST NOT */ 165682a5f3d7Sflorian imsg.ciaddr = iface->requested_ip; /* IP address */ 165782a5f3d7Sflorian break; 165882a5f3d7Sflorian case IF_REBINDING: 165982a5f3d7Sflorian imsg.dhcp_server.s_addr = INADDR_ANY; /* broadcast */ 166082a5f3d7Sflorian imsg.server_identifier.s_addr = INADDR_ANY; /* MUST NOT */ 166182a5f3d7Sflorian imsg.requested_ip.s_addr = INADDR_ANY; /* MUST NOT */ 166282a5f3d7Sflorian imsg.ciaddr = iface->requested_ip; /* IP address */ 166382a5f3d7Sflorian break; 16646d7478f0Sflorian case IF_IPV6_ONLY: 16656d7478f0Sflorian fatalx("invalid state IF_IPV6_ONLY in %s", __func__); 16666d7478f0Sflorian break; 166782a5f3d7Sflorian } 166882a5f3d7Sflorian 166982a5f3d7Sflorian engine_imsg_compose_frontend(IMSG_SEND_REQUEST, 0, &imsg, sizeof(imsg)); 167057419a7fSflorian } 167157419a7fSflorian 167257419a7fSflorian void 16730bc729e2Sflorian log_lease(struct dhcpleased_iface *iface, int deconfigure) 16740bc729e2Sflorian { 16750bc729e2Sflorian char hbuf_lease[INET_ADDRSTRLEN], hbuf_server[INET_ADDRSTRLEN]; 1676e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 16770bc729e2Sflorian 1678e998cdbeSflorian if_name = if_indextoname(iface->if_index, ifnamebuf); 16790bc729e2Sflorian inet_ntop(AF_INET, &iface->requested_ip, hbuf_lease, 16800bc729e2Sflorian sizeof(hbuf_lease)); 16810bc729e2Sflorian inet_ntop(AF_INET, &iface->server_identifier, hbuf_server, 16820bc729e2Sflorian sizeof(hbuf_server)); 16830bc729e2Sflorian 16840bc729e2Sflorian 16850bc729e2Sflorian if (deconfigure) 16860bc729e2Sflorian log_info("deleting %s from %s (lease from %s)", hbuf_lease, 1687e998cdbeSflorian if_name == NULL ? "?" : if_name, hbuf_server); 16880bc729e2Sflorian else 16890bc729e2Sflorian log_info("adding %s to %s (lease from %s)", hbuf_lease, 1690e998cdbeSflorian if_name == NULL ? "?" : if_name, hbuf_server); 16910bc729e2Sflorian } 16920bc729e2Sflorian 16930bc729e2Sflorian void 169457419a7fSflorian send_configure_interface(struct dhcpleased_iface *iface) 169557419a7fSflorian { 169657419a7fSflorian struct imsg_configure_interface imsg; 16976aeacae8Sflorian int i, j, found; 169857419a7fSflorian 16990bc729e2Sflorian log_lease(iface, 0); 17000bc729e2Sflorian 17016aeacae8Sflorian memset(&imsg, 0, sizeof(imsg)); 170257419a7fSflorian imsg.if_index = iface->if_index; 170357419a7fSflorian imsg.rdomain = iface->rdomain; 1704b2b40540Sflorian imsg.addr = iface->requested_ip; 1705b2b40540Sflorian imsg.mask = iface->mask; 1706b2b40540Sflorian imsg.siaddr = iface->siaddr; 1707ebace80cSflorian strlcpy(imsg.file, iface->file, sizeof(imsg.file)); 1708ebace80cSflorian strlcpy(imsg.domainname, iface->domainname, sizeof(imsg.domainname)); 1709ebace80cSflorian strlcpy(imsg.hostname, iface->hostname, sizeof(imsg.hostname)); 17106aeacae8Sflorian for (i = 0; i < iface->prev_routes_len; i++) { 17116aeacae8Sflorian found = 0; 17126aeacae8Sflorian for (j = 0; j < iface->routes_len; j++) { 17136aeacae8Sflorian if (memcmp(&iface->prev_routes[i], &iface->routes[j], 17146aeacae8Sflorian sizeof(struct dhcp_route)) == 0) { 17156aeacae8Sflorian found = 1; 17166aeacae8Sflorian break; 17176aeacae8Sflorian } 17186aeacae8Sflorian } 17196aeacae8Sflorian if (!found) 17206aeacae8Sflorian imsg.routes[imsg.routes_len++] = iface->prev_routes[i]; 17216aeacae8Sflorian } 17226aeacae8Sflorian if (imsg.routes_len > 0) 17236aeacae8Sflorian engine_imsg_compose_main(IMSG_WITHDRAW_ROUTES, 0, &imsg, 17246aeacae8Sflorian sizeof(imsg)); 1725351dd593Sflorian imsg.routes_len = iface->routes_len; 1726351dd593Sflorian memcpy(imsg.routes, iface->routes, sizeof(imsg.routes)); 172757419a7fSflorian engine_imsg_compose_main(IMSG_CONFIGURE_INTERFACE, 0, &imsg, 172857419a7fSflorian sizeof(imsg)); 172957419a7fSflorian } 173057419a7fSflorian 173157419a7fSflorian void 173257419a7fSflorian send_deconfigure_interface(struct dhcpleased_iface *iface) 173357419a7fSflorian { 173457419a7fSflorian struct imsg_configure_interface imsg; 173557419a7fSflorian 17364083f3dcSflorian if (iface->requested_ip.s_addr == INADDR_ANY) 17374083f3dcSflorian return; 17384083f3dcSflorian 17390bc729e2Sflorian log_lease(iface, 1); 17400bc729e2Sflorian 174157419a7fSflorian imsg.if_index = iface->if_index; 174257419a7fSflorian imsg.rdomain = iface->rdomain; 1743b2b40540Sflorian imsg.addr = iface->requested_ip; 1744b2b40540Sflorian imsg.mask = iface->mask; 1745b2b40540Sflorian imsg.siaddr = iface->siaddr; 1746ebace80cSflorian strlcpy(imsg.file, iface->file, sizeof(imsg.file)); 1747ebace80cSflorian strlcpy(imsg.domainname, iface->domainname, sizeof(imsg.domainname)); 1748ebace80cSflorian strlcpy(imsg.hostname, iface->hostname, sizeof(imsg.hostname)); 1749351dd593Sflorian imsg.routes_len = iface->routes_len; 1750351dd593Sflorian memcpy(imsg.routes, iface->routes, sizeof(imsg.routes)); 175157419a7fSflorian engine_imsg_compose_main(IMSG_DECONFIGURE_INTERFACE, 0, &imsg, 175257419a7fSflorian sizeof(imsg)); 1753b7f83d9aSflorian 1754b7f83d9aSflorian iface->server_identifier.s_addr = INADDR_ANY; 1755b7f83d9aSflorian iface->dhcp_server.s_addr = INADDR_ANY; 1756b7f83d9aSflorian iface->requested_ip.s_addr = INADDR_ANY; 1757b7f83d9aSflorian iface->mask.s_addr = INADDR_ANY; 1758351dd593Sflorian iface->routes_len = 0; 1759351dd593Sflorian memset(iface->routes, 0, sizeof(iface->routes)); 176057419a7fSflorian } 176157419a7fSflorian 176257419a7fSflorian void 1763c2bc6c6dSflorian send_routes_withdraw(struct dhcpleased_iface *iface) 1764c2bc6c6dSflorian { 1765c2bc6c6dSflorian struct imsg_configure_interface imsg; 1766c2bc6c6dSflorian 1767c2bc6c6dSflorian if (iface->requested_ip.s_addr == INADDR_ANY || iface->routes_len == 0) 1768c2bc6c6dSflorian return; 1769c2bc6c6dSflorian 1770c2bc6c6dSflorian imsg.if_index = iface->if_index; 1771c2bc6c6dSflorian imsg.rdomain = iface->rdomain; 1772b2b40540Sflorian imsg.addr = iface->requested_ip; 1773b2b40540Sflorian imsg.mask = iface->mask; 1774b2b40540Sflorian imsg.siaddr = iface->siaddr; 1775c2bc6c6dSflorian strlcpy(imsg.file, iface->file, sizeof(imsg.file)); 1776c2bc6c6dSflorian strlcpy(imsg.domainname, iface->domainname, sizeof(imsg.domainname)); 1777c2bc6c6dSflorian strlcpy(imsg.hostname, iface->hostname, sizeof(imsg.hostname)); 1778c2bc6c6dSflorian imsg.routes_len = iface->routes_len; 1779c2bc6c6dSflorian memcpy(imsg.routes, iface->routes, sizeof(imsg.routes)); 1780c2bc6c6dSflorian engine_imsg_compose_main(IMSG_WITHDRAW_ROUTES, 0, &imsg, 1781c2bc6c6dSflorian sizeof(imsg)); 1782c2bc6c6dSflorian } 1783c2bc6c6dSflorian 1784c2bc6c6dSflorian void 17850bc729e2Sflorian log_rdns(struct dhcpleased_iface *iface, int withdraw) 17860bc729e2Sflorian { 17870bc729e2Sflorian int i; 17880bc729e2Sflorian char hbuf_rdns[INET_ADDRSTRLEN], hbuf_server[INET_ADDRSTRLEN]; 1789e998cdbeSflorian char ifnamebuf[IF_NAMESIZE], *if_name, *rdns_buf = NULL, *tmp_buf; 17900bc729e2Sflorian 1791e998cdbeSflorian if_name = if_indextoname(iface->if_index, ifnamebuf); 17920bc729e2Sflorian 17930bc729e2Sflorian inet_ntop(AF_INET, &iface->server_identifier, hbuf_server, 17940bc729e2Sflorian sizeof(hbuf_server)); 17950bc729e2Sflorian 17960bc729e2Sflorian for (i = 0; i < MAX_RDNS_COUNT && iface->nameservers[i].s_addr != 17970bc729e2Sflorian INADDR_ANY; i++) { 17980bc729e2Sflorian inet_ntop(AF_INET, &iface->nameservers[i], hbuf_rdns, 17990bc729e2Sflorian sizeof(hbuf_rdns)); 18000bc729e2Sflorian tmp_buf = rdns_buf; 18010bc729e2Sflorian if (asprintf(&rdns_buf, "%s %s", tmp_buf ? tmp_buf : "", 18020bc729e2Sflorian hbuf_rdns) < 0) { 18030bc729e2Sflorian rdns_buf = NULL; 18040bc729e2Sflorian break; 18050bc729e2Sflorian } 18060bc729e2Sflorian free(tmp_buf); 18070bc729e2Sflorian } 18080bc729e2Sflorian 18090bc729e2Sflorian if (rdns_buf != NULL) { 1810e998cdbeSflorian if (withdraw) { 18110bc729e2Sflorian log_info("deleting nameservers%s (lease from %s on %s)", 1812e998cdbeSflorian rdns_buf, hbuf_server, if_name == NULL ? "?" : 1813e998cdbeSflorian if_name); 1814e998cdbeSflorian } else { 18150bc729e2Sflorian log_info("adding nameservers%s (lease from %s on %s)", 1816e998cdbeSflorian rdns_buf, hbuf_server, if_name == NULL ? "?" : 1817e998cdbeSflorian if_name); 1818e998cdbeSflorian } 18190bc729e2Sflorian free(rdns_buf); 18200bc729e2Sflorian } 18210bc729e2Sflorian } 18220bc729e2Sflorian 18230bc729e2Sflorian void 182457419a7fSflorian send_rdns_proposal(struct dhcpleased_iface *iface) 182557419a7fSflorian { 182657419a7fSflorian struct imsg_propose_rdns imsg; 182757419a7fSflorian 18280bc729e2Sflorian log_rdns(iface, 0); 18290bc729e2Sflorian 183057419a7fSflorian memset(&imsg, 0, sizeof(imsg)); 183157419a7fSflorian 183257419a7fSflorian imsg.if_index = iface->if_index; 183357419a7fSflorian imsg.rdomain = iface->rdomain; 183457419a7fSflorian for (imsg.rdns_count = 0; imsg.rdns_count < MAX_RDNS_COUNT && 183557419a7fSflorian iface->nameservers[imsg.rdns_count].s_addr != INADDR_ANY; 183657419a7fSflorian imsg.rdns_count++) 183757419a7fSflorian ; 183857419a7fSflorian memcpy(imsg.rdns, iface->nameservers, sizeof(imsg.rdns)); 183957419a7fSflorian engine_imsg_compose_main(IMSG_PROPOSE_RDNS, 0, &imsg, sizeof(imsg)); 184057419a7fSflorian } 184157419a7fSflorian 184257419a7fSflorian void 184357419a7fSflorian send_rdns_withdraw(struct dhcpleased_iface *iface) 184457419a7fSflorian { 184557419a7fSflorian struct imsg_propose_rdns imsg; 184657419a7fSflorian 18470bc729e2Sflorian log_rdns(iface, 1); 18480bc729e2Sflorian 184957419a7fSflorian memset(&imsg, 0, sizeof(imsg)); 185057419a7fSflorian 185157419a7fSflorian imsg.if_index = iface->if_index; 185257419a7fSflorian imsg.rdomain = iface->rdomain; 185357419a7fSflorian engine_imsg_compose_main(IMSG_WITHDRAW_RDNS, 0, &imsg, sizeof(imsg)); 1854b7f83d9aSflorian memset(iface->nameservers, 0, sizeof(iface->nameservers)); 185557419a7fSflorian } 185657419a7fSflorian 185757419a7fSflorian void 18586e93e3e9Sflorian parse_lease(struct dhcpleased_iface *iface, struct imsg_ifinfo *imsg_ifinfo) 185957419a7fSflorian { 18606e93e3e9Sflorian char *p, *p1; 186157419a7fSflorian 18626e93e3e9Sflorian iface->requested_ip.s_addr = INADDR_ANY; 186357419a7fSflorian 1864ebace80cSflorian if ((p = strstr(imsg_ifinfo->lease, LEASE_IP_PREFIX)) == NULL) 18656e93e3e9Sflorian return; 186657419a7fSflorian 1867ebace80cSflorian p += sizeof(LEASE_IP_PREFIX) - 1; 186857419a7fSflorian if ((p1 = strchr(p, '\n')) == NULL) 18696e93e3e9Sflorian return; 187057419a7fSflorian *p1 = '\0'; 187157419a7fSflorian 187257419a7fSflorian if (inet_pton(AF_INET, p, &iface->requested_ip) != 1) 187357419a7fSflorian iface->requested_ip.s_addr = INADDR_ANY; 187457419a7fSflorian } 1875e998cdbeSflorian 1876e998cdbeSflorian void 1877e998cdbeSflorian log_dhcp_hdr(struct dhcp_hdr *dhcp_hdr) 1878e998cdbeSflorian { 18794730fc81Sflorian #ifndef SMALL 1880e998cdbeSflorian char hbuf[INET_ADDRSTRLEN]; 1881e998cdbeSflorian 1882e998cdbeSflorian log_debug("dhcp_hdr op: %s (%d)", dhcp_hdr->op == DHCP_BOOTREQUEST ? 1883e998cdbeSflorian "Boot Request" : dhcp_hdr->op == DHCP_BOOTREPLY ? "Boot Reply" : 1884e998cdbeSflorian "Unknown", dhcp_hdr->op); 1885e998cdbeSflorian log_debug("dhcp_hdr htype: %s (%d)", dhcp_hdr->htype == 1 ? "Ethernet": 1886e998cdbeSflorian "Unknown", dhcp_hdr->htype); 1887e998cdbeSflorian log_debug("dhcp_hdr hlen: %d", dhcp_hdr->hlen); 1888e998cdbeSflorian log_debug("dhcp_hdr hops: %d", dhcp_hdr->hops); 1889c2da98a6Sflorian log_debug("dhcp_hdr xid: 0x%x", ntohl(dhcp_hdr->xid)); 1890e998cdbeSflorian log_debug("dhcp_hdr secs: %u", dhcp_hdr->secs); 1891e998cdbeSflorian log_debug("dhcp_hdr flags: 0x%x", dhcp_hdr->flags); 1892e998cdbeSflorian log_debug("dhcp_hdr ciaddr: %s", inet_ntop(AF_INET, &dhcp_hdr->ciaddr, 1893e998cdbeSflorian hbuf, sizeof(hbuf))); 1894e998cdbeSflorian log_debug("dhcp_hdr yiaddr: %s", inet_ntop(AF_INET, &dhcp_hdr->yiaddr, 1895e998cdbeSflorian hbuf, sizeof(hbuf))); 1896e998cdbeSflorian log_debug("dhcp_hdr siaddr: %s", inet_ntop(AF_INET, &dhcp_hdr->siaddr, 1897e998cdbeSflorian hbuf, sizeof(hbuf))); 1898e998cdbeSflorian log_debug("dhcp_hdr giaddr: %s", inet_ntop(AF_INET, &dhcp_hdr->giaddr, 1899e998cdbeSflorian hbuf, sizeof(hbuf))); 1900e998cdbeSflorian log_debug("dhcp_hdr chaddr: %02x:%02x:%02x:%02x:%02x:%02x " 1901e998cdbeSflorian "(%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x)", 1902e998cdbeSflorian dhcp_hdr->chaddr[0], dhcp_hdr->chaddr[1], dhcp_hdr->chaddr[2], 1903e998cdbeSflorian dhcp_hdr->chaddr[3], dhcp_hdr->chaddr[4], dhcp_hdr->chaddr[5], 1904e998cdbeSflorian dhcp_hdr->chaddr[6], dhcp_hdr->chaddr[7], dhcp_hdr->chaddr[8], 1905e998cdbeSflorian dhcp_hdr->chaddr[9], dhcp_hdr->chaddr[10], dhcp_hdr->chaddr[11], 1906e998cdbeSflorian dhcp_hdr->chaddr[12], dhcp_hdr->chaddr[13], dhcp_hdr->chaddr[14], 1907e998cdbeSflorian dhcp_hdr->chaddr[15]); 1908e998cdbeSflorian /* ignore sname and file, if we ever print it use strvis(3) */ 19094730fc81Sflorian #endif 1910e998cdbeSflorian } 1911e998cdbeSflorian 1912e998cdbeSflorian const char * 1913e998cdbeSflorian dhcp_message_type2str(uint8_t dhcp_message_type) 1914e998cdbeSflorian { 1915e998cdbeSflorian switch (dhcp_message_type) { 1916e998cdbeSflorian case DHCPDISCOVER: 1917e998cdbeSflorian return "DHCPDISCOVER"; 1918e998cdbeSflorian case DHCPOFFER: 1919e998cdbeSflorian return "DHCPOFFER"; 1920e998cdbeSflorian case DHCPREQUEST: 1921e998cdbeSflorian return "DHCPREQUEST"; 1922e998cdbeSflorian case DHCPDECLINE: 1923e998cdbeSflorian return "DHCPDECLINE"; 1924e998cdbeSflorian case DHCPACK: 1925e998cdbeSflorian return "DHCPACK"; 1926e998cdbeSflorian case DHCPNAK: 1927e998cdbeSflorian return "DHCPNAK"; 1928e998cdbeSflorian case DHCPRELEASE: 1929e998cdbeSflorian return "DHCPRELEASE"; 1930e998cdbeSflorian case DHCPINFORM: 1931e998cdbeSflorian return "DHCPINFORM"; 1932e998cdbeSflorian default: 1933e998cdbeSflorian return "Unknown"; 1934e998cdbeSflorian } 1935e998cdbeSflorian } 1936