1/* $OpenBSD: frontend.c,v 1.33 2024/01/26 21:14:08 jan Exp $ */ 2 3/* 4 * Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org> 5 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 6 * Copyright (c) 2004 Esben Norby <norby@openbsd.org> 7 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21#include <sys/types.h> 22#include <sys/ioctl.h> 23#include <sys/queue.h> 24#include <sys/socket.h> 25#include <sys/syslog.h> 26#include <sys/uio.h> 27 28#include <net/bpf.h> 29#include <net/if.h> 30#include <net/if_dl.h> 31#include <net/if_types.h> 32#include <net/route.h> 33 34#include <netinet/in.h> 35#include <netinet/if_ether.h> 36#include <netinet/ip.h> 37#include <netinet/udp.h> 38 39#include <arpa/inet.h> 40 41#include <errno.h> 42#include <event.h> 43#include <ifaddrs.h> 44#include <imsg.h> 45#include <pwd.h> 46#include <signal.h> 47#include <stdio.h> 48#include <stdlib.h> 49#include <string.h> 50#include <unistd.h> 51 52#include "bpf.h" 53#include "log.h" 54#include "dhcpleased.h" 55#include "frontend.h" 56#include "control.h" 57#include "checksum.h" 58 59#define ROUTE_SOCKET_BUF_SIZE 16384 60#define BOOTP_MIN_LEN 300 /* fixed bootp packet adds up to 300 */ 61 62struct bpf_ev { 63 struct event ev; 64 uint8_t buf[BPFLEN]; 65}; 66 67struct iface { 68 LIST_ENTRY(iface) entries; 69 struct bpf_ev bpfev; 70 struct imsg_ifinfo ifinfo; 71 int send_discover; 72 uint32_t xid; 73 struct in_addr ciaddr; 74 struct in_addr requested_ip; 75 struct in_addr server_identifier; 76 struct in_addr dhcp_server; 77 int udpsock; 78}; 79 80__dead void frontend_shutdown(void); 81void frontend_sig_handler(int, short, void *); 82void update_iface(struct if_msghdr *, struct sockaddr_dl *); 83void frontend_startup(void); 84void init_ifaces(void); 85void route_receive(int, short, void *); 86void handle_route_message(struct rt_msghdr *, struct sockaddr **); 87void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 88void bpf_receive(int, short, void *); 89int get_flags(char *); 90int get_xflags(char *); 91struct iface *get_iface_by_id(uint32_t); 92void remove_iface(uint32_t); 93void set_bpfsock(int, uint32_t); 94void iface_data_from_imsg(struct iface*, struct imsg_req_dhcp *); 95ssize_t build_packet(uint8_t, char *, uint32_t, struct ether_addr *, 96 struct in_addr *, struct in_addr *, struct in_addr *); 97void send_packet(uint8_t, struct iface *); 98void bpf_send_packet(struct iface *, uint8_t *, ssize_t); 99int udp_send_packet(struct iface *, uint8_t *, ssize_t); 100#ifndef SMALL 101int iface_conf_cmp(struct iface_conf *, struct iface_conf *); 102#endif /* SMALL */ 103 104LIST_HEAD(, iface) interfaces; 105struct dhcpleased_conf *frontend_conf; 106static struct imsgev *iev_main; 107static struct imsgev *iev_engine; 108struct event ev_route; 109int ioctlsock; 110 111uint8_t dhcp_packet[1500]; 112 113void 114frontend_sig_handler(int sig, short event, void *bula) 115{ 116 /* 117 * Normal signal handler rules don't apply because libevent 118 * decouples for us. 119 */ 120 121 switch (sig) { 122 case SIGINT: 123 case SIGTERM: 124 frontend_shutdown(); 125 default: 126 fatalx("unexpected signal"); 127 } 128} 129 130void 131frontend(int debug, int verbose) 132{ 133 struct event ev_sigint, ev_sigterm; 134 struct passwd *pw; 135 136#ifndef SMALL 137 frontend_conf = config_new_empty(); 138#endif /* SMALL */ 139 140 log_init(debug, LOG_DAEMON); 141 log_setverbose(verbose); 142 143 if ((pw = getpwnam(DHCPLEASED_USER)) == NULL) 144 fatal("getpwnam"); 145 146 if (chdir("/") == -1) 147 fatal("chdir(\"/\")"); 148 149 if (unveil("/", "") == -1) 150 fatal("unveil /"); 151 if (unveil(NULL, NULL) == -1) 152 fatal("unveil"); 153 154 setproctitle("%s", "frontend"); 155 log_procinit("frontend"); 156 157 if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) 158 fatal("socket"); 159 160 if (setgroups(1, &pw->pw_gid) || 161 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 162 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 163 fatal("can't drop privileges"); 164 165 if (pledge("stdio unix recvfd route", NULL) == -1) 166 fatal("pledge"); 167 event_init(); 168 169 /* Setup signal handler. */ 170 signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL); 171 signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL); 172 signal_add(&ev_sigint, NULL); 173 signal_add(&ev_sigterm, NULL); 174 signal(SIGPIPE, SIG_IGN); 175 signal(SIGHUP, SIG_IGN); 176 177 /* Setup pipe and event handler to the parent process. */ 178 if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) 179 fatal(NULL); 180 imsg_init(&iev_main->ibuf, 3); 181 iev_main->handler = frontend_dispatch_main; 182 iev_main->events = EV_READ; 183 event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, 184 iev_main->handler, iev_main); 185 event_add(&iev_main->ev, NULL); 186 187 LIST_INIT(&interfaces); 188 event_dispatch(); 189 190 frontend_shutdown(); 191} 192 193__dead void 194frontend_shutdown(void) 195{ 196 /* Close pipes. */ 197 msgbuf_write(&iev_engine->ibuf.w); 198 msgbuf_clear(&iev_engine->ibuf.w); 199 close(iev_engine->ibuf.fd); 200 msgbuf_write(&iev_main->ibuf.w); 201 msgbuf_clear(&iev_main->ibuf.w); 202 close(iev_main->ibuf.fd); 203 204#ifndef SMALL 205 config_clear(frontend_conf); 206#endif /* SMALL */ 207 208 free(iev_engine); 209 free(iev_main); 210 211 log_info("frontend exiting"); 212 exit(0); 213} 214 215int 216frontend_imsg_compose_main(int type, pid_t pid, void *data, 217 uint16_t datalen) 218{ 219 return (imsg_compose_event(iev_main, type, 0, pid, -1, data, 220 datalen)); 221} 222 223int 224frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid, 225 void *data, uint16_t datalen) 226{ 227 return (imsg_compose_event(iev_engine, type, peerid, pid, -1, 228 data, datalen)); 229} 230 231void 232frontend_dispatch_main(int fd, short event, void *bula) 233{ 234 static struct dhcpleased_conf *nconf; 235 static struct iface_conf *iface_conf; 236 struct imsg imsg; 237 struct imsgev *iev = bula; 238 struct imsgbuf *ibuf = &iev->ibuf; 239 struct iface *iface; 240 ssize_t n; 241 int shut = 0, bpfsock, if_index, udpsock; 242 243 if (event & EV_READ) { 244 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 245 fatal("imsg_read error"); 246 if (n == 0) /* Connection closed. */ 247 shut = 1; 248 } 249 if (event & EV_WRITE) { 250 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 251 fatal("msgbuf_write"); 252 if (n == 0) /* Connection closed. */ 253 shut = 1; 254 } 255 256 for (;;) { 257 if ((n = imsg_get(ibuf, &imsg)) == -1) 258 fatal("%s: imsg_get error", __func__); 259 if (n == 0) /* No more messages. */ 260 break; 261 262 switch (imsg.hdr.type) { 263 case IMSG_SOCKET_IPC: 264 /* 265 * Setup pipe and event handler to the engine 266 * process. 267 */ 268 if (iev_engine) 269 fatalx("%s: received unexpected imsg fd " 270 "to frontend", __func__); 271 272 if ((fd = imsg_get_fd(&imsg)) == -1) 273 fatalx("%s: expected to receive imsg fd to " 274 "frontend but didn't receive any", 275 __func__); 276 277 iev_engine = malloc(sizeof(struct imsgev)); 278 if (iev_engine == NULL) 279 fatal(NULL); 280 281 imsg_init(&iev_engine->ibuf, fd); 282 iev_engine->handler = frontend_dispatch_engine; 283 iev_engine->events = EV_READ; 284 285 event_set(&iev_engine->ev, iev_engine->ibuf.fd, 286 iev_engine->events, iev_engine->handler, iev_engine); 287 event_add(&iev_engine->ev, NULL); 288 break; 289 case IMSG_BPFSOCK: 290 if ((bpfsock = imsg_get_fd(&imsg)) == -1) 291 fatalx("%s: expected to receive imsg " 292 "bpf fd but didn't receive any", 293 __func__); 294 if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 295 fatalx("%s: IMSG_BPFSOCK wrong length: " 296 "%lu", __func__, IMSG_DATA_SIZE(imsg)); 297 memcpy(&if_index, imsg.data, sizeof(if_index)); 298 set_bpfsock(bpfsock, if_index); 299 break; 300 case IMSG_UDPSOCK: 301 if ((udpsock = imsg_get_fd(&imsg)) == -1) 302 fatalx("%s: expected to receive imsg " 303 "udpsocket fd but didn't receive any", 304 __func__); 305 if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 306 fatalx("%s: IMSG_UDPSOCK wrong length: " 307 "%lu", __func__, IMSG_DATA_SIZE(imsg)); 308 memcpy(&if_index, imsg.data, sizeof(if_index)); 309 if ((iface = get_iface_by_id(if_index)) == NULL) { 310 close(fd); 311 break; 312 } 313 if (iface->udpsock != -1) 314 fatalx("%s: received unexpected udpsocket", 315 __func__); 316 iface->udpsock = udpsock; 317 break; 318 case IMSG_CLOSE_UDPSOCK: 319 if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) 320 fatalx("%s: IMSG_UDPSOCK wrong length: " 321 "%lu", __func__, IMSG_DATA_SIZE(imsg)); 322 memcpy(&if_index, imsg.data, sizeof(if_index)); 323 if ((iface = get_iface_by_id(if_index)) != NULL && 324 iface->udpsock != -1) { 325 close(iface->udpsock); 326 iface->udpsock = -1; 327 } 328 break; 329 case IMSG_ROUTESOCK: 330 if ((fd = imsg_get_fd(&imsg)) == -1) 331 fatalx("%s: expected to receive imsg " 332 "routesocket fd but didn't receive any", 333 __func__); 334 event_set(&ev_route, fd, EV_READ | EV_PERSIST, 335 route_receive, NULL); 336 break; 337 case IMSG_STARTUP: 338 frontend_startup(); 339 break; 340#ifndef SMALL 341 case IMSG_RECONF_CONF: 342 if (nconf != NULL) 343 fatalx("%s: IMSG_RECONF_CONF already in " 344 "progress", __func__); 345 if ((nconf = malloc(sizeof(struct dhcpleased_conf))) == 346 NULL) 347 fatal(NULL); 348 SIMPLEQ_INIT(&nconf->iface_list); 349 break; 350 case IMSG_RECONF_IFACE: 351 if (IMSG_DATA_SIZE(imsg) != sizeof(struct 352 iface_conf)) 353 fatalx("%s: IMSG_RECONF_IFACE wrong length: " 354 "%lu", __func__, IMSG_DATA_SIZE(imsg)); 355 if ((iface_conf = malloc(sizeof(struct iface_conf))) 356 == NULL) 357 fatal(NULL); 358 memcpy(iface_conf, imsg.data, sizeof(struct 359 iface_conf)); 360 iface_conf->vc_id = NULL; 361 iface_conf->vc_id_len = 0; 362 iface_conf->c_id = NULL; 363 iface_conf->c_id_len = 0; 364 iface_conf->h_name = NULL; 365 SIMPLEQ_INSERT_TAIL(&nconf->iface_list, 366 iface_conf, entry); 367 break; 368 case IMSG_RECONF_VC_ID: 369 if (iface_conf == NULL) 370 fatal("IMSG_RECONF_VC_ID without " 371 "IMSG_RECONF_IFACE"); 372 if (IMSG_DATA_SIZE(imsg) > 255 + 2) 373 fatalx("%s: IMSG_RECONF_VC_ID wrong length: " 374 "%lu", __func__, IMSG_DATA_SIZE(imsg)); 375 if ((iface_conf->vc_id = malloc(IMSG_DATA_SIZE(imsg))) 376 == NULL) 377 fatal(NULL); 378 memcpy(iface_conf->vc_id, imsg.data, 379 IMSG_DATA_SIZE(imsg)); 380 iface_conf->vc_id_len = IMSG_DATA_SIZE(imsg); 381 break; 382 case IMSG_RECONF_C_ID: 383 if (iface_conf == NULL) 384 fatal("IMSG_RECONF_C_ID without " 385 "IMSG_RECONF_IFACE"); 386 if (IMSG_DATA_SIZE(imsg) > 255 + 2) 387 fatalx("%s: IMSG_RECONF_C_ID wrong length: " 388 "%lu", __func__, IMSG_DATA_SIZE(imsg)); 389 if ((iface_conf->c_id = malloc(IMSG_DATA_SIZE(imsg))) 390 == NULL) 391 fatal(NULL); 392 memcpy(iface_conf->c_id, imsg.data, 393 IMSG_DATA_SIZE(imsg)); 394 iface_conf->c_id_len = IMSG_DATA_SIZE(imsg); 395 break; 396 case IMSG_RECONF_H_NAME: 397 if (iface_conf == NULL) 398 fatal("IMSG_RECONF_H_NAME without " 399 "IMSG_RECONF_IFACE"); 400 if (((char *)imsg.data)[IMSG_DATA_SIZE(imsg) - 1] != 401 '\0') 402 fatalx("Invalid hostname"); 403 if (IMSG_DATA_SIZE(imsg) > 256) 404 fatalx("Invalid hostname"); 405 if ((iface_conf->h_name = strdup(imsg.data)) == NULL) 406 fatal(NULL); 407 break; 408 case IMSG_RECONF_END: { 409 int i; 410 int *ifaces; 411 char ifnamebuf[IF_NAMESIZE], *if_name; 412 413 if (nconf == NULL) 414 fatalx("%s: IMSG_RECONF_END without " 415 "IMSG_RECONF_CONF", __func__); 416 417 ifaces = changed_ifaces(frontend_conf, nconf); 418 merge_config(frontend_conf, nconf); 419 nconf = NULL; 420 for (i = 0; ifaces[i] != 0; i++) { 421 if_index = ifaces[i]; 422 if_name = if_indextoname(if_index, ifnamebuf); 423 log_debug("changed iface: %s[%d]", if_name != 424 NULL ? if_name : "<unknown>", if_index); 425 frontend_imsg_compose_engine( 426 IMSG_REQUEST_REBOOT, 0, 0, &if_index, 427 sizeof(if_index)); 428 } 429 free(ifaces); 430 break; 431 } 432 case IMSG_CONTROLFD: 433 if ((fd = imsg_get_fd(&imsg)) == -1) 434 fatalx("%s: expected to receive imsg " 435 "control fd but didn't receive any", 436 __func__); 437 /* Listen on control socket. */ 438 control_listen(fd); 439 break; 440 case IMSG_CTL_END: 441 control_imsg_relay(&imsg); 442 break; 443#endif /* SMALL */ 444 default: 445 log_debug("%s: error handling imsg %d", __func__, 446 imsg.hdr.type); 447 break; 448 } 449 imsg_free(&imsg); 450 } 451 if (!shut) 452 imsg_event_add(iev); 453 else { 454 /* This pipe is dead. Remove its event handler. */ 455 event_del(&iev->ev); 456 event_loopexit(NULL); 457 } 458} 459 460void 461frontend_dispatch_engine(int fd, short event, void *bula) 462{ 463 struct imsgev *iev = bula; 464 struct imsgbuf *ibuf = &iev->ibuf; 465 struct imsg imsg; 466 struct iface *iface; 467 ssize_t n; 468 int shut = 0; 469 470 if (event & EV_READ) { 471 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 472 fatal("imsg_read error"); 473 if (n == 0) /* Connection closed. */ 474 shut = 1; 475 } 476 if (event & EV_WRITE) { 477 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 478 fatal("msgbuf_write"); 479 if (n == 0) /* Connection closed. */ 480 shut = 1; 481 } 482 483 for (;;) { 484 if ((n = imsg_get(ibuf, &imsg)) == -1) 485 fatal("%s: imsg_get error", __func__); 486 if (n == 0) /* No more messages. */ 487 break; 488 489 switch (imsg.hdr.type) { 490#ifndef SMALL 491 case IMSG_CTL_END: 492 case IMSG_CTL_SHOW_INTERFACE_INFO: 493 control_imsg_relay(&imsg); 494 break; 495#endif /* SMALL */ 496 case IMSG_SEND_DISCOVER: { 497 struct imsg_req_dhcp imsg_req_dhcp; 498 if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_dhcp)) 499 fatalx("%s: IMSG_SEND_DISCOVER wrong " 500 "length: %lu", __func__, 501 IMSG_DATA_SIZE(imsg)); 502 memcpy(&imsg_req_dhcp, imsg.data, 503 sizeof(imsg_req_dhcp)); 504 505 iface = get_iface_by_id(imsg_req_dhcp.if_index); 506 507 if (iface == NULL) 508 break; 509 510 iface_data_from_imsg(iface, &imsg_req_dhcp); 511 send_packet(DHCPDISCOVER, iface); 512 break; 513 } 514 case IMSG_SEND_REQUEST: { 515 struct imsg_req_dhcp imsg_req_dhcp; 516 if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_dhcp)) 517 fatalx("%s: IMSG_SEND_REQUEST wrong " 518 "length: %lu", __func__, 519 IMSG_DATA_SIZE(imsg)); 520 memcpy(&imsg_req_dhcp, imsg.data, 521 sizeof(imsg_req_dhcp)); 522 523 iface = get_iface_by_id(imsg_req_dhcp.if_index); 524 525 if (iface == NULL) 526 break; 527 528 iface_data_from_imsg(iface, &imsg_req_dhcp); 529 send_packet(DHCPREQUEST, iface); 530 break; 531 } 532 default: 533 log_debug("%s: error handling imsg %d", __func__, 534 imsg.hdr.type); 535 break; 536 } 537 imsg_free(&imsg); 538 } 539 if (!shut) 540 imsg_event_add(iev); 541 else { 542 /* This pipe is dead. Remove its event handler. */ 543 event_del(&iev->ev); 544 event_loopexit(NULL); 545 } 546} 547 548int 549get_flags(char *if_name) 550{ 551 struct ifreq ifr; 552 553 strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 554 if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) { 555 log_warn("SIOCGIFFLAGS"); 556 return -1; 557 } 558 return ifr.ifr_flags; 559} 560 561int 562get_xflags(char *if_name) 563{ 564 struct ifreq ifr; 565 566 strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); 567 if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) { 568 log_warn("SIOCGIFXFLAGS"); 569 return -1; 570 } 571 return ifr.ifr_flags; 572} 573 574void 575update_iface(struct if_msghdr *ifm, struct sockaddr_dl *sdl) 576{ 577 struct iface *iface; 578 struct imsg_ifinfo ifinfo; 579 uint32_t if_index; 580 int flags, xflags; 581 char ifnamebuf[IF_NAMESIZE], *if_name; 582 583 if_index = ifm->ifm_index; 584 585 flags = ifm->ifm_flags; 586 xflags = ifm->ifm_xflags; 587 588 iface = get_iface_by_id(if_index); 589 if_name = if_indextoname(if_index, ifnamebuf); 590 591 if (if_name == NULL) { 592 if (iface != NULL) { 593 log_debug("interface with idx %d removed", if_index); 594 frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 595 &if_index, sizeof(if_index)); 596 remove_iface(if_index); 597 } 598 return; 599 } 600 601 if (!(xflags & IFXF_AUTOCONF4)) { 602 if (iface != NULL) { 603 log_info("Removed autoconf flag from %s", if_name); 604 frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 605 &if_index, sizeof(if_index)); 606 remove_iface(if_index); 607 } 608 return; 609 } 610 611 memset(&ifinfo, 0, sizeof(ifinfo)); 612 ifinfo.if_index = if_index; 613 ifinfo.link_state = ifm->ifm_data.ifi_link_state; 614 ifinfo.rdomain = ifm->ifm_tableid; 615 ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == 616 (IFF_UP | IFF_RUNNING); 617 618 if (sdl != NULL && (sdl->sdl_type == IFT_ETHER || 619 sdl->sdl_type == IFT_CARP) && sdl->sdl_alen == ETHER_ADDR_LEN) 620 memcpy(ifinfo.hw_address.ether_addr_octet, LLADDR(sdl), 621 ETHER_ADDR_LEN); 622 else if (iface == NULL) { 623 log_warnx("Could not find AF_LINK address for %s.", if_name); 624 return; 625 } 626 627 if (iface == NULL) { 628 if ((iface = calloc(1, sizeof(*iface))) == NULL) 629 fatal("calloc"); 630 iface->udpsock = -1; 631 LIST_INSERT_HEAD(&interfaces, iface, entries); 632 frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0, 633 &if_index, sizeof(if_index)); 634 } else { 635 if (iface->ifinfo.rdomain != ifinfo.rdomain && 636 iface->udpsock != -1) { 637 close(iface->udpsock); 638 iface->udpsock = -1; 639 } 640 } 641 642 if (memcmp(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)) != 0) { 643 memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); 644 frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo, 645 sizeof(iface->ifinfo)); 646 } 647} 648 649void 650frontend_startup(void) 651{ 652 if (!event_initialized(&ev_route)) 653 fatalx("%s: did not receive a route socket from the main " 654 "process", __func__); 655 656 init_ifaces(); 657 if (pledge("stdio unix recvfd", NULL) == -1) 658 fatal("pledge"); 659 event_add(&ev_route, NULL); 660} 661 662void 663init_ifaces(void) 664{ 665 struct iface *iface; 666 struct imsg_ifinfo ifinfo; 667 struct if_nameindex *ifnidxp, *ifnidx; 668 struct ifaddrs *ifap, *ifa; 669 uint32_t if_index; 670 int flags, xflags; 671 char *if_name; 672 673 if ((ifnidxp = if_nameindex()) == NULL) 674 fatalx("if_nameindex"); 675 676 if (getifaddrs(&ifap) != 0) 677 fatal("getifaddrs"); 678 679 for (ifnidx = ifnidxp; ifnidx->if_index != 0 && ifnidx->if_name != NULL; 680 ifnidx++) { 681 if_index = ifnidx->if_index; 682 if_name = ifnidx->if_name; 683 if ((flags = get_flags(if_name)) == -1) 684 continue; 685 if ((xflags = get_xflags(if_name)) == -1) 686 continue; 687 if (!(xflags & IFXF_AUTOCONF4)) 688 continue; 689 690 memset(&ifinfo, 0, sizeof(ifinfo)); 691 ifinfo.if_index = if_index; 692 ifinfo.link_state = -1; 693 ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == 694 (IFF_UP | IFF_RUNNING); 695 696 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 697 if (strcmp(if_name, ifa->ifa_name) != 0) 698 continue; 699 if (ifa->ifa_addr == NULL) 700 continue; 701 702 switch (ifa->ifa_addr->sa_family) { 703 case AF_LINK: { 704 struct if_data *if_data; 705 struct sockaddr_dl *sdl; 706 707 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 708 if ((sdl->sdl_type != IFT_ETHER && 709 sdl->sdl_type != IFT_CARP) || 710 sdl->sdl_alen != ETHER_ADDR_LEN) 711 continue; 712 memcpy(ifinfo.hw_address.ether_addr_octet, 713 LLADDR(sdl), ETHER_ADDR_LEN); 714 715 if_data = (struct if_data *)ifa->ifa_data; 716 ifinfo.link_state = if_data->ifi_link_state; 717 ifinfo.rdomain = if_data->ifi_rdomain; 718 goto out; 719 } 720 default: 721 break; 722 } 723 } 724 out: 725 if (ifinfo.link_state == -1) 726 /* no AF_LINK found */ 727 continue; 728 729 if ((iface = calloc(1, sizeof(*iface))) == NULL) 730 fatal("calloc"); 731 iface->udpsock = -1; 732 memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); 733 LIST_INSERT_HEAD(&interfaces, iface, entries); 734 frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0, 735 &if_index, sizeof(if_index)); 736 frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo, 737 sizeof(iface->ifinfo)); 738 } 739 740 freeifaddrs(ifap); 741 if_freenameindex(ifnidxp); 742} 743 744void 745route_receive(int fd, short events, void *arg) 746{ 747 static uint8_t *buf; 748 749 struct rt_msghdr *rtm; 750 struct sockaddr *sa, *rti_info[RTAX_MAX]; 751 ssize_t n; 752 753 if (buf == NULL) { 754 buf = malloc(ROUTE_SOCKET_BUF_SIZE); 755 if (buf == NULL) 756 fatal("malloc"); 757 } 758 rtm = (struct rt_msghdr *)buf; 759 if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) { 760 if (errno == EAGAIN || errno == EINTR) 761 return; 762 log_warn("dispatch_rtmsg: read error"); 763 return; 764 } 765 766 if (n == 0) 767 fatal("routing socket closed"); 768 769 if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) { 770 log_warnx("partial rtm of %zd in buffer", n); 771 return; 772 } 773 774 if (rtm->rtm_version != RTM_VERSION) 775 return; 776 777 sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen); 778 get_rtaddrs(rtm->rtm_addrs, sa, rti_info); 779 780 handle_route_message(rtm, rti_info); 781} 782 783void 784handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info) 785{ 786 struct sockaddr_dl *sdl = NULL; 787 struct if_announcemsghdr *ifan; 788 uint32_t if_index; 789 790 switch (rtm->rtm_type) { 791 case RTM_IFINFO: 792 if (rtm->rtm_addrs & RTA_IFP && rti_info[RTAX_IFP]->sa_family 793 == AF_LINK) 794 sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP]; 795 update_iface((struct if_msghdr *)rtm, sdl); 796 break; 797 case RTM_IFANNOUNCE: 798 ifan = (struct if_announcemsghdr *)rtm; 799 if_index = ifan->ifan_index; 800 if (ifan->ifan_what == IFAN_DEPARTURE) { 801 frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, 802 &if_index, sizeof(if_index)); 803 remove_iface(if_index); 804 } 805 break; 806 case RTM_PROPOSAL: 807 if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) { 808 log_debug("RTP_PROPOSAL_SOLICIT"); 809 frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS, 810 0, 0, NULL, 0); 811 } 812#ifndef SMALL 813 else if (rtm->rtm_flags & RTF_PROTO3) { 814 char ifnamebuf[IF_NAMESIZE], *if_name; 815 816 if_index = rtm->rtm_index; 817 if_name = if_indextoname(if_index, ifnamebuf); 818 log_warnx("\"dhclient %s\" ran, requesting new lease", 819 if_name != NULL ? if_name : "(unknown)"); 820 frontend_imsg_compose_engine(IMSG_REQUEST_REBOOT, 821 0, 0, &if_index, sizeof(if_index)); 822 } 823#endif /* SMALL */ 824 break; 825 default: 826 log_debug("unexpected RTM: %d", rtm->rtm_type); 827 break; 828 } 829} 830 831#define ROUNDUP(a) \ 832 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 833 834void 835get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 836{ 837 int i; 838 839 for (i = 0; i < RTAX_MAX; i++) { 840 if (addrs & (1 << i)) { 841 rti_info[i] = sa; 842 sa = (struct sockaddr *)((char *)(sa) + 843 ROUNDUP(sa->sa_len)); 844 } else 845 rti_info[i] = NULL; 846 } 847} 848 849void 850bpf_receive(int fd, short events, void *arg) 851{ 852 struct bpf_hdr *hdr; 853 struct imsg_dhcp imsg_dhcp; 854 struct iface *iface; 855 ssize_t len, rem; 856 uint8_t *p; 857 858 iface = (struct iface *)arg; 859 860 if ((len = read(fd, iface->bpfev.buf, BPFLEN)) == -1) { 861 log_warn("%s: read", __func__); 862 return; 863 } 864 865 if (len == 0) 866 fatal("%s len == 0", __func__); 867 868 memset(&imsg_dhcp, 0, sizeof(imsg_dhcp)); 869 imsg_dhcp.if_index = iface->ifinfo.if_index; 870 871 rem = len; 872 p = iface->bpfev.buf; 873 874 while (rem > 0) { 875 if ((size_t)rem < sizeof(*hdr)) { 876 log_warnx("packet too short"); 877 return; 878 } 879 hdr = (struct bpf_hdr *)p; 880 if (hdr->bh_caplen != hdr->bh_datalen) { 881 log_warnx("skipping truncated packet"); 882 goto cont; 883 } 884 if (rem < hdr->bh_hdrlen + hdr->bh_caplen) 885 /* we are done */ 886 break; 887 if (hdr->bh_caplen > sizeof(imsg_dhcp.packet)) { 888 log_warn("packet too big"); 889 goto cont; 890 } 891 memcpy(&imsg_dhcp.packet, p + hdr->bh_hdrlen, hdr->bh_caplen); 892 imsg_dhcp.len = hdr->bh_caplen; 893 imsg_dhcp.csumflags = hdr->bh_csumflags; 894 frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp, 895 sizeof(imsg_dhcp)); 896 cont: 897 p += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 898 rem -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 899 900 } 901} 902 903void 904iface_data_from_imsg(struct iface* iface, struct imsg_req_dhcp *imsg) 905{ 906 iface->xid = imsg->xid; 907 iface->ciaddr = imsg->ciaddr; 908 iface->requested_ip = imsg->requested_ip; 909 iface->server_identifier = imsg->server_identifier; 910 iface->dhcp_server = imsg->dhcp_server; 911} 912 913ssize_t 914build_packet(uint8_t message_type, char *if_name, uint32_t xid, 915 struct ether_addr *hw_address, struct in_addr *ciaddr, struct in_addr 916 *requested_ip, struct in_addr *server_identifier) 917{ 918 static uint8_t dhcp_cookie[] = DHCP_COOKIE; 919 static uint8_t dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1, 920 DHCPDISCOVER}; 921 static uint8_t dhcp_hostname[255 + 2] = {DHO_HOST_NAME, 0 /*, ... */}; 922 static uint8_t dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7, 923 HTYPE_ETHER, 0, 0, 0, 0, 0, 0}; 924 static uint8_t dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST, 925 8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS, 926 DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS, 927 DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES}; 928 static uint8_t dhcp_req_list_v6[] = {DHO_DHCP_PARAMETER_REQUEST_LIST, 929 9, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS, 930 DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS, 931 DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES, 932 DHO_IPV6_ONLY_PREFERRED}; 933 static uint8_t dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS, 934 4, 0, 0, 0, 0}; 935 static uint8_t dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER, 936 4, 0, 0, 0, 0}; 937#ifndef SMALL 938 struct iface_conf *iface_conf; 939#endif /* SMALL */ 940 struct dhcp_hdr *hdr; 941 ssize_t len; 942 uint8_t *p; 943 char *c; 944 945#ifndef SMALL 946 iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name); 947#endif /* SMALL */ 948 949 memset(dhcp_packet, 0, sizeof(dhcp_packet)); 950 dhcp_message_type[2] = message_type; 951 p = dhcp_packet; 952 hdr = (struct dhcp_hdr *)p; 953 hdr->op = DHCP_BOOTREQUEST; 954 hdr->htype = HTYPE_ETHER; 955 hdr->hlen = 6; 956 hdr->hops = 0; 957 hdr->xid = htonl(xid); 958 hdr->secs = 0; 959 hdr->ciaddr = *ciaddr; 960 memcpy(hdr->chaddr, hw_address, sizeof(*hw_address)); 961 p += sizeof(struct dhcp_hdr); 962 memcpy(p, dhcp_cookie, sizeof(dhcp_cookie)); 963 p += sizeof(dhcp_cookie); 964 memcpy(p, dhcp_message_type, sizeof(dhcp_message_type)); 965 p += sizeof(dhcp_message_type); 966 967#ifndef SMALL 968 if (iface_conf != NULL && iface_conf->h_name != NULL) { 969 if (iface_conf->h_name[0] != '\0') { 970 dhcp_hostname[1] = strlen(iface_conf->h_name); 971 memcpy(dhcp_hostname + 2, iface_conf->h_name, 972 strlen(iface_conf->h_name)); 973 memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2); 974 p += dhcp_hostname[1] + 2; 975 } 976 } else 977#endif /* SMALL */ 978 { 979 if (gethostname(dhcp_hostname + 2, 980 sizeof(dhcp_hostname) - 2) == 0 && 981 dhcp_hostname[2] != '\0') { 982 if ((c = strchr(dhcp_hostname + 2, '.')) != NULL) 983 *c = '\0'; 984 dhcp_hostname[1] = strlen(dhcp_hostname + 2); 985 memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2); 986 p += dhcp_hostname[1] + 2; 987 } 988 } 989 990#ifndef SMALL 991 if (iface_conf != NULL) { 992 if (iface_conf->c_id_len > 0) { 993 /* XXX check space */ 994 memcpy(p, iface_conf->c_id, iface_conf->c_id_len); 995 p += iface_conf->c_id_len; 996 } else { 997 memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address)); 998 memcpy(p, dhcp_client_id, sizeof(dhcp_client_id)); 999 p += sizeof(dhcp_client_id); 1000 } 1001 if (iface_conf->vc_id_len > 0) { 1002 /* XXX check space */ 1003 memcpy(p, iface_conf->vc_id, iface_conf->vc_id_len); 1004 p += iface_conf->vc_id_len; 1005 } 1006 if (iface_conf->prefer_ipv6) { 1007 memcpy(p, dhcp_req_list_v6, sizeof(dhcp_req_list_v6)); 1008 p += sizeof(dhcp_req_list_v6); 1009 1010 } else { 1011 memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); 1012 p += sizeof(dhcp_req_list); 1013 } 1014 } else 1015#endif /* SMALL */ 1016 { 1017 memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address)); 1018 memcpy(p, dhcp_client_id, sizeof(dhcp_client_id)); 1019 p += sizeof(dhcp_client_id); 1020 memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); 1021 p += sizeof(dhcp_req_list); 1022 } 1023 1024 if (requested_ip->s_addr != INADDR_ANY) { 1025 memcpy(dhcp_requested_address + 2, requested_ip, 1026 sizeof(*requested_ip)); 1027 memcpy(p, dhcp_requested_address, 1028 sizeof(dhcp_requested_address)); 1029 p += sizeof(dhcp_requested_address); 1030 } 1031 1032 if (server_identifier->s_addr != INADDR_ANY) { 1033 memcpy(dhcp_server_identifier + 2, server_identifier, 1034 sizeof(*server_identifier)); 1035 memcpy(p, dhcp_server_identifier, 1036 sizeof(dhcp_server_identifier)); 1037 p += sizeof(dhcp_server_identifier); 1038 } 1039 1040 *p = DHO_END; 1041 p += 1; 1042 1043 len = p - dhcp_packet; 1044 1045 /* dhcp_packet is initialized with DHO_PADs */ 1046 if (len < BOOTP_MIN_LEN) 1047 len = BOOTP_MIN_LEN; 1048 1049 return (len); 1050} 1051 1052void 1053send_packet(uint8_t message_type, struct iface *iface) 1054{ 1055 ssize_t pkt_len; 1056 char ifnamebuf[IF_NAMESIZE], *if_name; 1057 1058 if (!event_initialized(&iface->bpfev.ev)) { 1059 iface->send_discover = 1; 1060 return; 1061 } 1062 1063 iface->send_discover = 0; 1064 1065 if ((if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf)) == NULL) 1066 return; /* iface went away, nothing to do */ 1067 1068 log_debug("%s on %s", message_type == DHCPDISCOVER ? "DHCPDISCOVER" : 1069 "DHCPREQUEST", if_name); 1070 1071 pkt_len = build_packet(message_type, if_name, iface->xid, 1072 &iface->ifinfo.hw_address, &iface->ciaddr, &iface->requested_ip, 1073 &iface->server_identifier); 1074 if (iface->dhcp_server.s_addr != INADDR_ANY) { 1075 if (udp_send_packet(iface, dhcp_packet, pkt_len) == -1) 1076 bpf_send_packet(iface, dhcp_packet, pkt_len); 1077 } else 1078 bpf_send_packet(iface, dhcp_packet, pkt_len); 1079} 1080 1081int 1082udp_send_packet(struct iface *iface, uint8_t *packet, ssize_t len) 1083{ 1084 struct sockaddr_in to; 1085 1086 memset(&to, 0, sizeof(to)); 1087 to.sin_family = AF_INET; 1088 to.sin_len = sizeof(to); 1089 to.sin_addr = iface->dhcp_server; 1090 to.sin_port = ntohs(SERVER_PORT); 1091 1092 if (sendto(iface->udpsock, packet, len, 0, (struct sockaddr *)&to, 1093 sizeof(to)) == -1) { 1094 log_warn("sendto"); 1095 return -1; 1096 } 1097 return 0; 1098} 1099void 1100bpf_send_packet(struct iface *iface, uint8_t *packet, ssize_t len) 1101{ 1102 struct iovec iov[4]; 1103 struct ether_header eh; 1104 struct ip ip; 1105 struct udphdr udp; 1106 ssize_t total, result; 1107 int iovcnt = 0, i; 1108 1109 memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost)); 1110 memcpy(eh.ether_shost, &iface->ifinfo.hw_address, 1111 sizeof(eh.ether_dhost)); 1112 eh.ether_type = htons(ETHERTYPE_IP); 1113 iov[0].iov_base = &eh; 1114 iov[0].iov_len = sizeof(eh); 1115 iovcnt++; 1116 1117 ip.ip_v = 4; 1118 ip.ip_hl = 5; 1119 ip.ip_tos = IPTOS_LOWDELAY; 1120 ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); 1121 ip.ip_id = 0; 1122 ip.ip_off = 0; 1123 ip.ip_ttl = 128; 1124 ip.ip_p = IPPROTO_UDP; 1125 ip.ip_sum = 0; 1126 ip.ip_src.s_addr = INADDR_ANY; 1127 ip.ip_dst.s_addr = INADDR_BROADCAST; 1128 ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); 1129 iov[iovcnt].iov_base = &ip; 1130 iov[iovcnt].iov_len = sizeof(ip); 1131 iovcnt++; 1132 1133 udp.uh_sport = htons(CLIENT_PORT); 1134 udp.uh_dport = htons(SERVER_PORT); 1135 udp.uh_ulen = htons(sizeof(udp) + len); 1136 udp.uh_sum = 0; 1137 udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), 1138 checksum((unsigned char *)packet, len, 1139 checksum((unsigned char *)&ip.ip_src, 1140 2 * sizeof(ip.ip_src), 1141 IPPROTO_UDP + (uint32_t)ntohs(udp.uh_ulen))))); 1142 iov[iovcnt].iov_base = &udp; 1143 iov[iovcnt].iov_len = sizeof(udp); 1144 iovcnt++; 1145 1146 iov[iovcnt].iov_base = packet; 1147 iov[iovcnt].iov_len = len; 1148 iovcnt++; 1149 1150 total = 0; 1151 for (i = 0; i < iovcnt; i++) 1152 total += iov[i].iov_len; 1153 1154 result = writev(EVENT_FD(&iface->bpfev.ev), iov, iovcnt); 1155 if (result == -1) 1156 log_warn("%s: writev", __func__); 1157 else if (result < total) { 1158 log_warnx("%s, writev: %zd of %zd bytes", __func__, result, 1159 total); 1160 } 1161} 1162 1163struct iface* 1164get_iface_by_id(uint32_t if_index) 1165{ 1166 struct iface *iface; 1167 1168 LIST_FOREACH (iface, &interfaces, entries) { 1169 if (iface->ifinfo.if_index == if_index) 1170 return (iface); 1171 } 1172 1173 return (NULL); 1174} 1175 1176void 1177remove_iface(uint32_t if_index) 1178{ 1179 struct iface *iface; 1180 1181 iface = get_iface_by_id(if_index); 1182 1183 if (iface == NULL) 1184 return; 1185 1186 LIST_REMOVE(iface, entries); 1187 if (event_initialized(&iface->bpfev.ev)) { 1188 event_del(&iface->bpfev.ev); 1189 close(EVENT_FD(&iface->bpfev.ev)); 1190 } 1191 if (iface->udpsock != -1) 1192 close(iface->udpsock); 1193 free(iface); 1194} 1195 1196void 1197set_bpfsock(int bpfsock, uint32_t if_index) 1198{ 1199 struct iface *iface; 1200 1201 iface = get_iface_by_id(if_index); 1202 1203 if (iface == NULL) { 1204 /* 1205 * The interface disappeared while we were waiting for the 1206 * parent process to open the bpf socket. 1207 */ 1208 close(bpfsock); 1209 } else if (event_initialized(&iface->bpfev.ev)) { 1210 /* 1211 * The autoconf flag is flapping and we have multiple bpf sockets in 1212 * flight. We don't need this one because we already got one. 1213 */ 1214 close(bpfsock); 1215 } else { 1216 event_set(&iface->bpfev.ev, bpfsock, EV_READ | 1217 EV_PERSIST, bpf_receive, iface); 1218 event_add(&iface->bpfev.ev, NULL); 1219 if (iface->send_discover) 1220 send_packet(DHCPDISCOVER, iface); 1221 } 1222} 1223 1224#ifndef SMALL 1225struct iface_conf* 1226find_iface_conf(struct iface_conf_head *head, char *if_name) 1227{ 1228 struct iface_conf *iface_conf; 1229 1230 if (if_name == NULL) 1231 return (NULL); 1232 1233 SIMPLEQ_FOREACH(iface_conf, head, entry) { 1234 if (strcmp(iface_conf->name, if_name) == 0) 1235 return iface_conf; 1236 } 1237 return (NULL); 1238} 1239 1240int* 1241changed_ifaces(struct dhcpleased_conf *oconf, struct dhcpleased_conf *nconf) 1242{ 1243 struct iface_conf *iface_conf, *oiface_conf; 1244 int *ret, if_index, count = 0, i = 0; 1245 1246 /* 1247 * Worst case: All old interfaces replaced with new interfaces. 1248 * This should still be a small number 1249 */ 1250 SIMPLEQ_FOREACH(iface_conf, &oconf->iface_list, entry) 1251 count++; 1252 SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) 1253 count++; 1254 1255 ret = calloc(count + 1, sizeof(int)); 1256 1257 SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) { 1258 if ((if_index = if_nametoindex(iface_conf->name)) == 0) 1259 continue; 1260 oiface_conf = find_iface_conf(&oconf->iface_list, 1261 iface_conf->name); 1262 if (oiface_conf == NULL) { 1263 /* new interface added to config */ 1264 ret[i++] = if_index; 1265 } else if (iface_conf_cmp(iface_conf, oiface_conf) != 0) { 1266 /* interface conf changed */ 1267 ret[i++] = if_index; 1268 } 1269 } 1270 SIMPLEQ_FOREACH(oiface_conf, &oconf->iface_list, entry) { 1271 if ((if_index = if_nametoindex(oiface_conf->name)) == 0) 1272 continue; 1273 if (find_iface_conf(&nconf->iface_list, oiface_conf->name) == 1274 NULL) { 1275 /* interface removed from config */ 1276 ret[i++] = if_index; 1277 } 1278 } 1279 return ret; 1280} 1281 1282int 1283iface_conf_cmp(struct iface_conf *a, struct iface_conf *b) 1284{ 1285 if (a->vc_id_len != b->vc_id_len) 1286 return 1; 1287 if (memcmp(a->vc_id, b->vc_id, a->vc_id_len) != 0) 1288 return 1; 1289 if (a->c_id_len != b->c_id_len) 1290 return 1; 1291 if (memcmp(a->c_id, b->c_id, a->c_id_len) != 0) 1292 return 1; 1293 if (a->h_name == NULL || b->h_name == NULL) 1294 return 1; 1295 if (strcmp(a->h_name, b->h_name) != 0) 1296 return 1; 1297 if (a->ignore != b->ignore) 1298 return 1; 1299 if (a->ignore_servers_len != b->ignore_servers_len) 1300 return 1; 1301 if (memcmp(a->ignore_servers, b->ignore_servers, 1302 a->ignore_servers_len * sizeof (struct in_addr)) != 0) 1303 return 1; 1304 return 0; 1305} 1306#endif /* SMALL */ 1307