1*efb7adaaSflorian /* $OpenBSD: engine.c,v 1.31 2024/12/24 17:40:06 florian Exp $ */ 2ad7c548dSflorian 3ad7c548dSflorian /* 4ad7c548dSflorian * Copyright (c) 2017, 2021, 2024 Florian Obser <florian@openbsd.org> 5ad7c548dSflorian * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org> 6ad7c548dSflorian * Copyright (c) 2004 Esben Norby <norby@openbsd.org> 7ad7c548dSflorian * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 8ad7c548dSflorian * 9ad7c548dSflorian * Permission to use, copy, modify, and distribute this software for any 10ad7c548dSflorian * purpose with or without fee is hereby granted, provided that the above 11ad7c548dSflorian * copyright notice and this permission notice appear in all copies. 12ad7c548dSflorian * 13ad7c548dSflorian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14ad7c548dSflorian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15ad7c548dSflorian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16ad7c548dSflorian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17ad7c548dSflorian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18ad7c548dSflorian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19ad7c548dSflorian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20ad7c548dSflorian */ 21ad7c548dSflorian 22ad7c548dSflorian #include <sys/types.h> 23ad7c548dSflorian #include <sys/queue.h> 24ad7c548dSflorian #include <sys/socket.h> 25ad7c548dSflorian #include <sys/syslog.h> 26ad7c548dSflorian #include <sys/uio.h> 27ad7c548dSflorian #include <sys/mbuf.h> 28ad7c548dSflorian 29ad7c548dSflorian #include <net/if.h> 30ad7c548dSflorian #include <net/route.h> 31ad7c548dSflorian #include <arpa/inet.h> 32ad7c548dSflorian #include <netinet/in.h> 33ad7c548dSflorian #include <netinet/ip.h> 34ad7c548dSflorian 35ad7c548dSflorian #include <errno.h> 36ad7c548dSflorian #include <event.h> 37ad7c548dSflorian #include <imsg.h> 38ad7c548dSflorian #include <pwd.h> 39ad7c548dSflorian #include <signal.h> 40ad7c548dSflorian #include <stddef.h> 41ad7c548dSflorian #include <stdio.h> 42ad7c548dSflorian #include <stdlib.h> 43ad7c548dSflorian #include <string.h> 44ad7c548dSflorian #include <time.h> 45ad7c548dSflorian #include <unistd.h> 46ad7c548dSflorian #include <vis.h> 47ad7c548dSflorian 48ad7c548dSflorian #include "log.h" 49ad7c548dSflorian #include "dhcp6leased.h" 50ad7c548dSflorian #include "engine.h" 51ad7c548dSflorian 52ad7c548dSflorian /* 53ad7c548dSflorian * RFC 2131 4.1 p23 has "SHOULD be 4 seconds", we are a bit more aggressive, 54ad7c548dSflorian * networks are faster these days. 55ad7c548dSflorian */ 56ad7c548dSflorian #define START_EXP_BACKOFF 1 57ad7c548dSflorian #define MAX_EXP_BACKOFF_SLOW 64 /* RFC 2131 4.1 p23 */ 58ad7c548dSflorian #define MAX_EXP_BACKOFF_FAST 2 59ad7c548dSflorian #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 60ad7c548dSflorian 61ad7c548dSflorian enum if_state { 62ad7c548dSflorian IF_DOWN, 63ad7c548dSflorian IF_INIT, 64ad7c548dSflorian /* IF_SELECTING, */ 65ad7c548dSflorian IF_REQUESTING, 66ad7c548dSflorian IF_BOUND, 67ad7c548dSflorian IF_RENEWING, 68ad7c548dSflorian IF_REBINDING, 69ad7c548dSflorian /* IF_INIT_REBOOT, */ 70ad7c548dSflorian IF_REBOOTING, 71ad7c548dSflorian }; 72ad7c548dSflorian 73ad7c548dSflorian const char* if_state_name[] = { 74ad7c548dSflorian "Down", 75ad7c548dSflorian "Init", 76ad7c548dSflorian /* "Selecting", */ 77ad7c548dSflorian "Requesting", 78ad7c548dSflorian "Bound", 79ad7c548dSflorian "Renewing", 80ad7c548dSflorian "Rebinding", 81ad7c548dSflorian /* "Init-Reboot", */ 82ad7c548dSflorian "Rebooting", 83ad7c548dSflorian "IPv6 only", 84ad7c548dSflorian }; 85ad7c548dSflorian 8633c99573Sflorian enum reconfigure_action { 8733c99573Sflorian CONFIGURE, 8833c99573Sflorian DECONFIGURE, 8933c99573Sflorian }; 9033c99573Sflorian 9133c99573Sflorian const char* reconfigure_action_name[] = { 9233c99573Sflorian "configure", 9333c99573Sflorian "deconfigure", 9433c99573Sflorian }; 9533c99573Sflorian 96ad7c548dSflorian struct dhcp6leased_iface { 97ad7c548dSflorian LIST_ENTRY(dhcp6leased_iface) entries; 98ad7c548dSflorian enum if_state state; 99ad7c548dSflorian struct event timer; 100ad7c548dSflorian struct timeval timo; 101ad7c548dSflorian uint32_t if_index; 102ad7c548dSflorian int rdomain; 103ad7c548dSflorian int running; 104ad7c548dSflorian int link_state; 105ad7c548dSflorian uint8_t xid[XID_SIZE]; 106ad7c548dSflorian int serverid_len; 107ad7c548dSflorian uint8_t serverid[SERVERID_SIZE]; 108ad7c548dSflorian struct prefix pds[MAX_IA]; 1090da4c31dSflorian struct prefix new_pds[MAX_IA]; 110ad7c548dSflorian struct timespec request_time; 111ad7c548dSflorian struct timespec elapsed_time_start; 112ad7c548dSflorian uint32_t lease_time; 113ad7c548dSflorian uint32_t t1; 114ad7c548dSflorian uint32_t t2; 115ad7c548dSflorian }; 116ad7c548dSflorian 117ad7c548dSflorian LIST_HEAD(, dhcp6leased_iface) dhcp6leased_interfaces; 118ad7c548dSflorian 119ad7c548dSflorian __dead void engine_shutdown(void); 120ad7c548dSflorian void engine_sig_handler(int sig, short, void *); 121ad7c548dSflorian void engine_dispatch_frontend(int, short, void *); 122ad7c548dSflorian void engine_dispatch_main(int, short, void *); 123ad7c548dSflorian void send_interface_info(struct dhcp6leased_iface *, pid_t); 124ad7c548dSflorian void engine_showinfo_ctl(struct imsg *, uint32_t); 125ad7c548dSflorian void engine_update_iface(struct imsg_ifinfo *); 126ad7c548dSflorian struct dhcp6leased_iface *get_dhcp6leased_iface_by_id(uint32_t); 127ad7c548dSflorian void remove_dhcp6leased_iface(uint32_t); 128ad7c548dSflorian void parse_dhcp(struct dhcp6leased_iface *, 129ad7c548dSflorian struct imsg_dhcp *); 13070eb162fSflorian int parse_ia_pd_options(uint8_t *, size_t, struct prefix *); 131ad7c548dSflorian void state_transition(struct dhcp6leased_iface *, enum 132ad7c548dSflorian if_state); 133ad7c548dSflorian void iface_timeout(int, short, void *); 134ad7c548dSflorian void request_dhcp_discover(struct dhcp6leased_iface *); 135ad7c548dSflorian void request_dhcp_request(struct dhcp6leased_iface *); 136ad7c548dSflorian void configure_interfaces(struct dhcp6leased_iface *); 13733c99573Sflorian void deconfigure_interfaces(struct dhcp6leased_iface *); 138*efb7adaaSflorian void deprecate_interfaces(struct dhcp6leased_iface *); 1390da4c31dSflorian int prefixcmp(struct prefix *, struct prefix *, int); 14033c99573Sflorian void send_reconfigure_interface(struct iface_pd_conf *, 14133c99573Sflorian struct prefix *, enum reconfigure_action); 142ad7c548dSflorian int engine_imsg_compose_main(int, pid_t, void *, uint16_t); 1437571100dSflorian const char *dhcp_option_type2str(int); 144ad7c548dSflorian const char *dhcp_duid2str(int, uint8_t *); 1457571100dSflorian const char *dhcp_status2str(int); 146ad7c548dSflorian void in6_prefixlen2mask(struct in6_addr *, int len); 147ad7c548dSflorian 148ad7c548dSflorian struct dhcp6leased_conf *engine_conf; 149ad7c548dSflorian 150ad7c548dSflorian static struct imsgev *iev_frontend; 151ad7c548dSflorian static struct imsgev *iev_main; 152ad7c548dSflorian int64_t proposal_id; 153ad7c548dSflorian static struct dhcp_duid duid; 154ad7c548dSflorian 155ad7c548dSflorian void 156ad7c548dSflorian engine_sig_handler(int sig, short event, void *arg) 157ad7c548dSflorian { 158ad7c548dSflorian /* 159ad7c548dSflorian * Normal signal handler rules don't apply because libevent 160ad7c548dSflorian * decouples for us. 161ad7c548dSflorian */ 162ad7c548dSflorian 163ad7c548dSflorian switch (sig) { 164ad7c548dSflorian case SIGINT: 165ad7c548dSflorian case SIGTERM: 166ad7c548dSflorian engine_shutdown(); 167ad7c548dSflorian default: 168ad7c548dSflorian fatalx("unexpected signal"); 169ad7c548dSflorian } 170ad7c548dSflorian } 171ad7c548dSflorian 172ad7c548dSflorian void 173ad7c548dSflorian engine(int debug, int verbose) 174ad7c548dSflorian { 175ad7c548dSflorian struct event ev_sigint, ev_sigterm; 176ad7c548dSflorian struct passwd *pw; 177ad7c548dSflorian 178ad7c548dSflorian engine_conf = config_new_empty(); 179ad7c548dSflorian 180ad7c548dSflorian log_init(debug, LOG_DAEMON); 181ad7c548dSflorian log_setverbose(verbose); 182ad7c548dSflorian 183ad7c548dSflorian if ((pw = getpwnam(DHCP6LEASED_USER)) == NULL) 184ad7c548dSflorian fatal("getpwnam"); 185ad7c548dSflorian 186ad7c548dSflorian if (chdir("/") == -1) 187ad7c548dSflorian fatal("chdir(\"/\")"); 188ad7c548dSflorian 189ad7c548dSflorian if (unveil("/", "") == -1) 190ad7c548dSflorian fatal("unveil /"); 191ad7c548dSflorian if (unveil(NULL, NULL) == -1) 192ad7c548dSflorian fatal("unveil"); 193ad7c548dSflorian 194ad7c548dSflorian setproctitle("%s", "engine"); 195ad7c548dSflorian log_procinit("engine"); 196ad7c548dSflorian 197ad7c548dSflorian if (setgroups(1, &pw->pw_gid) || 198ad7c548dSflorian setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 199ad7c548dSflorian setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 200ad7c548dSflorian fatal("can't drop privileges"); 201ad7c548dSflorian 202ad7c548dSflorian if (pledge("stdio recvfd", NULL) == -1) 203ad7c548dSflorian fatal("pledge"); 204ad7c548dSflorian 205ad7c548dSflorian event_init(); 206ad7c548dSflorian 207ad7c548dSflorian /* Setup signal handler(s). */ 208ad7c548dSflorian signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL); 209ad7c548dSflorian signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL); 210ad7c548dSflorian signal_add(&ev_sigint, NULL); 211ad7c548dSflorian signal_add(&ev_sigterm, NULL); 212ad7c548dSflorian signal(SIGPIPE, SIG_IGN); 213ad7c548dSflorian signal(SIGHUP, SIG_IGN); 214ad7c548dSflorian 215ad7c548dSflorian /* Setup pipe and event handler to the main process. */ 216ad7c548dSflorian if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) 217ad7c548dSflorian fatal(NULL); 218ad7c548dSflorian 2190e59d0d1Sclaudio if (imsgbuf_init(&iev_main->ibuf, 3) == -1) 2200e59d0d1Sclaudio fatal(NULL); 2210e59d0d1Sclaudio imsgbuf_allow_fdpass(&iev_main->ibuf); 222ad7c548dSflorian iev_main->handler = engine_dispatch_main; 223ad7c548dSflorian 224ad7c548dSflorian /* Setup event handlers. */ 225ad7c548dSflorian iev_main->events = EV_READ; 226ad7c548dSflorian event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, 227ad7c548dSflorian iev_main->handler, iev_main); 228ad7c548dSflorian event_add(&iev_main->ev, NULL); 229ad7c548dSflorian 230ad7c548dSflorian LIST_INIT(&dhcp6leased_interfaces); 231ad7c548dSflorian 232ad7c548dSflorian event_dispatch(); 233ad7c548dSflorian 234ad7c548dSflorian engine_shutdown(); 235ad7c548dSflorian } 236ad7c548dSflorian 237ad7c548dSflorian __dead void 238ad7c548dSflorian engine_shutdown(void) 239ad7c548dSflorian { 240ad7c548dSflorian /* Close pipes. */ 2419cbf9e90Sclaudio imsgbuf_clear(&iev_frontend->ibuf); 242ad7c548dSflorian close(iev_frontend->ibuf.fd); 2439cbf9e90Sclaudio imsgbuf_clear(&iev_main->ibuf); 244ad7c548dSflorian close(iev_main->ibuf.fd); 245ad7c548dSflorian 246ad7c548dSflorian free(iev_frontend); 247ad7c548dSflorian free(iev_main); 248ad7c548dSflorian 249ad7c548dSflorian log_info("engine exiting"); 250ad7c548dSflorian exit(0); 251ad7c548dSflorian } 252ad7c548dSflorian 253ad7c548dSflorian int 254ad7c548dSflorian engine_imsg_compose_frontend(int type, pid_t pid, void *data, 255ad7c548dSflorian uint16_t datalen) 256ad7c548dSflorian { 257ad7c548dSflorian return (imsg_compose_event(iev_frontend, type, 0, pid, -1, 258ad7c548dSflorian data, datalen)); 259ad7c548dSflorian } 260ad7c548dSflorian 261ad7c548dSflorian int 262ad7c548dSflorian engine_imsg_compose_main(int type, pid_t pid, void *data, 263ad7c548dSflorian uint16_t datalen) 264ad7c548dSflorian { 265ad7c548dSflorian return (imsg_compose_event(iev_main, type, 0, pid, -1, 266ad7c548dSflorian data, datalen)); 267ad7c548dSflorian } 268ad7c548dSflorian 269ad7c548dSflorian void 270ad7c548dSflorian engine_dispatch_frontend(int fd, short event, void *bula) 271ad7c548dSflorian { 272ad7c548dSflorian struct imsgev *iev = bula; 273ad7c548dSflorian struct imsgbuf *ibuf = &iev->ibuf; 274ad7c548dSflorian struct imsg imsg; 275ad7c548dSflorian struct dhcp6leased_iface *iface; 276ad7c548dSflorian ssize_t n; 277ad7c548dSflorian int shut = 0; 278ad7c548dSflorian int verbose; 279ad7c548dSflorian uint32_t if_index; 280ad7c548dSflorian 281ad7c548dSflorian if (event & EV_READ) { 282668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 283dd7efffeSclaudio fatal("imsgbuf_read error"); 284ad7c548dSflorian if (n == 0) /* Connection closed. */ 285ad7c548dSflorian shut = 1; 286ad7c548dSflorian } 287ad7c548dSflorian if (event & EV_WRITE) { 288dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 289e3b6409cSclaudio if (errno == EPIPE) /* Connection closed. */ 290ad7c548dSflorian shut = 1; 291e3b6409cSclaudio else 292dd7efffeSclaudio fatal("imsgbuf_write"); 293e3b6409cSclaudio } 294ad7c548dSflorian } 295ad7c548dSflorian 296ad7c548dSflorian for (;;) { 297ad7c548dSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 298ad7c548dSflorian fatal("%s: imsg_get error", __func__); 299ad7c548dSflorian if (n == 0) /* No more messages. */ 300ad7c548dSflorian break; 301ad7c548dSflorian 302ad7c548dSflorian switch (imsg.hdr.type) { 303ad7c548dSflorian case IMSG_CTL_LOG_VERBOSE: 304ad7c548dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(verbose)) 305ad7c548dSflorian fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: " 306ad7c548dSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 307ad7c548dSflorian memcpy(&verbose, imsg.data, sizeof(verbose)); 308ad7c548dSflorian log_setverbose(verbose); 309ad7c548dSflorian break; 310ad7c548dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO: 311ad7c548dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 312ad7c548dSflorian fatalx("%s: IMSG_CTL_SHOW_INTERFACE_INFO wrong " 313ad7c548dSflorian "length: %lu", __func__, 314ad7c548dSflorian IMSG_DATA_SIZE(imsg)); 315ad7c548dSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 316ad7c548dSflorian engine_showinfo_ctl(&imsg, if_index); 317ad7c548dSflorian break; 318ad7c548dSflorian case IMSG_REQUEST_REBOOT: 319ad7c548dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 320ad7c548dSflorian fatalx("%s: IMSG_CTL_SEND_DISCOVER wrong " 321ad7c548dSflorian "length: %lu", __func__, 322ad7c548dSflorian IMSG_DATA_SIZE(imsg)); 323ad7c548dSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 324ad7c548dSflorian iface = get_dhcp6leased_iface_by_id(if_index); 325ad7c548dSflorian if (iface != NULL) { 326ad7c548dSflorian switch (iface->state) { 327ad7c548dSflorian case IF_DOWN: 328ad7c548dSflorian break; 329ad7c548dSflorian case IF_INIT: 330ad7c548dSflorian case IF_REQUESTING: 331ad7c548dSflorian state_transition(iface, iface->state); 332ad7c548dSflorian break; 333ad7c548dSflorian case IF_RENEWING: 334ad7c548dSflorian case IF_REBINDING: 335ad7c548dSflorian case IF_REBOOTING: 336ad7c548dSflorian case IF_BOUND: 337ad7c548dSflorian state_transition(iface, IF_REBOOTING); 338ad7c548dSflorian break; 339ad7c548dSflorian } 340ad7c548dSflorian } 341ad7c548dSflorian break; 342ad7c548dSflorian case IMSG_REMOVE_IF: 343ad7c548dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 344ad7c548dSflorian fatalx("%s: IMSG_REMOVE_IF wrong length: %lu", 345ad7c548dSflorian __func__, IMSG_DATA_SIZE(imsg)); 346ad7c548dSflorian memcpy(&if_index, imsg.data, sizeof(if_index)); 347ad7c548dSflorian remove_dhcp6leased_iface(if_index); 348ad7c548dSflorian break; 349ad7c548dSflorian case IMSG_DHCP: { 350ad7c548dSflorian struct imsg_dhcp imsg_dhcp; 351ad7c548dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_dhcp)) 352ad7c548dSflorian fatalx("%s: IMSG_DHCP wrong length: %lu", 353ad7c548dSflorian __func__, IMSG_DATA_SIZE(imsg)); 354ad7c548dSflorian memcpy(&imsg_dhcp, imsg.data, sizeof(imsg_dhcp)); 355ad7c548dSflorian iface = get_dhcp6leased_iface_by_id(imsg_dhcp.if_index); 356ad7c548dSflorian if (iface != NULL) 357ad7c548dSflorian parse_dhcp(iface, &imsg_dhcp); 358ad7c548dSflorian break; 359ad7c548dSflorian } 360ad7c548dSflorian default: 361ad7c548dSflorian log_debug("%s: unexpected imsg %d", __func__, 362ad7c548dSflorian imsg.hdr.type); 363ad7c548dSflorian break; 364ad7c548dSflorian } 365ad7c548dSflorian imsg_free(&imsg); 366ad7c548dSflorian } 367ad7c548dSflorian if (!shut) 368ad7c548dSflorian imsg_event_add(iev); 369ad7c548dSflorian else { 370ad7c548dSflorian /* This pipe is dead. Remove its event handler. */ 371ad7c548dSflorian event_del(&iev->ev); 372ad7c548dSflorian event_loopexit(NULL); 373ad7c548dSflorian } 374ad7c548dSflorian } 375ad7c548dSflorian 376ad7c548dSflorian void 377ad7c548dSflorian engine_dispatch_main(int fd, short event, void *bula) 378ad7c548dSflorian { 379ad7c548dSflorian static struct dhcp6leased_conf *nconf; 380ad7c548dSflorian static struct iface_conf *iface_conf; 381ad7c548dSflorian static struct iface_ia_conf *iface_ia_conf; 382ad7c548dSflorian struct iface_pd_conf *iface_pd_conf; 383ad7c548dSflorian struct imsg imsg; 384ad7c548dSflorian struct imsgev *iev = bula; 385ad7c548dSflorian struct imsgbuf *ibuf = &iev->ibuf; 386ad7c548dSflorian struct imsg_ifinfo imsg_ifinfo; 387ad7c548dSflorian ssize_t n; 388ad7c548dSflorian int shut = 0; 389ad7c548dSflorian 390ad7c548dSflorian if (event & EV_READ) { 391668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 392dd7efffeSclaudio fatal("imsgbuf_read error"); 393ad7c548dSflorian if (n == 0) /* Connection closed. */ 394ad7c548dSflorian shut = 1; 395ad7c548dSflorian } 396ad7c548dSflorian if (event & EV_WRITE) { 397dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 398e3b6409cSclaudio if (errno == EPIPE) /* Connection closed. */ 399ad7c548dSflorian shut = 1; 400e3b6409cSclaudio else 401dd7efffeSclaudio fatal("imsgbuf_write"); 402e3b6409cSclaudio } 403ad7c548dSflorian } 404ad7c548dSflorian 405ad7c548dSflorian for (;;) { 406ad7c548dSflorian if ((n = imsg_get(ibuf, &imsg)) == -1) 407ad7c548dSflorian fatal("%s: imsg_get error", __func__); 408ad7c548dSflorian if (n == 0) /* No more messages. */ 409ad7c548dSflorian break; 410ad7c548dSflorian 411ad7c548dSflorian switch (imsg.hdr.type) { 412ad7c548dSflorian case IMSG_SOCKET_IPC: 413ad7c548dSflorian /* 414ad7c548dSflorian * Setup pipe and event handler to the frontend 415ad7c548dSflorian * process. 416ad7c548dSflorian */ 417ad7c548dSflorian if (iev_frontend) 418ad7c548dSflorian fatalx("%s: received unexpected imsg fd " 419ad7c548dSflorian "to engine", __func__); 420ad7c548dSflorian 421ad7c548dSflorian if ((fd = imsg_get_fd(&imsg)) == -1) 422ad7c548dSflorian fatalx("%s: expected to receive imsg fd to " 423ad7c548dSflorian "engine but didn't receive any", __func__); 424ad7c548dSflorian 425ad7c548dSflorian iev_frontend = malloc(sizeof(struct imsgev)); 426ad7c548dSflorian if (iev_frontend == NULL) 427ad7c548dSflorian fatal(NULL); 428ad7c548dSflorian 4290e59d0d1Sclaudio if (imsgbuf_init(&iev_frontend->ibuf, fd) == -1) 4300e59d0d1Sclaudio fatal(NULL); 431ad7c548dSflorian iev_frontend->handler = engine_dispatch_frontend; 432ad7c548dSflorian iev_frontend->events = EV_READ; 433ad7c548dSflorian 434ad7c548dSflorian event_set(&iev_frontend->ev, iev_frontend->ibuf.fd, 435ad7c548dSflorian iev_frontend->events, iev_frontend->handler, 436ad7c548dSflorian iev_frontend); 437ad7c548dSflorian event_add(&iev_frontend->ev, NULL); 438ad7c548dSflorian 439ad7c548dSflorian if (pledge("stdio", NULL) == -1) 440ad7c548dSflorian fatal("pledge"); 441ad7c548dSflorian 442ad7c548dSflorian break; 443ad7c548dSflorian case IMSG_UUID: 444ad7c548dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(duid.uuid)) 445ad7c548dSflorian fatalx("%s: IMSG_UUID wrong length: " 446ad7c548dSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 447ad7c548dSflorian duid.type = htons(DUID_UUID_TYPE); 448ad7c548dSflorian memcpy(duid.uuid, imsg.data, sizeof(duid.uuid)); 449ad7c548dSflorian break; 450ad7c548dSflorian case IMSG_UPDATE_IF: 451ad7c548dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_ifinfo)) 452ad7c548dSflorian fatalx("%s: IMSG_UPDATE_IF wrong length: %lu", 453ad7c548dSflorian __func__, IMSG_DATA_SIZE(imsg)); 454ad7c548dSflorian memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo)); 455ad7c548dSflorian engine_update_iface(&imsg_ifinfo); 456ad7c548dSflorian break; 457ad7c548dSflorian case IMSG_RECONF_CONF: 458ad7c548dSflorian if (nconf != NULL) 459ad7c548dSflorian fatalx("%s: IMSG_RECONF_CONF already in " 460ad7c548dSflorian "progress", __func__); 4613ae4b9dfSflorian if (IMSG_DATA_SIZE(imsg) != 4623ae4b9dfSflorian sizeof(struct dhcp6leased_conf)) 4633ae4b9dfSflorian fatalx("%s: IMSG_RECONF_CONF wrong length: %lu", 4643ae4b9dfSflorian __func__, IMSG_DATA_SIZE(imsg)); 465ad7c548dSflorian if ((nconf = malloc(sizeof(struct dhcp6leased_conf))) == 466ad7c548dSflorian NULL) 467ad7c548dSflorian fatal(NULL); 4683ae4b9dfSflorian memcpy(nconf, imsg.data, 4693ae4b9dfSflorian sizeof(struct dhcp6leased_conf)); 470ad7c548dSflorian SIMPLEQ_INIT(&nconf->iface_list); 471ad7c548dSflorian break; 472ad7c548dSflorian case IMSG_RECONF_IFACE: 473ad7c548dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(struct 474ad7c548dSflorian iface_conf)) 475ad7c548dSflorian fatalx("%s: IMSG_RECONF_IFACE wrong length: " 476ad7c548dSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 477ad7c548dSflorian if ((iface_conf = malloc(sizeof(struct iface_conf))) 478ad7c548dSflorian == NULL) 479ad7c548dSflorian fatal(NULL); 480ad7c548dSflorian memcpy(iface_conf, imsg.data, sizeof(struct 481ad7c548dSflorian iface_conf)); 482ad7c548dSflorian SIMPLEQ_INIT(&iface_conf->iface_ia_list); 483ad7c548dSflorian SIMPLEQ_INSERT_TAIL(&nconf->iface_list, 484ad7c548dSflorian iface_conf, entry); 485ad7c548dSflorian iface_conf->ia_count = 0; 486ad7c548dSflorian break; 487ad7c548dSflorian case IMSG_RECONF_IFACE_IA: 488ad7c548dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(struct 489ad7c548dSflorian iface_ia_conf)) 490ad7c548dSflorian fatalx("%s: IMSG_RECONF_IFACE_IA wrong " 491ad7c548dSflorian "length: %lu", __func__, 492ad7c548dSflorian IMSG_DATA_SIZE(imsg)); 493ad7c548dSflorian if ((iface_ia_conf = 494ad7c548dSflorian malloc(sizeof(struct iface_ia_conf))) == NULL) 495ad7c548dSflorian fatal(NULL); 496ad7c548dSflorian memcpy(iface_ia_conf, imsg.data, sizeof(struct 497ad7c548dSflorian iface_ia_conf)); 498ad7c548dSflorian SIMPLEQ_INIT(&iface_ia_conf->iface_pd_list); 499ad7c548dSflorian SIMPLEQ_INSERT_TAIL(&iface_conf->iface_ia_list, 500ad7c548dSflorian iface_ia_conf, entry); 50192119d76Sflorian iface_ia_conf->id = iface_conf->ia_count++; 502ad7c548dSflorian if (iface_conf->ia_count > MAX_IA) 503ad7c548dSflorian fatalx("Too many prefix delegation requests."); 504ad7c548dSflorian break; 505ad7c548dSflorian case IMSG_RECONF_IFACE_PD: 506ad7c548dSflorian if (IMSG_DATA_SIZE(imsg) != sizeof(struct 507ad7c548dSflorian iface_pd_conf)) 508ad7c548dSflorian fatalx("%s: IMSG_RECONF_IFACE_PD wrong length: " 509ad7c548dSflorian "%lu", __func__, IMSG_DATA_SIZE(imsg)); 510ad7c548dSflorian if ((iface_pd_conf = 511ad7c548dSflorian malloc(sizeof(struct iface_pd_conf))) == NULL) 512ad7c548dSflorian fatal(NULL); 513ad7c548dSflorian memcpy(iface_pd_conf, imsg.data, sizeof(struct 514ad7c548dSflorian iface_pd_conf)); 515ad7c548dSflorian SIMPLEQ_INSERT_TAIL(&iface_ia_conf->iface_pd_list, 516ad7c548dSflorian iface_pd_conf, entry); 517ad7c548dSflorian break; 518ad7c548dSflorian case IMSG_RECONF_IFACE_IA_END: 519ad7c548dSflorian iface_ia_conf = NULL; 520ad7c548dSflorian break; 521ad7c548dSflorian case IMSG_RECONF_IFACE_END: 522ad7c548dSflorian iface_conf = NULL; 523ad7c548dSflorian break; 524ad7c548dSflorian case IMSG_RECONF_END: { 525ad7c548dSflorian struct dhcp6leased_iface *iface; 526ad7c548dSflorian int *ifaces; 527ad7c548dSflorian int i, if_index; 528ad7c548dSflorian char *if_name; 529ad7c548dSflorian char ifnamebuf[IF_NAMESIZE]; 530ad7c548dSflorian 531ad7c548dSflorian if (nconf == NULL) 532ad7c548dSflorian fatalx("%s: IMSG_RECONF_END without " 533ad7c548dSflorian "IMSG_RECONF_CONF", __func__); 534ad7c548dSflorian ifaces = changed_ifaces(engine_conf, nconf); 535ad7c548dSflorian merge_config(engine_conf, nconf); 536ad7c548dSflorian nconf = NULL; 537ad7c548dSflorian for (i = 0; ifaces[i] != 0; i++) { 538ad7c548dSflorian if_index = ifaces[i]; 539ad7c548dSflorian if_name = if_indextoname(if_index, ifnamebuf); 540ad7c548dSflorian iface = get_dhcp6leased_iface_by_id(if_index); 541ad7c548dSflorian if (if_name == NULL || iface == NULL) 542ad7c548dSflorian continue; 543ad7c548dSflorian iface_conf = find_iface_conf( 544ad7c548dSflorian &engine_conf->iface_list, if_name); 545ad7c548dSflorian if (iface_conf == NULL) 546ad7c548dSflorian continue; 547ad7c548dSflorian } 548ad7c548dSflorian free(ifaces); 549ad7c548dSflorian break; 550ad7c548dSflorian } 551ad7c548dSflorian default: 552ad7c548dSflorian log_debug("%s: unexpected imsg %d", __func__, 553ad7c548dSflorian imsg.hdr.type); 554ad7c548dSflorian break; 555ad7c548dSflorian } 556ad7c548dSflorian imsg_free(&imsg); 557ad7c548dSflorian } 558ad7c548dSflorian if (!shut) 559ad7c548dSflorian imsg_event_add(iev); 560ad7c548dSflorian else { 561ad7c548dSflorian /* This pipe is dead. Remove its event handler. */ 562ad7c548dSflorian event_del(&iev->ev); 563ad7c548dSflorian event_loopexit(NULL); 564ad7c548dSflorian } 565ad7c548dSflorian } 566ad7c548dSflorian 567ad7c548dSflorian void 568ad7c548dSflorian send_interface_info(struct dhcp6leased_iface *iface, pid_t pid) 569ad7c548dSflorian { 570ad7c548dSflorian struct ctl_engine_info cei; 571ad7c548dSflorian 572ad7c548dSflorian memset(&cei, 0, sizeof(cei)); 573ad7c548dSflorian cei.if_index = iface->if_index; 574ad7c548dSflorian cei.running = iface->running; 575ad7c548dSflorian cei.link_state = iface->link_state; 576ad7c548dSflorian strlcpy(cei.state, if_state_name[iface->state], sizeof(cei.state)); 577ad7c548dSflorian memcpy(&cei.request_time, &iface->request_time, 578ad7c548dSflorian sizeof(cei.request_time)); 579ad7c548dSflorian cei.lease_time = iface->lease_time; 580ad7c548dSflorian cei.t1 = iface->t1; 581ad7c548dSflorian cei.t2 = iface->t2; 582f35103ceSflorian memcpy(&cei.pds, &iface->pds, sizeof(cei.pds)); 583ad7c548dSflorian engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei, 584ad7c548dSflorian sizeof(cei)); 585ad7c548dSflorian } 586ad7c548dSflorian 587ad7c548dSflorian void 588ad7c548dSflorian engine_showinfo_ctl(struct imsg *imsg, uint32_t if_index) 589ad7c548dSflorian { 590ad7c548dSflorian struct dhcp6leased_iface *iface; 591ad7c548dSflorian 592ad7c548dSflorian switch (imsg->hdr.type) { 593ad7c548dSflorian case IMSG_CTL_SHOW_INTERFACE_INFO: 594ad7c548dSflorian if ((iface = get_dhcp6leased_iface_by_id(if_index)) != NULL) 595ad7c548dSflorian send_interface_info(iface, imsg->hdr.pid); 596ad7c548dSflorian else 597ad7c548dSflorian engine_imsg_compose_frontend(IMSG_CTL_END, 598ad7c548dSflorian imsg->hdr.pid, NULL, 0); 599ad7c548dSflorian break; 600ad7c548dSflorian default: 601ad7c548dSflorian log_debug("%s: error handling imsg", __func__); 602ad7c548dSflorian break; 603ad7c548dSflorian } 604ad7c548dSflorian } 605ad7c548dSflorian 606ad7c548dSflorian void 607ad7c548dSflorian engine_update_iface(struct imsg_ifinfo *imsg_ifinfo) 608ad7c548dSflorian { 609ad7c548dSflorian struct dhcp6leased_iface *iface; 610763cddbdSflorian struct iface_conf *iface_conf; 611ad7c548dSflorian int need_refresh = 0; 612763cddbdSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 613ad7c548dSflorian 614ad7c548dSflorian iface = get_dhcp6leased_iface_by_id(imsg_ifinfo->if_index); 615ad7c548dSflorian 616ad7c548dSflorian if (iface == NULL) { 617ad7c548dSflorian if ((iface = calloc(1, sizeof(*iface))) == NULL) 618ad7c548dSflorian fatal("calloc"); 619ad7c548dSflorian iface->state = IF_DOWN; 620ad7c548dSflorian arc4random_buf(iface->xid, sizeof(iface->xid)); 621ad7c548dSflorian iface->timo.tv_usec = arc4random_uniform(1000000); 622ad7c548dSflorian evtimer_set(&iface->timer, iface_timeout, iface); 623ad7c548dSflorian iface->if_index = imsg_ifinfo->if_index; 624ad7c548dSflorian iface->rdomain = imsg_ifinfo->rdomain; 625ad7c548dSflorian iface->running = imsg_ifinfo->running; 626ad7c548dSflorian iface->link_state = imsg_ifinfo->link_state; 627ad7c548dSflorian LIST_INSERT_HEAD(&dhcp6leased_interfaces, iface, entries); 628ad7c548dSflorian need_refresh = 1; 629ad7c548dSflorian } else { 630ad7c548dSflorian if (imsg_ifinfo->rdomain != iface->rdomain) { 631ad7c548dSflorian iface->rdomain = imsg_ifinfo->rdomain; 632ad7c548dSflorian need_refresh = 1; 633ad7c548dSflorian } 634ad7c548dSflorian if (imsg_ifinfo->running != iface->running) { 635ad7c548dSflorian iface->running = imsg_ifinfo->running; 636ad7c548dSflorian need_refresh = 1; 637ad7c548dSflorian } 638ad7c548dSflorian 639ad7c548dSflorian if (imsg_ifinfo->link_state != iface->link_state) { 640ad7c548dSflorian iface->link_state = imsg_ifinfo->link_state; 641ad7c548dSflorian need_refresh = 1; 642ad7c548dSflorian } 643ad7c548dSflorian } 644ad7c548dSflorian 645ad7c548dSflorian if (!need_refresh) 646ad7c548dSflorian return; 647ad7c548dSflorian 648763cddbdSflorian if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) { 649763cddbdSflorian log_debug("%s: unknown interface %d", __func__, 650763cddbdSflorian iface->if_index); 651763cddbdSflorian return; 652763cddbdSflorian } 653ad7c548dSflorian 654763cddbdSflorian if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name)) 655763cddbdSflorian == NULL) { 656763cddbdSflorian log_debug("%s: no interface configuration for %d", __func__, 657763cddbdSflorian iface->if_index); 658763cddbdSflorian return; 659763cddbdSflorian } 660763cddbdSflorian 661763cddbdSflorian if (iface->running && LINK_STATE_IS_UP(iface->link_state)) { 662763cddbdSflorian uint32_t i; 663763cddbdSflorian int got_lease; 664763cddbdSflorian 665763cddbdSflorian if (iface->pds[0].prefix_len == 0) 666763cddbdSflorian memcpy(iface->pds, imsg_ifinfo->pds, 667763cddbdSflorian sizeof(iface->pds)); 668763cddbdSflorian 669763cddbdSflorian got_lease = 0; 670763cddbdSflorian for (i = 0; i < iface_conf->ia_count; i++) { 671763cddbdSflorian if (iface->pds[i].prefix_len > 0) { 672763cddbdSflorian got_lease = 1; 673763cddbdSflorian break; 674763cddbdSflorian } 675763cddbdSflorian } 676763cddbdSflorian if (got_lease) 677ad7c548dSflorian state_transition(iface, IF_REBOOTING); 678763cddbdSflorian else 679ad7c548dSflorian state_transition(iface, IF_INIT); 680ad7c548dSflorian } else 681ad7c548dSflorian state_transition(iface, IF_DOWN); 682ad7c548dSflorian } 683ad7c548dSflorian struct dhcp6leased_iface* 684ad7c548dSflorian get_dhcp6leased_iface_by_id(uint32_t if_index) 685ad7c548dSflorian { 686ad7c548dSflorian struct dhcp6leased_iface *iface; 687ad7c548dSflorian LIST_FOREACH (iface, &dhcp6leased_interfaces, entries) { 688ad7c548dSflorian if (iface->if_index == if_index) 689ad7c548dSflorian return (iface); 690ad7c548dSflorian } 691ad7c548dSflorian 692ad7c548dSflorian return (NULL); 693ad7c548dSflorian } 694ad7c548dSflorian 695ad7c548dSflorian void 696ad7c548dSflorian remove_dhcp6leased_iface(uint32_t if_index) 697ad7c548dSflorian { 698ad7c548dSflorian struct dhcp6leased_iface *iface; 699ad7c548dSflorian 700ad7c548dSflorian iface = get_dhcp6leased_iface_by_id(if_index); 701ad7c548dSflorian 702ad7c548dSflorian if (iface == NULL) 703ad7c548dSflorian return; 704ad7c548dSflorian 70533c99573Sflorian deconfigure_interfaces(iface); 706ad7c548dSflorian LIST_REMOVE(iface, entries); 707ad7c548dSflorian evtimer_del(&iface->timer); 708ad7c548dSflorian free(iface); 709ad7c548dSflorian } 710ad7c548dSflorian 711ad7c548dSflorian void 712ad7c548dSflorian parse_dhcp(struct dhcp6leased_iface *iface, struct imsg_dhcp *dhcp) 713ad7c548dSflorian { 714ad7c548dSflorian struct iface_conf *iface_conf; 715ad7c548dSflorian struct iface_ia_conf *ia_conf; 716ad7c548dSflorian struct dhcp_hdr hdr; 717ad7c548dSflorian struct dhcp_option_hdr opt_hdr; 718ad7c548dSflorian struct dhcp_iapd iapd; 719ad7c548dSflorian size_t rem; 7201c8c2e64Sflorian uint32_t t1, t2, lease_time; 7213ae4b9dfSflorian int serverid_len, rapid_commit = 0; 722ad7c548dSflorian uint8_t serverid[SERVERID_SIZE]; 723ad7c548dSflorian uint8_t *p; 724ad7c548dSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 725ad7c548dSflorian char ntopbuf[INET6_ADDRSTRLEN]; 726ad7c548dSflorian 727ad7c548dSflorian if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) { 728ad7c548dSflorian log_debug("%s: unknown interface %d", __func__, 729ad7c548dSflorian iface->if_index); 730ad7c548dSflorian goto out; 731ad7c548dSflorian } 732ad7c548dSflorian if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name)) 733ad7c548dSflorian == NULL) { 734ad7c548dSflorian log_debug("%s: no interface configuration for %d", __func__, 735ad7c548dSflorian iface->if_index); 736ad7c548dSflorian goto out; 737ad7c548dSflorian } 738ad7c548dSflorian 739ad7c548dSflorian log_debug("%s: %s ia_count: %d", __func__, if_name, 740ad7c548dSflorian iface_conf->ia_count); 741ad7c548dSflorian 7421c8c2e64Sflorian serverid_len = t1 = t2 = lease_time = 0; 7430da4c31dSflorian memset(iface->new_pds, 0, sizeof(iface->new_pds)); 744ad7c548dSflorian 745ad7c548dSflorian p = dhcp->packet; 746ad7c548dSflorian rem = dhcp->len; 747ad7c548dSflorian 748ad7c548dSflorian if (rem < sizeof(struct dhcp_hdr)) { 749ad7c548dSflorian log_warnx("%s: message too short", __func__); 750ad7c548dSflorian goto out; 751ad7c548dSflorian } 752ad7c548dSflorian memcpy(&hdr, p, sizeof(struct dhcp_hdr)); 753ad7c548dSflorian p += sizeof(struct dhcp_hdr); 754ad7c548dSflorian rem -= sizeof(struct dhcp_hdr); 755ad7c548dSflorian 756ad7c548dSflorian if (log_getverbose() > 1) 757ad7c548dSflorian log_debug("%s: %s, xid: 0x%02x%02x%02x", __func__, 758ad7c548dSflorian dhcp_message_type2str(hdr.msg_type), hdr.xid[0], hdr.xid[1], 759ad7c548dSflorian hdr.xid[2]); 760ad7c548dSflorian 761ad7c548dSflorian while (rem >= sizeof(struct dhcp_option_hdr)) { 762ad7c548dSflorian memcpy(&opt_hdr, p, sizeof(struct dhcp_option_hdr)); 763ad7c548dSflorian opt_hdr.code = ntohs(opt_hdr.code); 764ad7c548dSflorian opt_hdr.len = ntohs(opt_hdr.len); 765ad7c548dSflorian p += sizeof(struct dhcp_option_hdr); 766ad7c548dSflorian rem -= sizeof(struct dhcp_option_hdr); 767ad7c548dSflorian if (log_getverbose() > 1) 768ad7c548dSflorian log_debug("%s: %s, len: %u", __func__, 769ad7c548dSflorian dhcp_option_type2str(opt_hdr.code), opt_hdr.len); 770ad7c548dSflorian 771ad7c548dSflorian if (rem < opt_hdr.len) { 772ad7c548dSflorian log_warnx("%s: malformed packet, ignoring", __func__); 773ad7c548dSflorian goto out; 774ad7c548dSflorian } 775ad7c548dSflorian 776ad7c548dSflorian switch (opt_hdr.code) { 777ad7c548dSflorian case DHO_CLIENTID: 778ad7c548dSflorian if (opt_hdr.len != sizeof(struct dhcp_duid) || 779ad7c548dSflorian memcmp(&duid, p, sizeof(struct dhcp_duid)) != 0) { 780ad7c548dSflorian log_debug("%s: message not for us", __func__); 781ad7c548dSflorian goto out; 782ad7c548dSflorian } 783ad7c548dSflorian break; 784ad7c548dSflorian case DHO_SERVERID: 785ad7c548dSflorian /* 786ad7c548dSflorian * RFC 8415, 11.1: 787ad7c548dSflorian * The length of the DUID (not including the type code) 788ad7c548dSflorian * is at least 1 octet and at most 128 octets. 789ad7c548dSflorian */ 790ad7c548dSflorian if (opt_hdr.len < 2 + 1) { 791ad7c548dSflorian log_warnx("%s: SERVERID too short", __func__); 792ad7c548dSflorian goto out; 793ad7c548dSflorian } 794ad7c548dSflorian if (opt_hdr.len > SERVERID_SIZE) { 795ad7c548dSflorian log_warnx("%s: SERVERID too long", __func__); 796ad7c548dSflorian goto out; 797ad7c548dSflorian } 798ad7c548dSflorian log_debug("%s: SERVERID: %s", __func__, 799ad7c548dSflorian dhcp_duid2str(opt_hdr.len, p)); 800ad7c548dSflorian if (serverid_len != 0) { 801ad7c548dSflorian log_warnx("%s: duplicate SERVERID option", 802ad7c548dSflorian __func__); 803ad7c548dSflorian goto out; 804ad7c548dSflorian } 805ad7c548dSflorian serverid_len = opt_hdr.len; 806ad7c548dSflorian memcpy(serverid, p, serverid_len); 807ad7c548dSflorian break; 808ad7c548dSflorian case DHO_IA_PD: 809ad7c548dSflorian if (opt_hdr.len < sizeof(struct dhcp_iapd)) { 810ad7c548dSflorian log_warnx("%s: IA_PD too short", __func__); 811ad7c548dSflorian goto out; 812ad7c548dSflorian } 813ad7c548dSflorian memcpy(&iapd, p, sizeof(struct dhcp_iapd)); 814ad7c548dSflorian 815ad7c548dSflorian if (t1 == 0 || t1 > ntohl(iapd.t1)) 816ad7c548dSflorian t1 = ntohl(iapd.t1); 817ad7c548dSflorian if (t2 == 0 || t2 > ntohl(iapd.t2)) 818ad7c548dSflorian t2 = ntohl(iapd.t2); 819ad7c548dSflorian 820ad7c548dSflorian log_debug("%s: IA_PD, IAID: %08x, T1: %u, T2: %u", 821ad7c548dSflorian __func__, ntohl(iapd.iaid), ntohl(iapd.t1), 822ad7c548dSflorian ntohl(iapd.t2)); 82370eb162fSflorian if (ntohl(iapd.iaid) < iface_conf->ia_count) { 82470eb162fSflorian int status_code; 82570eb162fSflorian status_code = parse_ia_pd_options(p + 826ad7c548dSflorian sizeof(struct dhcp_iapd), opt_hdr.len - 827ad7c548dSflorian sizeof(struct dhcp_iapd), 8280da4c31dSflorian &iface->new_pds[ntohl(iapd.iaid)]); 82970eb162fSflorian 83070eb162fSflorian if (status_code != DHCP_STATUS_SUCCESS && 83170eb162fSflorian iface->state == IF_RENEWING) { 83270eb162fSflorian state_transition(iface, IF_REBINDING); 83370eb162fSflorian goto out; 83470eb162fSflorian } 83570eb162fSflorian } 836ad7c548dSflorian break; 8373ae4b9dfSflorian case DHO_RAPID_COMMIT: 8383ae4b9dfSflorian if (opt_hdr.len != 0) { 8393ae4b9dfSflorian log_warnx("%s: invalid rapid commit option", 8403ae4b9dfSflorian __func__); 8413ae4b9dfSflorian goto out; 8423ae4b9dfSflorian } 8433ae4b9dfSflorian rapid_commit = 1; 8443ae4b9dfSflorian break; 845ad7c548dSflorian default: 846ad7c548dSflorian log_debug("unhandled option: %u", opt_hdr.code); 847ad7c548dSflorian break; 848ad7c548dSflorian } 849ad7c548dSflorian 850ad7c548dSflorian p += opt_hdr.len; 851ad7c548dSflorian rem -= opt_hdr.len; 852ad7c548dSflorian } 853ad7c548dSflorian 854ad7c548dSflorian /* check that we got all the information we need */ 855ad7c548dSflorian if (serverid_len == 0) { 856ad7c548dSflorian log_warnx("%s: Did not receive server identifier", __func__); 857ad7c548dSflorian goto out; 858ad7c548dSflorian } 859ad7c548dSflorian 860ad7c548dSflorian 861ad7c548dSflorian SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) { 8620da4c31dSflorian struct prefix *pd = &iface->new_pds[ia_conf->id]; 863ad7c548dSflorian 864ad7c548dSflorian if (pd->prefix_len == 0) { 865ad7c548dSflorian log_warnx("%s: no IA for IAID %d found", __func__, 866ad7c548dSflorian ia_conf->id); 867ad7c548dSflorian goto out; 868ad7c548dSflorian } 869ad7c548dSflorian if (pd->prefix_len > ia_conf->prefix_len) { 870ad7c548dSflorian log_warnx("%s: prefix for IAID %d too small: %d > %d", 871ad7c548dSflorian __func__, ia_conf->id, pd->prefix_len, 872ad7c548dSflorian ia_conf->prefix_len); 873ad7c548dSflorian goto out; 874ad7c548dSflorian } 8751c8c2e64Sflorian 8761c8c2e64Sflorian if (lease_time < pd->vltime) 8771c8c2e64Sflorian lease_time = pd->vltime; 8781c8c2e64Sflorian 879ad7c548dSflorian log_debug("%s: pltime: %u, vltime: %u, prefix: %s/%u", 880ad7c548dSflorian __func__, pd->pltime, pd->vltime, inet_ntop(AF_INET6, 881ad7c548dSflorian &pd->prefix, ntopbuf, INET6_ADDRSTRLEN), pd->prefix_len); 882ad7c548dSflorian } 883ad7c548dSflorian 884ad7c548dSflorian switch (hdr.msg_type) { 885ad7c548dSflorian case DHCPSOLICIT: 886ad7c548dSflorian case DHCPREQUEST: 887ad7c548dSflorian case DHCPCONFIRM: 888ad7c548dSflorian case DHCPRENEW: 889ad7c548dSflorian case DHCPREBIND: 890ad7c548dSflorian case DHCPRELEASE: 891ad7c548dSflorian case DHCPDECLINE: 892ad7c548dSflorian case DHCPINFORMATIONREQUEST: 893ad7c548dSflorian log_warnx("%s: Ignoring client-only message (%s) from server", 894ad7c548dSflorian __func__, dhcp_message_type2str(hdr.msg_type)); 895ad7c548dSflorian goto out; 896ad7c548dSflorian case DHCPRELAYFORW: 897ad7c548dSflorian case DHCPRELAYREPL: 898ad7c548dSflorian log_warnx("%s: Ignoring relay-agent-only message (%s) from " 899ad7c548dSflorian "server", __func__, dhcp_message_type2str(hdr.msg_type)); 900ad7c548dSflorian goto out; 901ad7c548dSflorian case DHCPADVERTISE: 902ad7c548dSflorian if (iface->state != IF_INIT) { 903ad7c548dSflorian log_debug("%s: ignoring unexpected %s", __func__, 904ad7c548dSflorian dhcp_message_type2str(hdr.msg_type)); 905ad7c548dSflorian goto out; 906ad7c548dSflorian } 907ad7c548dSflorian iface->serverid_len = serverid_len; 908ad7c548dSflorian memcpy(iface->serverid, serverid, SERVERID_SIZE); 9090da4c31dSflorian memcpy(iface->pds, iface->new_pds, sizeof(iface->pds)); 910ad7c548dSflorian state_transition(iface, IF_REQUESTING); 911ad7c548dSflorian break; 912ad7c548dSflorian case DHCPREPLY: 9131c8c2e64Sflorian switch (iface->state) { 9141c8c2e64Sflorian case IF_REQUESTING: 9151c8c2e64Sflorian case IF_RENEWING: 9161c8c2e64Sflorian case IF_REBINDING: 917763cddbdSflorian case IF_REBOOTING: 9181c8c2e64Sflorian break; 9193ae4b9dfSflorian case IF_INIT: 9203ae4b9dfSflorian if (rapid_commit && engine_conf->rapid_commit) 9213ae4b9dfSflorian break; 9223ae4b9dfSflorian /* fall through */ 9231c8c2e64Sflorian default: 924ad7c548dSflorian log_debug("%s: ignoring unexpected %s", __func__, 925ad7c548dSflorian dhcp_message_type2str(hdr.msg_type)); 926ad7c548dSflorian goto out; 927ad7c548dSflorian } 928ad7c548dSflorian iface->serverid_len = serverid_len; 929ad7c548dSflorian memcpy(iface->serverid, serverid, SERVERID_SIZE); 9300da4c31dSflorian 931ad7c548dSflorian /* XXX handle t1 = 0 or t2 = 0 */ 932ad7c548dSflorian iface->t1 = t1; 933ad7c548dSflorian iface->t2 = t2; 9341c8c2e64Sflorian iface->lease_time = lease_time; 9351c8c2e64Sflorian clock_gettime(CLOCK_MONOTONIC, &iface->request_time); 936ad7c548dSflorian state_transition(iface, IF_BOUND); 937ad7c548dSflorian break; 938ad7c548dSflorian case DHCPRECONFIGURE: 939ad7c548dSflorian log_warnx("%s: Ignoring %s from server", 940ad7c548dSflorian __func__, dhcp_message_type2str(hdr.msg_type)); 941ad7c548dSflorian goto out; 942ad7c548dSflorian default: 943ad7c548dSflorian fatalx("%s: %s unhandled", 944ad7c548dSflorian __func__, dhcp_message_type2str(hdr.msg_type)); 945ad7c548dSflorian break; 946ad7c548dSflorian } 947ad7c548dSflorian out: 9480da4c31dSflorian return; 949ad7c548dSflorian } 950ad7c548dSflorian 95170eb162fSflorian int 952ad7c548dSflorian parse_ia_pd_options(uint8_t *p, size_t len, struct prefix *prefix) 953ad7c548dSflorian { 954ad7c548dSflorian struct dhcp_option_hdr opt_hdr; 955ad7c548dSflorian struct dhcp_iaprefix iaprefix; 956ad7c548dSflorian struct in6_addr mask; 957ad7c548dSflorian int i; 958363167f4Sflorian uint16_t status_code = DHCP_STATUS_SUCCESS; 959ad7c548dSflorian char ntopbuf[INET6_ADDRSTRLEN], *visbuf; 960ad7c548dSflorian 961ad7c548dSflorian while (len >= sizeof(struct dhcp_option_hdr)) { 962ad7c548dSflorian memcpy(&opt_hdr, p, sizeof(struct dhcp_option_hdr)); 963ad7c548dSflorian opt_hdr.code = ntohs(opt_hdr.code); 964ad7c548dSflorian opt_hdr.len = ntohs(opt_hdr.len); 965ad7c548dSflorian p += sizeof(struct dhcp_option_hdr); 966ad7c548dSflorian len -= sizeof(struct dhcp_option_hdr); 967ad7c548dSflorian if (log_getverbose() > 1) 968ad7c548dSflorian log_debug("%s: %s, len: %u", __func__, 969ad7c548dSflorian dhcp_option_type2str(opt_hdr.code), opt_hdr.len); 970ad7c548dSflorian if (len < opt_hdr.len) { 971ad7c548dSflorian log_warnx("%s: malformed packet, ignoring", __func__); 97270eb162fSflorian return DHCP_STATUS_UNSPECFAIL; 973ad7c548dSflorian } 974ad7c548dSflorian 975ad7c548dSflorian switch (opt_hdr.code) { 976ad7c548dSflorian case DHO_IA_PREFIX: 977ad7c548dSflorian if (len < sizeof(struct dhcp_iaprefix)) { 978ad7c548dSflorian log_warnx("%s: malformed packet, ignoring", 979ad7c548dSflorian __func__); 98070eb162fSflorian return DHCP_STATUS_UNSPECFAIL; 981ad7c548dSflorian } 982ad7c548dSflorian 983ad7c548dSflorian memcpy(&iaprefix, p, sizeof(struct dhcp_iaprefix)); 984ad7c548dSflorian log_debug("%s: pltime: %u, vltime: %u, prefix: %s/%u", 985ad7c548dSflorian __func__, ntohl(iaprefix.pltime), 986ad7c548dSflorian ntohl(iaprefix.vltime), inet_ntop(AF_INET6, 987ad7c548dSflorian &iaprefix.prefix, ntopbuf, INET6_ADDRSTRLEN), 988ad7c548dSflorian iaprefix.prefix_len); 98910da7d5dSflorian 990ad7c548dSflorian if (ntohl(iaprefix.vltime) < ntohl(iaprefix.pltime)) { 991ad7c548dSflorian log_warnx("%s: vltime < pltime, ignoring IA_PD", 992ad7c548dSflorian __func__); 993ad7c548dSflorian break; 994ad7c548dSflorian } 995ad7c548dSflorian 99610da7d5dSflorian if (ntohl(iaprefix.vltime) == 0) { 99710da7d5dSflorian log_debug("%s: vltime == 0, ignoring IA_PD", 99810da7d5dSflorian __func__); 99910da7d5dSflorian break; 100010da7d5dSflorian } 100110da7d5dSflorian 1002ad7c548dSflorian prefix->prefix = iaprefix.prefix; 1003ad7c548dSflorian prefix->prefix_len = iaprefix.prefix_len; 1004ad7c548dSflorian prefix->vltime = ntohl(iaprefix.vltime); 1005ad7c548dSflorian prefix->pltime = ntohl(iaprefix.pltime); 1006ad7c548dSflorian 100710da7d5dSflorian /* make sure prefix is masked correctly */ 1008ad7c548dSflorian memset(&mask, 0, sizeof(mask)); 1009ad7c548dSflorian in6_prefixlen2mask(&mask, prefix->prefix_len); 1010ad7c548dSflorian for (i = 0; i < 16; i++) 1011ad7c548dSflorian prefix->prefix.s6_addr[i] &= mask.s6_addr[i]; 1012ad7c548dSflorian 1013ad7c548dSflorian break; 1014ad7c548dSflorian case DHO_STATUS_CODE: 101570eb162fSflorian /* XXX STATUS_CODE can also appear outside of options */ 1016ad7c548dSflorian if (len < 2) { 1017ad7c548dSflorian log_warnx("%s: malformed packet, ignoring", 1018ad7c548dSflorian __func__); 101970eb162fSflorian return DHCP_STATUS_UNSPECFAIL; 1020ad7c548dSflorian } 1021ad7c548dSflorian memcpy(&status_code, p, sizeof(uint16_t)); 1022ad7c548dSflorian status_code = ntohs(status_code); 10237580ae52Sflorian /* must be at least 4 * srclen + 1 long */ 10247580ae52Sflorian visbuf = calloc(4, opt_hdr.len - 2 + 1); 10257580ae52Sflorian if (visbuf == NULL) { 10267580ae52Sflorian log_warn("%s", __func__); 10277580ae52Sflorian break; 10287580ae52Sflorian } 10297580ae52Sflorian strvisx(visbuf, p + 2, opt_hdr.len - 2, VIS_SAFE); 1030ad7c548dSflorian log_debug("%s: %s - %s", __func__, 1031ad7c548dSflorian dhcp_status2str(status_code), visbuf); 1032ad7c548dSflorian break; 1033ad7c548dSflorian default: 1034ad7c548dSflorian log_debug("unhandled option: %u", opt_hdr.code); 1035ad7c548dSflorian } 1036ad7c548dSflorian p += opt_hdr.len; 1037ad7c548dSflorian len -= opt_hdr.len; 1038ad7c548dSflorian } 103970eb162fSflorian return status_code; 1040ad7c548dSflorian } 1041ad7c548dSflorian 1042ad7c548dSflorian /* XXX check valid transitions */ 1043ad7c548dSflorian void 1044ad7c548dSflorian state_transition(struct dhcp6leased_iface *iface, enum if_state new_state) 1045ad7c548dSflorian { 1046ad7c548dSflorian enum if_state old_state = iface->state; 1047ad7c548dSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 1048ad7c548dSflorian 1049ad7c548dSflorian iface->state = new_state; 1050ad7c548dSflorian 1051ad7c548dSflorian switch (new_state) { 1052ad7c548dSflorian case IF_DOWN: 1053*efb7adaaSflorian switch (old_state) { 1054*efb7adaaSflorian case IF_RENEWING: 1055*efb7adaaSflorian case IF_REBINDING: 1056*efb7adaaSflorian case IF_REBOOTING: 1057*efb7adaaSflorian case IF_BOUND: 1058*efb7adaaSflorian deprecate_interfaces(iface); 1059*efb7adaaSflorian break; 1060*efb7adaaSflorian default: 1061*efb7adaaSflorian break; 1062*efb7adaaSflorian } 10636912c931Sflorian /* 1064*efb7adaaSflorian * Nothing else to do until iface comes up. 1065*efb7adaaSflorian * IP addresses will expire. 10666912c931Sflorian */ 1067ad7c548dSflorian iface->timo.tv_sec = -1; 1068ad7c548dSflorian break; 1069ad7c548dSflorian case IF_INIT: 1070ad7c548dSflorian switch (old_state) { 1071ad7c548dSflorian case IF_INIT: 1072ad7c548dSflorian if (iface->timo.tv_sec < MAX_EXP_BACKOFF_SLOW) 1073ad7c548dSflorian iface->timo.tv_sec *= 2; 1074ad7c548dSflorian break; 1075ad7c548dSflorian case IF_REQUESTING: 1076ad7c548dSflorian case IF_RENEWING: 1077ad7c548dSflorian case IF_REBINDING: 1078ad7c548dSflorian case IF_REBOOTING: 1079ad7c548dSflorian /* lease expired, got DHCPNAK or timeout: delete IP */ 108033c99573Sflorian deconfigure_interfaces(iface); 1081ad7c548dSflorian /* fall through */ 1082ad7c548dSflorian case IF_DOWN: 1083ad7c548dSflorian iface->timo.tv_sec = START_EXP_BACKOFF; 1084ad7c548dSflorian clock_gettime(CLOCK_MONOTONIC, 1085ad7c548dSflorian &iface->elapsed_time_start); 1086ad7c548dSflorian break; 1087ad7c548dSflorian case IF_BOUND: 1088ad7c548dSflorian fatal("invalid transition Bound -> Init"); 1089ad7c548dSflorian break; 1090ad7c548dSflorian } 1091ad7c548dSflorian request_dhcp_discover(iface); 1092ad7c548dSflorian break; 1093ad7c548dSflorian case IF_REBOOTING: 1094ad7c548dSflorian if (old_state == IF_REBOOTING) 1095ad7c548dSflorian iface->timo.tv_sec *= 2; 1096ad7c548dSflorian else { 1097ad7c548dSflorian iface->timo.tv_sec = START_EXP_BACKOFF; 1098ad7c548dSflorian arc4random_buf(iface->xid, sizeof(iface->xid)); 1099ad7c548dSflorian } 1100ad7c548dSflorian request_dhcp_request(iface); 1101ad7c548dSflorian break; 1102ad7c548dSflorian case IF_REQUESTING: 1103ad7c548dSflorian if (old_state == IF_REQUESTING) 1104ad7c548dSflorian iface->timo.tv_sec *= 2; 1105ad7c548dSflorian else { 1106ad7c548dSflorian iface->timo.tv_sec = START_EXP_BACKOFF; 1107ad7c548dSflorian clock_gettime(CLOCK_MONOTONIC, 1108ad7c548dSflorian &iface->elapsed_time_start); 1109ad7c548dSflorian } 1110ad7c548dSflorian request_dhcp_request(iface); 1111ad7c548dSflorian break; 1112ad7c548dSflorian case IF_BOUND: 1113ad7c548dSflorian iface->timo.tv_sec = iface->t1; 11141c8c2e64Sflorian switch (old_state) { 11151c8c2e64Sflorian case IF_REQUESTING: 11161c8c2e64Sflorian case IF_RENEWING: 11171c8c2e64Sflorian case IF_REBINDING: 11181c8c2e64Sflorian case IF_REBOOTING: 1119ad7c548dSflorian configure_interfaces(iface); 11201c8c2e64Sflorian break; 11213ae4b9dfSflorian case IF_INIT: 11223ae4b9dfSflorian if (engine_conf->rapid_commit) 11233ae4b9dfSflorian configure_interfaces(iface); 11243ae4b9dfSflorian else 11253ae4b9dfSflorian fatal("invalid transition Init -> Bound"); 11263ae4b9dfSflorian break; 11271c8c2e64Sflorian default: 11281c8c2e64Sflorian break; 1129ad7c548dSflorian } 1130ad7c548dSflorian break; 1131ad7c548dSflorian case IF_RENEWING: 1132ad7c548dSflorian if (old_state == IF_BOUND) { 1133ad7c548dSflorian iface->timo.tv_sec = (iface->t2 - 1134ad7c548dSflorian iface->t1) / 2; /* RFC 2131 4.4.5 */ 1135ad7c548dSflorian arc4random_buf(iface->xid, sizeof(iface->xid)); 1136ad7c548dSflorian } else 1137ad7c548dSflorian iface->timo.tv_sec /= 2; 1138ad7c548dSflorian 1139ad7c548dSflorian if (iface->timo.tv_sec < 60) 1140ad7c548dSflorian iface->timo.tv_sec = 60; 1141ad7c548dSflorian request_dhcp_request(iface); 1142ad7c548dSflorian break; 1143ad7c548dSflorian case IF_REBINDING: 1144ad7c548dSflorian if (old_state == IF_RENEWING) { 1145ad7c548dSflorian iface->timo.tv_sec = (iface->lease_time - 1146ad7c548dSflorian iface->t2) / 2; /* RFC 2131 4.4.5 */ 1147ad7c548dSflorian } else 1148ad7c548dSflorian iface->timo.tv_sec /= 2; 1149ad7c548dSflorian request_dhcp_request(iface); 1150ad7c548dSflorian break; 1151ad7c548dSflorian } 1152ad7c548dSflorian 1153ad7c548dSflorian if_name = if_indextoname(iface->if_index, ifnamebuf); 1154ad7c548dSflorian log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL ? 1155ad7c548dSflorian "?" : if_name, if_state_name[old_state], if_state_name[new_state], 1156ad7c548dSflorian iface->timo.tv_sec); 1157ad7c548dSflorian 1158ad7c548dSflorian if (iface->timo.tv_sec == -1) { 1159ad7c548dSflorian if (evtimer_pending(&iface->timer, NULL)) 1160ad7c548dSflorian evtimer_del(&iface->timer); 1161ad7c548dSflorian } else 1162ad7c548dSflorian evtimer_add(&iface->timer, &iface->timo); 1163ad7c548dSflorian } 1164ad7c548dSflorian 1165ad7c548dSflorian void 1166ad7c548dSflorian iface_timeout(int fd, short events, void *arg) 1167ad7c548dSflorian { 1168ad7c548dSflorian struct dhcp6leased_iface *iface = (struct dhcp6leased_iface *)arg; 1169ad7c548dSflorian struct timespec now, res; 1170ad7c548dSflorian 1171ad7c548dSflorian log_debug("%s[%d]: %s", __func__, iface->if_index, 1172ad7c548dSflorian if_state_name[iface->state]); 1173ad7c548dSflorian 1174ad7c548dSflorian switch (iface->state) { 1175ad7c548dSflorian case IF_DOWN: 1176ad7c548dSflorian state_transition(iface, IF_DOWN); 1177ad7c548dSflorian break; 1178ad7c548dSflorian case IF_INIT: 1179ad7c548dSflorian state_transition(iface, IF_INIT); 1180ad7c548dSflorian break; 1181ad7c548dSflorian case IF_REBOOTING: 1182ad7c548dSflorian if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_FAST) 1183ad7c548dSflorian state_transition(iface, IF_INIT); 1184ad7c548dSflorian else 1185ad7c548dSflorian state_transition(iface, IF_REBOOTING); 1186ad7c548dSflorian break; 1187ad7c548dSflorian case IF_REQUESTING: 1188ad7c548dSflorian if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_SLOW) 1189ad7c548dSflorian state_transition(iface, IF_INIT); 1190ad7c548dSflorian else 1191ad7c548dSflorian state_transition(iface, IF_REQUESTING); 1192ad7c548dSflorian break; 1193ad7c548dSflorian case IF_BOUND: 1194ad7c548dSflorian state_transition(iface, IF_RENEWING); 1195ad7c548dSflorian break; 1196ad7c548dSflorian case IF_RENEWING: 1197ad7c548dSflorian clock_gettime(CLOCK_MONOTONIC, &now); 1198ad7c548dSflorian timespecsub(&now, &iface->request_time, &res); 1199ad7c548dSflorian log_debug("%s: res.tv_sec: %lld, t2: %u", __func__, 1200ad7c548dSflorian res.tv_sec, iface->t2); 12011c8c2e64Sflorian if (res.tv_sec >= iface->t2) 1202ad7c548dSflorian state_transition(iface, IF_REBINDING); 1203ad7c548dSflorian else 1204ad7c548dSflorian state_transition(iface, IF_RENEWING); 1205ad7c548dSflorian break; 1206ad7c548dSflorian case IF_REBINDING: 1207ad7c548dSflorian clock_gettime(CLOCK_MONOTONIC, &now); 1208ad7c548dSflorian timespecsub(&now, &iface->request_time, &res); 1209ad7c548dSflorian log_debug("%s: res.tv_sec: %lld, lease_time: %u", __func__, 1210ad7c548dSflorian res.tv_sec, iface->lease_time); 1211ad7c548dSflorian if (res.tv_sec > iface->lease_time) 1212ad7c548dSflorian state_transition(iface, IF_INIT); 1213ad7c548dSflorian else 1214ad7c548dSflorian state_transition(iface, IF_REBINDING); 1215ad7c548dSflorian break; 1216ad7c548dSflorian } 1217ad7c548dSflorian } 1218ad7c548dSflorian 12191c8c2e64Sflorian /* XXX can this be merged into dhcp_request()? */ 1220ad7c548dSflorian void 1221ad7c548dSflorian request_dhcp_discover(struct dhcp6leased_iface *iface) 1222ad7c548dSflorian { 1223ad7c548dSflorian struct imsg_req_dhcp imsg; 1224ad7c548dSflorian struct timespec now, res; 1225ad7c548dSflorian 1226ad7c548dSflorian memset(&imsg, 0, sizeof(imsg)); 1227ad7c548dSflorian imsg.if_index = iface->if_index; 1228ad7c548dSflorian memcpy(imsg.xid, iface->xid, sizeof(imsg.xid)); 1229ad7c548dSflorian clock_gettime(CLOCK_MONOTONIC, &now); 1230ad7c548dSflorian timespecsub(&now, &iface->elapsed_time_start, &res); 1231ad7c548dSflorian if (res.tv_sec * 100 > 0xffff) 1232ad7c548dSflorian imsg.elapsed_time = 0xffff; 1233ad7c548dSflorian else 1234ad7c548dSflorian imsg.elapsed_time = res.tv_sec * 100; 12351c8c2e64Sflorian engine_imsg_compose_frontend(IMSG_SEND_SOLICIT, 0, &imsg, sizeof(imsg)); 1236ad7c548dSflorian } 1237ad7c548dSflorian 1238ad7c548dSflorian void 1239ad7c548dSflorian request_dhcp_request(struct dhcp6leased_iface *iface) 1240ad7c548dSflorian { 1241ad7c548dSflorian struct imsg_req_dhcp imsg; 1242ad7c548dSflorian struct timespec now, res; 1243ad7c548dSflorian 1244ad7c548dSflorian memset(&imsg, 0, sizeof(imsg)); 1245ad7c548dSflorian imsg.if_index = iface->if_index; 1246ad7c548dSflorian memcpy(imsg.xid, iface->xid, sizeof(imsg.xid)); 1247ad7c548dSflorian 1248ad7c548dSflorian clock_gettime(CLOCK_MONOTONIC, &now); 1249ad7c548dSflorian timespecsub(&now, &iface->elapsed_time_start, &res); 1250ad7c548dSflorian if (res.tv_sec * 100 > 0xffff) 1251ad7c548dSflorian imsg.elapsed_time = 0xffff; 1252ad7c548dSflorian else 1253ad7c548dSflorian imsg.elapsed_time = res.tv_sec * 100; 1254ad7c548dSflorian 1255ad7c548dSflorian switch (iface->state) { 1256ad7c548dSflorian case IF_DOWN: 1257ad7c548dSflorian fatalx("invalid state IF_DOWN in %s", __func__); 1258ad7c548dSflorian break; 1259ad7c548dSflorian case IF_INIT: 1260ad7c548dSflorian fatalx("invalid state IF_INIT in %s", __func__); 1261ad7c548dSflorian break; 1262ad7c548dSflorian case IF_BOUND: 1263ad7c548dSflorian fatalx("invalid state IF_BOUND in %s", __func__); 1264ad7c548dSflorian break; 1265ad7c548dSflorian case IF_REBOOTING: 1266ad7c548dSflorian case IF_REQUESTING: 12671c8c2e64Sflorian case IF_RENEWING: 12681c8c2e64Sflorian case IF_REBINDING: 1269ad7c548dSflorian imsg.serverid_len = iface->serverid_len; 1270ad7c548dSflorian memcpy(imsg.serverid, iface->serverid, SERVERID_SIZE); 1271ad7c548dSflorian memcpy(imsg.pds, iface->pds, sizeof(iface->pds)); 1272ad7c548dSflorian break; 12731c8c2e64Sflorian } 12741c8c2e64Sflorian switch (iface->state) { 12751c8c2e64Sflorian case IF_REQUESTING: 12761c8c2e64Sflorian engine_imsg_compose_frontend(IMSG_SEND_REQUEST, 0, &imsg, 12771c8c2e64Sflorian sizeof(imsg)); 12781c8c2e64Sflorian break; 1279ad7c548dSflorian case IF_RENEWING: 12801c8c2e64Sflorian engine_imsg_compose_frontend(IMSG_SEND_RENEW, 0, &imsg, 12811c8c2e64Sflorian sizeof(imsg)); 1282ad7c548dSflorian break; 1283763cddbdSflorian case IF_REBOOTING: 1284ad7c548dSflorian case IF_REBINDING: 12851c8c2e64Sflorian engine_imsg_compose_frontend(IMSG_SEND_REBIND, 0, &imsg, 12861c8c2e64Sflorian sizeof(imsg)); 1287ad7c548dSflorian break; 12881c8c2e64Sflorian default: 12891c8c2e64Sflorian fatalx("%s: wrong state", __func__); 1290ad7c548dSflorian } 1291ad7c548dSflorian } 1292ad7c548dSflorian 1293ad7c548dSflorian /* XXX we need to install a reject route for the delegated prefix */ 1294ad7c548dSflorian void 1295ad7c548dSflorian configure_interfaces(struct dhcp6leased_iface *iface) 1296ad7c548dSflorian { 1297ad7c548dSflorian struct iface_conf *iface_conf; 1298ad7c548dSflorian struct iface_ia_conf *ia_conf; 1299ad7c548dSflorian struct iface_pd_conf *pd_conf; 1300763cddbdSflorian struct imsg_lease_info imsg_lease_info; 13010498f896Sflorian uint32_t i; 13020498f896Sflorian char ntopbuf[INET6_ADDRSTRLEN]; 1303ad7c548dSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 1304ad7c548dSflorian 1305ad7c548dSflorian if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) { 1306ad7c548dSflorian log_debug("%s: unknown interface %d", __func__, 1307ad7c548dSflorian iface->if_index); 1308ad7c548dSflorian return; 1309ad7c548dSflorian } 1310ad7c548dSflorian if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name)) 1311ad7c548dSflorian == NULL) { 1312ad7c548dSflorian log_debug("%s: no interface configuration for %d", __func__, 1313ad7c548dSflorian iface->if_index); 1314ad7c548dSflorian return; 1315ad7c548dSflorian } 1316ad7c548dSflorian 13170498f896Sflorian for (i = 0; i < iface_conf->ia_count; i++) { 13180498f896Sflorian struct prefix *pd = &iface->new_pds[i]; 13190498f896Sflorian 13200498f896Sflorian log_info("prefix delegation #%d %s/%d received on %s from " 13210498f896Sflorian "server %s", i, inet_ntop(AF_INET6, &pd->prefix, ntopbuf, 13220498f896Sflorian INET6_ADDRSTRLEN), pd->prefix_len, if_name, 13230498f896Sflorian dhcp_duid2str(iface->serverid_len, iface->serverid)); 13240498f896Sflorian } 1325763cddbdSflorian 1326ad7c548dSflorian SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) { 13270da4c31dSflorian struct prefix *pd = &iface->new_pds[ia_conf->id]; 1328ad7c548dSflorian 1329ad7c548dSflorian SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) { 133033c99573Sflorian send_reconfigure_interface(pd_conf, pd, CONFIGURE); 1331ad7c548dSflorian } 1332ad7c548dSflorian } 13330da4c31dSflorian 13340da4c31dSflorian if (prefixcmp(iface->pds, iface->new_pds, iface_conf->ia_count) != 0) { 13350498f896Sflorian log_info("Prefix delegations on %s from server %s changed", 13360498f896Sflorian if_name, dhcp_duid2str(iface->serverid_len, 13370498f896Sflorian iface->serverid)); 13380da4c31dSflorian for (i = 0; i < iface_conf->ia_count; i++) { 13390da4c31dSflorian log_debug("%s: iface->pds [%d]: %s/%d", __func__, i, 13400da4c31dSflorian inet_ntop(AF_INET6, &iface->pds[i].prefix, ntopbuf, 13410da4c31dSflorian INET6_ADDRSTRLEN), iface->pds[i].prefix_len); 13420da4c31dSflorian log_debug("%s: pds [%d]: %s/%d", __func__, i, 13430da4c31dSflorian inet_ntop(AF_INET6, &iface->new_pds[i].prefix, 13440da4c31dSflorian ntopbuf, INET6_ADDRSTRLEN), 13450da4c31dSflorian iface->new_pds[i].prefix_len); 13460da4c31dSflorian } 13470da4c31dSflorian deconfigure_interfaces(iface); 13480da4c31dSflorian } 13490da4c31dSflorian 13500da4c31dSflorian memcpy(iface->pds, iface->new_pds, sizeof(iface->pds)); 13510da4c31dSflorian memset(iface->new_pds, 0, sizeof(iface->new_pds)); 1352cc3e93c2Sflorian 1353cc3e93c2Sflorian memset(&imsg_lease_info, 0, sizeof(imsg_lease_info)); 1354cc3e93c2Sflorian imsg_lease_info.if_index = iface->if_index; 1355cc3e93c2Sflorian memcpy(imsg_lease_info.pds, iface->pds, sizeof(iface->pds)); 1356cc3e93c2Sflorian engine_imsg_compose_main(IMSG_WRITE_LEASE, 0, &imsg_lease_info, 1357cc3e93c2Sflorian sizeof(imsg_lease_info)); 1358ad7c548dSflorian } 1359ad7c548dSflorian 1360ad7c548dSflorian void 136133c99573Sflorian deconfigure_interfaces(struct dhcp6leased_iface *iface) 136233c99573Sflorian { 136333c99573Sflorian struct iface_conf *iface_conf; 136433c99573Sflorian struct iface_ia_conf *ia_conf; 136533c99573Sflorian struct iface_pd_conf *pd_conf; 13660498f896Sflorian uint32_t i; 13670498f896Sflorian char ntopbuf[INET6_ADDRSTRLEN]; 136833c99573Sflorian char ifnamebuf[IF_NAMESIZE], *if_name; 136933c99573Sflorian 137033c99573Sflorian 137133c99573Sflorian if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) { 137233c99573Sflorian log_debug("%s: unknown interface %d", __func__, 137333c99573Sflorian iface->if_index); 137433c99573Sflorian return; 137533c99573Sflorian } 137633c99573Sflorian if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name)) 137733c99573Sflorian == NULL) { 137833c99573Sflorian log_debug("%s: no interface configuration for %d", __func__, 137933c99573Sflorian iface->if_index); 138033c99573Sflorian return; 138133c99573Sflorian } 138233c99573Sflorian 13830498f896Sflorian for (i = 0; i < iface_conf->ia_count; i++) { 13840498f896Sflorian struct prefix *pd = &iface->pds[i]; 13850498f896Sflorian 13860498f896Sflorian log_info("Prefix delegation #%d %s/%d expired on %s from " 13870498f896Sflorian "server %s", i, inet_ntop(AF_INET6, &pd->prefix, ntopbuf, 13880498f896Sflorian INET6_ADDRSTRLEN), pd->prefix_len, if_name, 13890498f896Sflorian dhcp_duid2str(iface->serverid_len, iface->serverid)); 13900498f896Sflorian } 13910498f896Sflorian 139233c99573Sflorian SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) { 139333c99573Sflorian struct prefix *pd = &iface->pds[ia_conf->id]; 139433c99573Sflorian 139533c99573Sflorian SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) { 139633c99573Sflorian send_reconfigure_interface(pd_conf, pd, DECONFIGURE); 139733c99573Sflorian } 139833c99573Sflorian } 13998e130894Sflorian memset(iface->pds, 0, sizeof(iface->pds)); 140033c99573Sflorian } 140133c99573Sflorian 1402*efb7adaaSflorian void 1403*efb7adaaSflorian deprecate_interfaces(struct dhcp6leased_iface *iface) 1404*efb7adaaSflorian { 1405*efb7adaaSflorian struct iface_conf *iface_conf; 1406*efb7adaaSflorian struct iface_ia_conf *ia_conf; 1407*efb7adaaSflorian struct iface_pd_conf *pd_conf; 1408*efb7adaaSflorian struct timespec now, diff; 1409*efb7adaaSflorian uint32_t i; 1410*efb7adaaSflorian char ntopbuf[INET6_ADDRSTRLEN]; 1411*efb7adaaSflorian char ifnamebuf[IF_NAMESIZE], *if_name; 1412*efb7adaaSflorian 1413*efb7adaaSflorian 1414*efb7adaaSflorian if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) { 1415*efb7adaaSflorian log_debug("%s: unknown interface %d", __func__, 1416*efb7adaaSflorian iface->if_index); 1417*efb7adaaSflorian return; 1418*efb7adaaSflorian } 1419*efb7adaaSflorian if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name)) 1420*efb7adaaSflorian == NULL) { 1421*efb7adaaSflorian log_debug("%s: no interface configuration for %d", __func__, 1422*efb7adaaSflorian iface->if_index); 1423*efb7adaaSflorian return; 1424*efb7adaaSflorian } 1425*efb7adaaSflorian 1426*efb7adaaSflorian for (i = 0; i < iface_conf->ia_count; i++) { 1427*efb7adaaSflorian struct prefix *pd = &iface->pds[i]; 1428*efb7adaaSflorian 1429*efb7adaaSflorian log_info("%s went down, deprecating prefix delegation #%d %s/%d" 1430*efb7adaaSflorian " from server %s", if_name, i, inet_ntop(AF_INET6, 1431*efb7adaaSflorian &pd->prefix, ntopbuf, INET6_ADDRSTRLEN), pd->prefix_len, 1432*efb7adaaSflorian dhcp_duid2str(iface->serverid_len, iface->serverid)); 1433*efb7adaaSflorian } 1434*efb7adaaSflorian 1435*efb7adaaSflorian clock_gettime(CLOCK_MONOTONIC, &now); 1436*efb7adaaSflorian timespecsub(&now, &iface->request_time, &diff); 1437*efb7adaaSflorian 1438*efb7adaaSflorian SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) { 1439*efb7adaaSflorian struct prefix *pd = &iface->pds[ia_conf->id]; 1440*efb7adaaSflorian 1441*efb7adaaSflorian if (pd->vltime > diff.tv_sec) 1442*efb7adaaSflorian pd->vltime -= diff.tv_sec; 1443*efb7adaaSflorian else 1444*efb7adaaSflorian pd->vltime = 0; 1445*efb7adaaSflorian 1446*efb7adaaSflorian pd->pltime = 0; 1447*efb7adaaSflorian 1448*efb7adaaSflorian SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) { 1449*efb7adaaSflorian send_reconfigure_interface(pd_conf, pd, CONFIGURE); 1450*efb7adaaSflorian } 1451*efb7adaaSflorian } 1452*efb7adaaSflorian } 1453*efb7adaaSflorian 14540da4c31dSflorian int 14550da4c31dSflorian prefixcmp(struct prefix *a, struct prefix *b, int count) 14560da4c31dSflorian { 14570da4c31dSflorian int i; 14580da4c31dSflorian 14590da4c31dSflorian for (i = 0; i < count; i++) { 14600da4c31dSflorian if (a[i].prefix_len != b[i].prefix_len) 14610da4c31dSflorian return 1; 14620da4c31dSflorian if (memcmp(&a[i].prefix, &b[i].prefix, 14630da4c31dSflorian sizeof(struct in6_addr)) != 0) 14640da4c31dSflorian return 1; 14650da4c31dSflorian } 14660da4c31dSflorian return 0; 14670da4c31dSflorian } 14680da4c31dSflorian 146933c99573Sflorian void 147033c99573Sflorian send_reconfigure_interface(struct iface_pd_conf *pd_conf, struct prefix *pd, 147133c99573Sflorian enum reconfigure_action action) 1472ad7c548dSflorian { 1473ad7c548dSflorian struct imsg_configure_address address; 1474ad7c548dSflorian uint32_t if_index; 1475ad7c548dSflorian int i; 1476ad7c548dSflorian char ntopbuf[INET6_ADDRSTRLEN]; 1477ad7c548dSflorian 14787e484d93Sflorian if (pd->prefix_len == 0) 14797e484d93Sflorian return; 14807e484d93Sflorian 1481ad7c548dSflorian if (strcmp(pd_conf->name, "reserve") == 0) 1482ad7c548dSflorian return; 1483ad7c548dSflorian 1484ad7c548dSflorian if ((if_index = if_nametoindex(pd_conf->name)) == 0) 1485ad7c548dSflorian return; 1486ad7c548dSflorian 1487ad7c548dSflorian memset(&address, 0, sizeof(address)); 1488ad7c548dSflorian 1489ad7c548dSflorian address.if_index = if_index; 1490ad7c548dSflorian address.addr.sin6_family = AF_INET6; 1491ad7c548dSflorian address.addr.sin6_len = sizeof(address.addr); 1492ad7c548dSflorian address.addr.sin6_addr = pd->prefix; 1493ad7c548dSflorian 1494ad7c548dSflorian for (i = 0; i < 16; i++) 1495ad7c548dSflorian address.addr.sin6_addr.s6_addr[i] |= 1496ad7c548dSflorian pd_conf->prefix_mask.s6_addr[i]; 1497ad7c548dSflorian 1498ad7c548dSflorian /* XXX make this configurable & use SOII */ 1499ad7c548dSflorian address.addr.sin6_addr.s6_addr[15] |= 1; 1500ad7c548dSflorian 1501ad7c548dSflorian in6_prefixlen2mask(&address.mask, pd_conf->prefix_len); 1502ad7c548dSflorian 150333c99573Sflorian log_debug("%s: %s %s: %s/%d", __func__, pd_conf->name, 150433c99573Sflorian reconfigure_action_name[action], inet_ntop(AF_INET6, 150533c99573Sflorian &address.addr.sin6_addr, ntopbuf, INET6_ADDRSTRLEN), 150633c99573Sflorian pd_conf->prefix_len); 1507ad7c548dSflorian 1508ad7c548dSflorian address.vltime = pd->vltime; 1509ad7c548dSflorian address.pltime = pd->pltime; 1510ad7c548dSflorian 151133c99573Sflorian if (action == CONFIGURE) 1512ad7c548dSflorian engine_imsg_compose_main(IMSG_CONFIGURE_ADDRESS, 0, &address, 1513ad7c548dSflorian sizeof(address)); 151433c99573Sflorian else 151533c99573Sflorian engine_imsg_compose_main(IMSG_DECONFIGURE_ADDRESS, 0, &address, 151633c99573Sflorian sizeof(address)); 1517ad7c548dSflorian } 1518ad7c548dSflorian 1519ad7c548dSflorian const char * 15207571100dSflorian dhcp_message_type2str(int type) 1521ad7c548dSflorian { 1522ad7c548dSflorian static char buf[sizeof("Unknown [255]")]; 1523ad7c548dSflorian 1524ad7c548dSflorian switch (type) { 1525ad7c548dSflorian case DHCPSOLICIT: 1526ad7c548dSflorian return "DHCPSOLICIT"; 1527ad7c548dSflorian case DHCPADVERTISE: 1528ad7c548dSflorian return "DHCPADVERTISE"; 1529ad7c548dSflorian case DHCPREQUEST: 1530ad7c548dSflorian return "DHCPREQUEST"; 1531ad7c548dSflorian case DHCPCONFIRM: 1532ad7c548dSflorian return "DHCPCONFIRM"; 1533ad7c548dSflorian case DHCPRENEW: 1534ad7c548dSflorian return "DHCPRENEW"; 1535ad7c548dSflorian case DHCPREBIND: 1536ad7c548dSflorian return "DHCPREBIND"; 1537ad7c548dSflorian case DHCPREPLY: 1538ad7c548dSflorian return "DHCPREPLY"; 1539ad7c548dSflorian case DHCPRELEASE: 1540ad7c548dSflorian return "DHCPRELEASE"; 1541ad7c548dSflorian case DHCPDECLINE: 1542ad7c548dSflorian return "DHCPDECLINE"; 1543ad7c548dSflorian case DHCPRECONFIGURE: 1544ad7c548dSflorian return "DHCPRECONFIGURE"; 1545ad7c548dSflorian case DHCPINFORMATIONREQUEST: 1546ad7c548dSflorian return "DHCPINFORMATIONREQUEST"; 1547ad7c548dSflorian case DHCPRELAYFORW: 1548ad7c548dSflorian return "DHCPRELAYFORW"; 1549ad7c548dSflorian case DHCPRELAYREPL: 1550ad7c548dSflorian return "DHCPRELAYREPL"; 1551ad7c548dSflorian default: 15527571100dSflorian snprintf(buf, sizeof(buf), "Unknown [%u]", type & 0xff); 1553ad7c548dSflorian return buf; 1554ad7c548dSflorian } 1555ad7c548dSflorian } 1556ad7c548dSflorian 1557ad7c548dSflorian const char * 15587571100dSflorian dhcp_option_type2str(int code) 1559ad7c548dSflorian { 1560ad7c548dSflorian static char buf[sizeof("Unknown [65535]")]; 1561ad7c548dSflorian switch (code) { 1562ad7c548dSflorian case DHO_CLIENTID: 1563ad7c548dSflorian return "DHO_CLIENTID"; 1564ad7c548dSflorian case DHO_SERVERID: 1565ad7c548dSflorian return "DHO_SERVERID"; 1566ad7c548dSflorian case DHO_ORO: 1567ad7c548dSflorian return "DHO_ORO"; 1568ad7c548dSflorian case DHO_ELAPSED_TIME: 1569ad7c548dSflorian return "DHO_ELAPSED_TIME"; 1570ad7c548dSflorian case DHO_STATUS_CODE: 1571ad7c548dSflorian return "DHO_STATUS_CODE"; 1572ad7c548dSflorian case DHO_RAPID_COMMIT: 1573ad7c548dSflorian return "DHO_RAPID_COMMIT"; 1574ad7c548dSflorian case DHO_VENDOR_CLASS: 1575ad7c548dSflorian return "DHO_VENDOR_CLASS"; 1576ad7c548dSflorian case DHO_IA_PD: 1577ad7c548dSflorian return "DHO_IA_PD"; 1578ad7c548dSflorian case DHO_IA_PREFIX: 1579ad7c548dSflorian return "DHO_IA_PREFIX"; 1580ad7c548dSflorian case DHO_SOL_MAX_RT: 1581ad7c548dSflorian return "DHO_SOL_MAX_RT"; 1582ad7c548dSflorian case DHO_INF_MAX_RT: 1583ad7c548dSflorian return "DHO_INF_MAX_RT"; 1584ad7c548dSflorian default: 15857571100dSflorian snprintf(buf, sizeof(buf), "Unknown [%u]", code &0xffff); 1586ad7c548dSflorian return buf; 1587ad7c548dSflorian } 1588ad7c548dSflorian } 1589ad7c548dSflorian 1590ad7c548dSflorian const char* 1591ad7c548dSflorian dhcp_duid2str(int len, uint8_t *p) 1592ad7c548dSflorian { 1593ad7c548dSflorian static char buf[2 * 130]; 1594ad7c548dSflorian int i, rem; 1595ad7c548dSflorian char *pbuf; 1596ad7c548dSflorian 1597ad7c548dSflorian if (len > 130) 1598ad7c548dSflorian return "invalid"; 1599ad7c548dSflorian 1600ad7c548dSflorian pbuf = buf; 1601ad7c548dSflorian rem = sizeof(buf); 1602ad7c548dSflorian for (i = 0; i < len && rem > 0; i++, pbuf += 2, rem -=2) 1603ad7c548dSflorian snprintf(pbuf, rem, "%02x", p[i]); 1604ad7c548dSflorian 1605ad7c548dSflorian return buf; 1606ad7c548dSflorian } 1607ad7c548dSflorian 1608ad7c548dSflorian const char* 16097571100dSflorian dhcp_status2str(int status) 1610ad7c548dSflorian { 1611ad7c548dSflorian static char buf[sizeof("Unknown [255]")]; 1612ad7c548dSflorian 1613ad7c548dSflorian switch (status) { 1614ad7c548dSflorian case DHCP_STATUS_SUCCESS: 1615ad7c548dSflorian return "Success"; 1616ad7c548dSflorian case DHCP_STATUS_UNSPECFAIL: 1617ad7c548dSflorian return "UnspecFail"; 1618ad7c548dSflorian case DHCP_STATUS_NOADDRSAVAIL: 1619ad7c548dSflorian return "NoAddrsAvail"; 1620ad7c548dSflorian case DHCP_STATUS_NOBINDING: 1621ad7c548dSflorian return "NoBinding"; 1622ad7c548dSflorian case DHCP_STATUS_NOTONLINK: 1623ad7c548dSflorian return "NotOnLink"; 1624ad7c548dSflorian case DHCP_STATUS_USEMULTICAST: 1625ad7c548dSflorian return "UseMulticast"; 1626ad7c548dSflorian case DHCP_STATUS_NOPREFIXAVAIL: 1627ad7c548dSflorian return "NoPrefixAvail"; 1628ad7c548dSflorian default: 16297571100dSflorian snprintf(buf, sizeof(buf), "Unknown [%u]", status & 0xff); 1630ad7c548dSflorian return buf; 1631ad7c548dSflorian } 1632ad7c548dSflorian } 1633ad7c548dSflorian 1634ad7c548dSflorian /* from sys/netinet6/in6.c */ 1635ad7c548dSflorian /* 1636ad7c548dSflorian * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 1637ad7c548dSflorian * All rights reserved. 1638ad7c548dSflorian * 1639ad7c548dSflorian * Redistribution and use in source and binary forms, with or without 1640ad7c548dSflorian * modification, are permitted provided that the following conditions 1641ad7c548dSflorian * are met: 1642ad7c548dSflorian * 1. Redistributions of source code must retain the above copyright 1643ad7c548dSflorian * notice, this list of conditions and the following disclaimer. 1644ad7c548dSflorian * 2. Redistributions in binary form must reproduce the above copyright 1645ad7c548dSflorian * notice, this list of conditions and the following disclaimer in the 1646ad7c548dSflorian * documentation and/or other materials provided with the distribution. 1647ad7c548dSflorian * 3. Neither the name of the project nor the names of its contributors 1648ad7c548dSflorian * may be used to endorse or promote products derived from this software 1649ad7c548dSflorian * without specific prior written permission. 1650ad7c548dSflorian * 1651ad7c548dSflorian * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1652ad7c548dSflorian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1653ad7c548dSflorian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1654ad7c548dSflorian * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 1655ad7c548dSflorian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1656ad7c548dSflorian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1657ad7c548dSflorian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1658ad7c548dSflorian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1659ad7c548dSflorian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1660ad7c548dSflorian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1661ad7c548dSflorian * SUCH DAMAGE. 1662ad7c548dSflorian */ 1663ad7c548dSflorian void 1664ad7c548dSflorian in6_prefixlen2mask(struct in6_addr *maskp, int len) 1665ad7c548dSflorian { 1666ad7c548dSflorian u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; 1667ad7c548dSflorian int bytelen, bitlen, i; 1668ad7c548dSflorian 1669ad7c548dSflorian if (0 > len || len > 128) 1670ad7c548dSflorian fatalx("%s: invalid prefix length(%d)\n", __func__, len); 1671ad7c548dSflorian 1672ad7c548dSflorian bzero(maskp, sizeof(*maskp)); 1673ad7c548dSflorian bytelen = len / 8; 1674ad7c548dSflorian bitlen = len % 8; 1675ad7c548dSflorian for (i = 0; i < bytelen; i++) 1676ad7c548dSflorian maskp->s6_addr[i] = 0xff; 1677ad7c548dSflorian /* len == 128 is ok because bitlen == 0 then */ 1678ad7c548dSflorian if (bitlen) 1679ad7c548dSflorian maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; 1680ad7c548dSflorian } 1681