1 /* $NetBSD: discover.c,v 1.5 2022/04/03 01:10:58 christos Exp $ */ 2 3 /* discover.c 4 5 Find and identify the network interfaces. */ 6 7 /* 8 * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC") 9 * Copyright (c) 1995-2003 by Internet Software Consortium 10 * 11 * This Source Code Form is subject to the terms of the Mozilla Public 12 * License, v. 2.0. If a copy of the MPL was not distributed with this 13 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 * 23 * Internet Systems Consortium, Inc. 24 * PO Box 360 25 * Newmarket, NH 03857 USA 26 * <info@isc.org> 27 * https://www.isc.org/ 28 * 29 */ 30 31 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: discover.c,v 1.5 2022/04/03 01:10:58 christos Exp $"); 33 34 #include "dhcpd.h" 35 36 /* length of line we can read from the IF file, 256 is too small in some cases */ 37 #define IF_LINE_LENGTH 1024 38 39 #define BSD_COMP /* needed on Solaris for SIOCGLIFNUM */ 40 #include <sys/ioctl.h> 41 #include <errno.h> 42 43 #ifdef HAVE_NET_IF6_H 44 # include <net/if6.h> 45 #endif 46 47 struct interface_info *interfaces = 0; 48 struct interface_info *dummy_interfaces = 0; 49 struct interface_info *fallback_interface = 0; 50 51 int interfaces_invalidated; 52 int quiet_interface_discovery; 53 u_int16_t local_port = 0; 54 u_int16_t remote_port = 0; 55 u_int16_t relay_port = 0; 56 int dhcpv4_over_dhcpv6 = 0; 57 int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *); 58 int (*dhcp_interface_discovery_hook) (struct interface_info *); 59 isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *); 60 int (*dhcp_interface_shutdown_hook) (struct interface_info *); 61 62 struct in_addr limited_broadcast; 63 64 int local_family = AF_INET; 65 struct in_addr local_address; 66 67 #ifdef DHCPv6 68 /* 69 * Another clear abuse of the fact that undefined IP addresses are all zeroes. 70 */ 71 struct in6_addr local_address6; 72 int bind_local_address6 = 0; 73 #endif /* DHCPv6 */ 74 75 void (*bootp_packet_handler) (struct interface_info *, 76 struct dhcp_packet *, unsigned, 77 unsigned int, 78 struct iaddr, struct hardware *); 79 80 #ifdef DHCPv6 81 void (*dhcpv6_packet_handler)(struct interface_info *, 82 const char *, int, 83 int, const struct iaddr *, 84 isc_boolean_t); 85 #endif /* DHCPv6 */ 86 87 88 omapi_object_type_t *dhcp_type_interface; 89 #if defined (TRACING) 90 trace_type_t *interface_trace; 91 trace_type_t *inpacket_trace; 92 trace_type_t *outpacket_trace; 93 #endif 94 struct interface_info **interface_vector; 95 int interface_count; 96 int interface_max; 97 98 OMAPI_OBJECT_ALLOC (interface, struct interface_info, dhcp_type_interface) 99 100 isc_result_t interface_setup () 101 { 102 isc_result_t status; 103 status = omapi_object_type_register (&dhcp_type_interface, 104 "interface", 105 dhcp_interface_set_value, 106 dhcp_interface_get_value, 107 dhcp_interface_destroy, 108 dhcp_interface_signal_handler, 109 dhcp_interface_stuff_values, 110 dhcp_interface_lookup, 111 dhcp_interface_create, 112 dhcp_interface_remove, 113 0, 0, 0, 114 sizeof (struct interface_info), 115 interface_initialize, RC_MISC); 116 if (status != ISC_R_SUCCESS) 117 log_fatal ("Can't register interface object type: %s", 118 isc_result_totext (status)); 119 120 return status; 121 } 122 123 #if defined (TRACING) 124 void interface_trace_setup () 125 { 126 interface_trace = trace_type_register ("interface", (void *)0, 127 trace_interface_input, 128 trace_interface_stop, MDL); 129 inpacket_trace = trace_type_register ("inpacket", (void *)0, 130 trace_inpacket_input, 131 trace_inpacket_stop, MDL); 132 outpacket_trace = trace_type_register ("outpacket", (void *)0, 133 trace_outpacket_input, 134 trace_outpacket_stop, MDL); 135 } 136 #endif 137 138 isc_result_t interface_initialize (omapi_object_t *ipo, 139 const char *file, int line) 140 { 141 struct interface_info *ip = (struct interface_info *)ipo; 142 ip -> rfdesc = ip -> wfdesc = -1; 143 return ISC_R_SUCCESS; 144 } 145 146 147 /* 148 * Scanning for Interfaces 149 * ----------------------- 150 * 151 * To find interfaces, we create an iterator that abstracts out most 152 * of the platform specifics. Use is fairly straightforward: 153 * 154 * - begin_iface_scan() starts the process. 155 * - Use next_iface() until it returns 0. 156 * - end_iface_scan() performs any necessary cleanup. 157 * 158 * We check for errors on each call to next_iface(), which returns a 159 * description of the error as a string if any occurs. 160 * 161 * We currently have code for Solaris and Linux. Other systems need 162 * to have code written. 163 * 164 * NOTE: the long-term goal is to use the interface code from BIND 9. 165 */ 166 167 #if defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && defined(SIOCGLIFFLAGS) 168 169 /* HP/UX doesn't define struct lifconf, instead they define struct 170 * if_laddrconf. Similarly, 'struct lifreq' and 'struct lifaddrreq'. 171 */ 172 #ifdef ISC_PLATFORM_HAVEIF_LADDRCONF 173 # define lifc_len iflc_len 174 # define lifc_buf iflc_buf 175 # define lifc_req iflc_req 176 # define LIFCONF if_laddrconf 177 #else 178 # define ISC_HAVE_LIFC_FAMILY 1 179 # define ISC_HAVE_LIFC_FLAGS 1 180 # define LIFCONF lifconf 181 #endif 182 183 #ifdef ISC_PLATFORM_HAVEIF_LADDRREQ 184 # define lifr_addr iflr_addr 185 # define lifr_name iflr_name 186 # define lifr_dstaddr iflr_dstaddr 187 # define lifr_flags iflr_flags 188 # define sockaddr_storage sockaddr_ext 189 # define ss_family sa_family 190 # define LIFREQ if_laddrreq 191 #else 192 # define LIFREQ lifreq 193 #endif 194 195 #ifndef IF_NAMESIZE 196 # if defined(LIFNAMSIZ) 197 # define IF_NAMESIZE LIFNAMSIZ 198 # elif defined(IFNAMSIZ) 199 # define IF_NAMESIZE IFNAMSIZ 200 # else 201 # define IF_NAMESIZE 16 202 # endif 203 #endif 204 #elif !defined(__linux) && !defined(HAVE_IFADDRS_H) 205 # define SIOCGLIFCONF SIOCGIFCONF 206 # define SIOCGLIFFLAGS SIOCGIFFLAGS 207 # define LIFREQ ifreq 208 # define LIFCONF ifconf 209 # define lifr_name ifr_name 210 # define lifr_addr ifr_addr 211 # define lifr_flags ifr_flags 212 # define lifc_len ifc_len 213 # define lifc_buf ifc_buf 214 # define lifc_req ifc_req 215 #ifdef _AIX 216 # define ss_family __ss_family 217 #endif 218 #endif 219 220 #if defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) 221 /* 222 * Solaris support 223 * --------------- 224 * 225 * The SIOCGLIFCONF ioctl() are the extension that you need to use 226 * on Solaris to get information about IPv6 addresses. 227 * 228 * Solaris' extended interface is documented in the if_tcp man page. 229 */ 230 231 /* 232 * Structure holding state about the scan. 233 */ 234 struct iface_conf_list { 235 int sock; /* file descriptor used to get information */ 236 int num; /* total number of interfaces */ 237 struct LIFCONF conf; /* structure used to get information */ 238 int next; /* next interface to retrieve when iterating */ 239 }; 240 241 /* 242 * Structure used to return information about a specific interface. 243 */ 244 struct iface_info { 245 char name[IF_NAMESIZE+1]; /* name of the interface, e.g. "bge0" */ 246 struct sockaddr_storage addr; /* address information */ 247 isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */ 248 }; 249 250 /* 251 * Start a scan of interfaces. 252 * 253 * The iface_conf_list structure maintains state for this process. 254 */ 255 static int 256 begin_iface_scan(struct iface_conf_list *ifaces) { 257 #ifdef ISC_PLATFORM_HAVELIFNUM 258 struct lifnum lifnum; 259 #else 260 int lifnum; 261 #endif 262 263 ifaces->sock = socket(local_family, SOCK_DGRAM, IPPROTO_UDP); 264 if (ifaces->sock < 0) { 265 log_error("Error creating socket to list interfaces; %m"); 266 return 0; 267 } 268 269 memset(&lifnum, 0, sizeof(lifnum)); 270 #ifdef ISC_PLATFORM_HAVELIFNUM 271 lifnum.lifn_family = AF_UNSPEC; 272 #endif 273 #ifdef SIOCGLIFNUM 274 if (ioctl(ifaces->sock, SIOCGLIFNUM, &lifnum) < 0) { 275 log_error("Error finding total number of interfaces; %m"); 276 close(ifaces->sock); 277 ifaces->sock = -1; 278 return 0; 279 } 280 281 #ifdef ISC_PLATFORM_HAVELIFNUM 282 ifaces->num = lifnum.lifn_count; 283 #else 284 ifaces->num = lifnum; 285 #endif 286 #else 287 ifaces->num = 64; 288 #endif /* SIOCGLIFNUM */ 289 290 memset(&ifaces->conf, 0, sizeof(ifaces->conf)); 291 #ifdef ISC_HAVE_LIFC_FAMILY 292 ifaces->conf.lifc_family = AF_UNSPEC; 293 #endif 294 ifaces->conf.lifc_len = ifaces->num * sizeof(struct LIFREQ); 295 ifaces->conf.lifc_buf = dmalloc(ifaces->conf.lifc_len, MDL); 296 if (ifaces->conf.lifc_buf == NULL) { 297 log_fatal("Out of memory getting interface list."); 298 } 299 300 if (ioctl(ifaces->sock, SIOCGLIFCONF, &ifaces->conf) < 0) { 301 log_error("Error getting interfaces configuration list; %m"); 302 dfree(ifaces->conf.lifc_buf, MDL); 303 close(ifaces->sock); 304 ifaces->sock = -1; 305 return 0; 306 } 307 308 ifaces->next = 0; 309 310 return 1; 311 } 312 313 /* 314 * Retrieve the next interface. 315 * 316 * Returns information in the info structure. 317 * Sets err to 1 if there is an error, otherwise 0. 318 */ 319 static int 320 next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { 321 struct LIFREQ *p; 322 struct LIFREQ tmp; 323 isc_boolean_t foundif; 324 #if defined(sun) || defined(__linux) 325 /* Pointer used to remove interface aliases. */ 326 char *s; 327 #endif 328 329 do { 330 foundif = ISC_FALSE; 331 332 if (ifaces->next >= ifaces->num) { 333 *err = 0; 334 return 0; 335 } 336 337 p = ifaces->conf.lifc_req; 338 p += ifaces->next; 339 340 if (strlen(p->lifr_name) >= sizeof(info->name)) { 341 *err = 1; 342 log_error("Interface name '%s' too long", p->lifr_name); 343 return 0; 344 } 345 346 /* Reject if interface address family does not match */ 347 if (p->lifr_addr.ss_family != local_family) { 348 ifaces->next++; 349 continue; 350 } 351 352 memset(info, 0, sizeof(struct iface_info)); 353 strncpy(info->name, p->lifr_name, sizeof(info->name) - 1); 354 memcpy(&info->addr, &p->lifr_addr, sizeof(p->lifr_addr)); 355 356 #if defined(sun) || defined(__linux) 357 /* interface aliases look like "eth0:1" or "wlan1:3" */ 358 s = strchr(info->name, ':'); 359 if (s != NULL) { 360 *s = '\0'; 361 } 362 #endif /* defined(sun) || defined(__linux) */ 363 364 foundif = ISC_TRUE; 365 } while ((foundif == ISC_FALSE) || 366 (strncmp(info->name, "dummy", 5) == 0)); 367 368 memset(&tmp, 0, sizeof(tmp)); 369 strncpy(tmp.lifr_name, info->name, sizeof(tmp.lifr_name) - 1); 370 if (ioctl(ifaces->sock, SIOCGLIFFLAGS, &tmp) < 0) { 371 log_error("Error getting interface flags for '%s'; %m", 372 p->lifr_name); 373 *err = 1; 374 return 0; 375 } 376 info->flags = tmp.lifr_flags; 377 378 ifaces->next++; 379 *err = 0; 380 return 1; 381 } 382 383 /* 384 * End scan of interfaces. 385 */ 386 static void 387 end_iface_scan(struct iface_conf_list *ifaces) { 388 dfree(ifaces->conf.lifc_buf, MDL); 389 close(ifaces->sock); 390 ifaces->sock = -1; 391 } 392 393 #else 394 395 /* 396 * BSD/Linux support 397 * ----------- 398 * 399 * FreeBSD, NetBSD, OpenBSD, OS X/macOS and Linux all have the getifaddrs() 400 * function. 401 * 402 * The getifaddrs() man page describes the use. 403 */ 404 405 #include <ifaddrs.h> 406 407 /* 408 * Structure holding state about the scan. 409 */ 410 struct iface_conf_list { 411 struct ifaddrs *head; /* beginning of the list */ 412 struct ifaddrs *next; /* current position in the list */ 413 }; 414 415 /* 416 * Structure used to return information about a specific interface. 417 */ 418 struct iface_info { 419 char name[IFNAMSIZ]; /* name of the interface, e.g. "bge0" */ 420 struct sockaddr_storage addr; /* address information */ 421 isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */ 422 }; 423 424 /* 425 * Start a scan of interfaces. 426 * 427 * The iface_conf_list structure maintains state for this process. 428 */ 429 static int 430 begin_iface_scan(struct iface_conf_list *ifaces) { 431 if (getifaddrs(&ifaces->head) != 0) { 432 log_error("Error getting interfaces; %m"); 433 return 0; 434 } 435 ifaces->next = ifaces->head; 436 return 1; 437 } 438 439 /* 440 * Retrieve the next interface. 441 * 442 * Returns information in the info structure. 443 * Sets err to 1 if there is an error, otherwise 0. 444 */ 445 static int 446 next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { 447 size_t sa_len = 0; 448 449 if (ifaces->next == NULL) { 450 *err = 0; 451 return 0; 452 } 453 if (strlen(ifaces->next->ifa_name) >= sizeof(info->name)) { 454 log_error("Interface name '%s' too long", 455 ifaces->next->ifa_name); 456 *err = 1; 457 return 0; 458 } 459 memset(info, 0, sizeof(struct iface_info)); 460 strncpy(info->name, ifaces->next->ifa_name, sizeof(info->name) - 1); 461 memset(&info->addr, 0 , sizeof(info->addr)); 462 /* 463 * getifaddrs() can on Linux with some interfaces like PPP or TEQL 464 * result in a record with no address (ifa_addr). 465 */ 466 if (ifaces->next->ifa_addr != NULL) { 467 /* Linux lacks the sa_len member in struct sockaddr. */ 468 #if defined(__linux) 469 if (ifaces->next->ifa_addr->sa_family == AF_INET) 470 sa_len = sizeof(struct sockaddr_in); 471 else if (ifaces->next->ifa_addr->sa_family == AF_INET6) 472 sa_len = sizeof(struct sockaddr_in6); 473 #else 474 sa_len = ifaces->next->ifa_addr->sa_len; 475 #endif 476 memcpy(&info->addr, ifaces->next->ifa_addr, sa_len); 477 } 478 info->flags = ifaces->next->ifa_flags; 479 ifaces->next = ifaces->next->ifa_next; 480 *err = 0; 481 return 1; 482 } 483 484 /* 485 * End scan of interfaces. 486 */ 487 static void 488 end_iface_scan(struct iface_conf_list *ifaces) { 489 freeifaddrs(ifaces->head); 490 ifaces->head = NULL; 491 ifaces->next = NULL; 492 } 493 #endif 494 495 /* XXX: perhaps create drealloc() rather than do it manually */ 496 static void 497 add_ipv4_addr_to_interface(struct interface_info *iface, 498 const struct in_addr *addr) { 499 /* 500 * We don't expect a lot of addresses per IPv4 interface, so 501 * we use 4, as our "chunk size" for collecting addresses. 502 */ 503 if (iface->addresses == NULL) { 504 iface->addresses = dmalloc(4 * sizeof(struct in_addr), MDL); 505 if (iface->addresses == NULL) { 506 log_fatal("Out of memory saving IPv4 address " 507 "on interface."); 508 } 509 iface->address_count = 0; 510 iface->address_max = 4; 511 } else if (iface->address_count >= iface->address_max) { 512 struct in_addr *tmp; 513 int new_max; 514 515 new_max = iface->address_max + 4; 516 tmp = dmalloc(new_max * sizeof(struct in_addr), MDL); 517 if (tmp == NULL) { 518 log_fatal("Out of memory saving IPv4 address " 519 "on interface."); 520 } 521 memcpy(tmp, 522 iface->addresses, 523 iface->address_max * sizeof(struct in_addr)); 524 dfree(iface->addresses, MDL); 525 iface->addresses = tmp; 526 iface->address_max = new_max; 527 } 528 iface->addresses[iface->address_count++] = *addr; 529 } 530 531 #ifdef DHCPv6 532 /* XXX: perhaps create drealloc() rather than do it manually */ 533 static void 534 add_ipv6_addr_to_interface(struct interface_info *iface, 535 const struct in6_addr *addr) { 536 /* 537 * Each IPv6 interface will have at least two IPv6 addresses, 538 * and likely quite a few more. So we use 8, as our "chunk size" for 539 * collecting addresses. 540 */ 541 if (iface->v6addresses == NULL) { 542 iface->v6addresses = dmalloc(8 * sizeof(struct in6_addr), MDL); 543 if (iface->v6addresses == NULL) { 544 log_fatal("Out of memory saving IPv6 address " 545 "on interface."); 546 } 547 iface->v6address_count = 0; 548 iface->v6address_max = 8; 549 } else if (iface->v6address_count >= iface->v6address_max) { 550 struct in6_addr *tmp; 551 int new_max; 552 553 new_max = iface->v6address_max + 8; 554 tmp = dmalloc(new_max * sizeof(struct in6_addr), MDL); 555 if (tmp == NULL) { 556 log_fatal("Out of memory saving IPv6 address " 557 "on interface."); 558 } 559 memcpy(tmp, 560 iface->v6addresses, 561 iface->v6address_max * sizeof(struct in6_addr)); 562 dfree(iface->v6addresses, MDL); 563 iface->v6addresses = tmp; 564 iface->v6address_max = new_max; 565 } 566 iface->v6addresses[iface->v6address_count++] = *addr; 567 } 568 #endif /* DHCPv6 */ 569 570 /* Use the SIOCGIFCONF ioctl to get a list of all the attached interfaces. 571 For each interface that's of type INET and not the loopback interface, 572 register that interface with the network I/O software, figure out what 573 subnet it's on, and add it to the list of interfaces. */ 574 575 void 576 discover_interfaces(int state) { 577 struct iface_conf_list ifaces; 578 struct iface_info info; 579 int err; 580 581 struct interface_info *tmp; 582 struct interface_info *last, *next; 583 584 #ifdef DHCPv6 585 char abuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; 586 #endif /* DHCPv6 */ 587 588 589 struct subnet *subnet; 590 int ir; 591 isc_result_t status; 592 int wifcount = 0; 593 #ifdef RELAY_PORT 594 int updone = 0; 595 int downdone = 0; 596 #endif 597 598 static int setup_fallback = 0; 599 600 if (!begin_iface_scan(&ifaces)) { 601 log_fatal("Can't get list of interfaces."); 602 } 603 604 /* If we already have a list of interfaces, and we're running as 605 a DHCP server, the interfaces were requested. */ 606 if (interfaces && (state == DISCOVER_SERVER || 607 state == DISCOVER_RELAY || 608 state == DISCOVER_REQUESTED)) 609 ir = 0; 610 else if (state == DISCOVER_UNCONFIGURED) 611 ir = INTERFACE_REQUESTED | INTERFACE_AUTOMATIC; 612 else { 613 ir = INTERFACE_REQUESTED; 614 if (state == DISCOVER_RELAY && local_family == AF_INET) { 615 /* We're a v4 relay without specifically requested 616 * interfaces, so mark them all as bidirectional. */ 617 ir |= INTERFACE_STREAMS; 618 } 619 } 620 621 /* Cycle through the list of interfaces looking for IP addresses. */ 622 while (next_iface(&info, &err, &ifaces)) { 623 624 /* See if we've seen an interface that matches this one. */ 625 for (tmp = interfaces; tmp; tmp = tmp->next) { 626 if (!strcmp(tmp->name, info.name)) 627 break; 628 } 629 630 /* Skip non broadcast interfaces (plus loopback and 631 point-to-point in case an OS incorrectly marks them 632 as broadcast). Also skip down interfaces unless we're 633 trying to get a list of configurable interfaces. */ 634 if ((((local_family == AF_INET && 635 !(info.flags & IFF_BROADCAST)) || 636 #ifdef DHCPv6 637 (local_family == AF_INET6 && 638 !(info.flags & IFF_MULTICAST)) || 639 #endif 640 info.flags & IFF_LOOPBACK || 641 info.flags & IFF_POINTOPOINT) && !tmp) || 642 (!(info.flags & IFF_UP) && 643 state != DISCOVER_UNCONFIGURED)) 644 continue; 645 646 /* If there isn't already an interface by this name, 647 allocate one. */ 648 if (tmp == NULL) { 649 status = interface_allocate(&tmp, MDL); 650 if (status != ISC_R_SUCCESS) { 651 log_fatal("Error allocating interface %s: %s", 652 info.name, isc_result_totext(status)); 653 } 654 655 memcpy(tmp->name, info.name, sizeof(tmp->name)); 656 657 interface_snorf(tmp, ir); 658 interface_dereference(&tmp, MDL); 659 tmp = interfaces; /* XXX */ 660 } 661 662 if (dhcp_interface_discovery_hook) { 663 (*dhcp_interface_discovery_hook)(tmp); 664 } 665 666 if ((info.addr.ss_family == AF_INET) && 667 (local_family == AF_INET)) { 668 struct sockaddr_in *a = (struct sockaddr_in*)&info.addr; 669 struct iaddr addr; 670 671 /* We don't want the loopback interface. */ 672 if (a->sin_addr.s_addr == htonl(INADDR_LOOPBACK) && 673 ((tmp->flags & INTERFACE_AUTOMATIC) && 674 ((state == DISCOVER_SERVER) || 675 (state == DISCOVER_SERVER46)))) 676 continue; 677 678 /* If the only address we have is 0.0.0.0, we 679 shouldn't consider the interface configured. */ 680 if (a->sin_addr.s_addr != htonl(INADDR_ANY)) 681 tmp->configured = 1; 682 683 add_ipv4_addr_to_interface(tmp, &a->sin_addr); 684 685 /* invoke the setup hook */ 686 addr.len = 4; 687 memcpy(addr.iabuf, &a->sin_addr.s_addr, addr.len); 688 if (dhcp_interface_setup_hook) { 689 (*dhcp_interface_setup_hook)(tmp, &addr); 690 } 691 } 692 #ifdef DHCPv6 693 else if ((info.addr.ss_family == AF_INET6) && 694 (local_family == AF_INET6)) { 695 struct sockaddr_in6 *a = 696 (struct sockaddr_in6*)&info.addr; 697 struct iaddr addr; 698 699 /* We don't want the loopback interface. */ 700 if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr) && 701 ((tmp->flags & INTERFACE_AUTOMATIC) && 702 ((state == DISCOVER_SERVER) || 703 (state == DISCOVER_SERVER46)))) 704 continue; 705 706 /* If the only address we have is 0.0.0.0, we 707 shouldn't consider the interface configured. */ 708 if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr)) 709 tmp->configured = 1; 710 711 add_ipv6_addr_to_interface(tmp, &a->sin6_addr); 712 713 /* invoke the setup hook */ 714 addr.len = 16; 715 memcpy(addr.iabuf, &a->sin6_addr, addr.len); 716 if (dhcp_interface_setup_hook) { 717 (*dhcp_interface_setup_hook)(tmp, &addr); 718 } 719 } 720 #endif /* DHCPv6 */ 721 } 722 723 if (err) { 724 log_fatal("Error getting interface information."); 725 } 726 727 end_iface_scan(&ifaces); 728 729 730 /* Mock-up an 'ifp' structure which is no longer used in the 731 * new interface-sensing code, but is used in higher layers 732 * (for example to sense fallback interfaces). 733 */ 734 for (tmp = interfaces ; tmp != NULL ; tmp = tmp->next) { 735 if (tmp->ifp == NULL) { 736 struct ifreq *tif; 737 738 tif = (struct ifreq *)dmalloc(sizeof(struct ifreq), 739 MDL); 740 if (tif == NULL) 741 log_fatal("no space for ifp mockup."); 742 strcpy(tif->ifr_name, tmp->name); 743 tmp->ifp = tif; 744 } 745 } 746 747 748 /* If we're just trying to get a list of interfaces that we might 749 be able to configure, we can quit now. */ 750 if (state == DISCOVER_UNCONFIGURED) { 751 return; 752 } 753 754 /* Weed out the interfaces that did not have IP addresses. */ 755 tmp = last = next = NULL; 756 if (interfaces) 757 interface_reference (&tmp, interfaces, MDL); 758 while (tmp) { 759 if (next) 760 interface_dereference (&next, MDL); 761 if (tmp -> next) 762 interface_reference (&next, tmp -> next, MDL); 763 /* skip interfaces that are running already */ 764 if (tmp -> flags & INTERFACE_RUNNING) { 765 interface_dereference(&tmp, MDL); 766 if(next) 767 interface_reference(&tmp, next, MDL); 768 continue; 769 } 770 if ((tmp -> flags & INTERFACE_AUTOMATIC) && 771 state == DISCOVER_REQUESTED) 772 tmp -> flags &= ~(INTERFACE_AUTOMATIC | 773 INTERFACE_REQUESTED); 774 775 #ifdef DHCPv6 776 if (!(tmp->flags & INTERFACE_REQUESTED)) { 777 #else 778 if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) { 779 #endif /* DHCPv6 */ 780 if ((tmp -> flags & INTERFACE_REQUESTED) != ir) 781 log_fatal ("%s: not found", tmp -> name); 782 if (!last) { 783 if (interfaces) 784 interface_dereference (&interfaces, 785 MDL); 786 if (next) 787 interface_reference (&interfaces, next, MDL); 788 } else { 789 interface_dereference (&last -> next, MDL); 790 if (next) 791 interface_reference (&last -> next, 792 next, MDL); 793 } 794 if (tmp -> next) 795 interface_dereference (&tmp -> next, MDL); 796 797 /* Remember the interface in case we need to know 798 about it later. */ 799 if (dummy_interfaces) { 800 interface_reference (&tmp -> next, 801 dummy_interfaces, MDL); 802 interface_dereference (&dummy_interfaces, MDL); 803 } 804 interface_reference (&dummy_interfaces, tmp, MDL); 805 interface_dereference (&tmp, MDL); 806 if (next) 807 interface_reference (&tmp, next, MDL); 808 continue; 809 } 810 last = tmp; 811 812 /* We must have a subnet declaration for each interface. */ 813 if (!tmp->shared_network && (state == DISCOVER_SERVER)) { 814 log_error("%s", ""); 815 if (local_family == AF_INET) { 816 log_error("No subnet declaration for %s (%s).", 817 tmp->name, 818 (tmp->addresses == NULL) ? 819 "no IPv4 addresses" : 820 inet_ntoa(tmp->addresses[0])); 821 #ifdef DHCPv6 822 } else { 823 if (tmp->v6addresses != NULL) { 824 inet_ntop(AF_INET6, 825 &tmp->v6addresses[0], 826 abuf, 827 sizeof(abuf)); 828 } else { 829 strcpy(abuf, "no IPv6 addresses"); 830 } 831 log_error("No subnet6 declaration for %s (%s).", 832 tmp->name, 833 abuf); 834 #endif /* DHCPv6 */ 835 } 836 if (supports_multiple_interfaces(tmp)) { 837 log_error ("** Ignoring requests on %s. %s", 838 tmp -> name, "If this is not what"); 839 log_error (" you want, please write %s", 840 #ifdef DHCPv6 841 (local_family != AF_INET) ? 842 "a subnet6 declaration" : 843 #endif 844 "a subnet declaration"); 845 log_error (" in your dhcpd.conf file %s", 846 "for the network segment"); 847 log_error (" to %s %s %s", 848 "which interface", 849 tmp -> name, "is attached. **"); 850 log_error ("%s", ""); 851 goto next; 852 } else { 853 log_error ("You must write a %s", 854 #ifdef DHCPv6 855 (local_family != AF_INET) ? 856 "subnet6 declaration for this" : 857 #endif 858 "subnet declaration for this"); 859 log_error ("subnet. You cannot prevent %s", 860 "the DHCP server"); 861 log_error ("from listening on this subnet %s", 862 "because your"); 863 log_fatal ("operating system does not %s.", 864 "support this capability"); 865 } 866 } 867 868 /* Find subnets that don't have valid interface 869 addresses... */ 870 for (subnet = (tmp -> shared_network 871 ? tmp -> shared_network -> subnets 872 : (struct subnet *)0); 873 subnet; subnet = subnet -> next_sibling) { 874 /* Set the interface address for this subnet 875 to the first address we found. */ 876 if (subnet->interface_address.len == 0) { 877 if (tmp->address_count > 0) { 878 subnet->interface_address.len = 4; 879 memcpy(subnet->interface_address.iabuf, 880 &tmp->addresses[0].s_addr, 4); 881 } else if (tmp->v6address_count > 0) { 882 subnet->interface_address.len = 16; 883 memcpy(subnet->interface_address.iabuf, 884 &tmp->v6addresses[0].s6_addr, 885 16); 886 } else { 887 /* XXX: should be one */ 888 log_error("%s missing an interface " 889 "address", tmp->name); 890 continue; 891 } 892 } 893 } 894 895 /* Flag the index as not having been set, so that the 896 interface registerer can set it or not as it chooses. */ 897 tmp -> index = -1; 898 899 /* Register the interface... */ 900 switch (local_family) { 901 case AF_INET: 902 if (!dhcpv4_over_dhcpv6) { 903 if_register_receive(tmp); 904 if_register_send(tmp); 905 } else { 906 /* get_hw_addr() was called by register. */ 907 get_hw_addr(tmp->name, &tmp->hw_address); 908 } 909 break; 910 #ifdef DHCPv6 911 case AF_INET6: 912 if ((state == DISCOVER_SERVER) || 913 (state == DISCOVER_RELAY)) { 914 if_register6(tmp, 1); 915 } else if (state == DISCOVER_SERVER46) { 916 /* get_hw_addr() was called by if_register*6 917 so now we have to call it explicitly 918 to not leave the hardware address unknown 919 (some code expects it cannot be. */ 920 get_hw_addr(tmp->name, &tmp->hw_address); 921 } else { 922 if_register_linklocal6(tmp); 923 } 924 break; 925 #endif /* DHCPv6 */ 926 } 927 928 interface_stash (tmp); 929 wifcount++; 930 #if defined (F_SETFD) 931 /* if_register*() are no longer always called so 932 descriptors must be checked. */ 933 if ((tmp -> rfdesc >= 0) && 934 (fcntl (tmp -> rfdesc, F_SETFD, 1) < 0)) 935 log_error ("Can't set close-on-exec on %s: %m", 936 tmp -> name); 937 if ((tmp -> wfdesc != tmp -> rfdesc) && 938 (tmp -> wfdesc >= 0) && 939 (fcntl (tmp -> wfdesc, F_SETFD, 1) < 0)) 940 log_error ("Can't set close-on-exec on %s: %m", 941 tmp -> name); 942 #endif 943 next: 944 interface_dereference (&tmp, MDL); 945 if (next) 946 interface_reference (&tmp, next, MDL); 947 } 948 949 /* 950 * Now register all the remaining interfaces as protocols. 951 * We register with omapi to allow for control of the interface, 952 * we've already registered the fd or socket with the socket 953 * manager as part of if_register_receive(). 954 */ 955 for (tmp = interfaces; tmp; tmp = tmp -> next) { 956 /* not if it's been registered before */ 957 if (tmp -> flags & INTERFACE_RUNNING) 958 continue; 959 if (tmp -> rfdesc == -1) 960 continue; 961 switch (local_family) { 962 #ifdef DHCPv6 963 case AF_INET6: 964 #ifdef RELAY_PORT 965 #define UPSTREAM(ifp) \ 966 ((ifp->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM) 967 #define DOWNSTREAM(ifp) \ 968 ((ifp->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM) 969 970 if (relay_port) { 971 /* 972 * The normal IPv6 relay only needs one 973 * socket as long as we find an interface. 974 * When user relay port is defined, and we 975 * have two different UDP ports. One to 976 * receive from DHCP client with port 547, 977 * and the other is user defined for sending 978 * to the server or upstream relay agent. 979 * Thus we need to register sockets for one 980 * upstream and one downstream interfaces. 981 */ 982 if (updone && UPSTREAM(tmp)) 983 continue; 984 if (downdone && DOWNSTREAM(tmp)) 985 continue; 986 } 987 #endif 988 status = omapi_register_io_object((omapi_object_t *)tmp, 989 if_readsocket, 990 0, got_one_v6, 0, 0); 991 #ifdef RELAY_PORT 992 if (UPSTREAM(tmp)) 993 updone++; 994 else 995 downdone++; 996 #endif 997 break; 998 #endif /* DHCPv6 */ 999 case AF_INET: 1000 default: 1001 status = omapi_register_io_object((omapi_object_t *)tmp, 1002 if_readsocket, 1003 0, got_one, 0, 0); 1004 break; 1005 } 1006 1007 if (status != ISC_R_SUCCESS) 1008 log_fatal ("Can't register I/O handle for %s: %s", 1009 tmp -> name, isc_result_totext (status)); 1010 1011 #if defined(DHCPv6) 1012 /* Only register the first interface for V6, since 1013 * servers and relays all use the same socket. 1014 * XXX: This has some messy side effects if we start 1015 * dynamically adding and removing interfaces, but 1016 * we're well beyond that point in terms of mess. 1017 */ 1018 if (((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY)) 1019 && (local_family == AF_INET6) 1020 #if defined(RELAY_PORT) 1021 && ((relay_port == 0) || (updone && downdone)) 1022 #endif 1023 ) 1024 break; 1025 #endif 1026 } /* for (tmp = interfaces; ... */ 1027 1028 if (state == DISCOVER_SERVER && wifcount == 0) { 1029 log_info ("%s", ""); 1030 log_fatal ("Not configured to listen on any interfaces!"); 1031 } 1032 1033 if ((local_family == AF_INET) && 1034 !setup_fallback && !dhcpv4_over_dhcpv6) { 1035 setup_fallback = 1; 1036 maybe_setup_fallback(); 1037 } 1038 1039 #if defined (F_SETFD) 1040 if (fallback_interface) { 1041 if (fcntl (fallback_interface -> rfdesc, F_SETFD, 1) < 0) 1042 log_error ("Can't set close-on-exec on fallback: %m"); 1043 if (fallback_interface -> rfdesc != fallback_interface -> wfdesc) { 1044 if (fcntl (fallback_interface -> wfdesc, F_SETFD, 1) < 0) 1045 log_error ("Can't set close-on-exec on fallback: %m"); 1046 } 1047 } 1048 #endif /* F_SETFD */ 1049 } 1050 1051 int if_readsocket (h) 1052 omapi_object_t *h; 1053 { 1054 struct interface_info *ip; 1055 1056 if (h -> type != dhcp_type_interface) 1057 return -1; 1058 ip = (struct interface_info *)h; 1059 return ip -> rfdesc; 1060 } 1061 1062 int setup_fallback (struct interface_info **fp, const char *file, int line) 1063 { 1064 isc_result_t status; 1065 1066 status = interface_allocate (&fallback_interface, file, line); 1067 if (status != ISC_R_SUCCESS) 1068 log_fatal ("Error allocating fallback interface: %s", 1069 isc_result_totext (status)); 1070 strcpy (fallback_interface -> name, "fallback"); 1071 if (dhcp_interface_setup_hook) 1072 (*dhcp_interface_setup_hook) (fallback_interface, 1073 (struct iaddr *)0); 1074 status = interface_reference (fp, fallback_interface, file, line); 1075 1076 fallback_interface -> index = -1; 1077 interface_stash (fallback_interface); 1078 return status == ISC_R_SUCCESS; 1079 } 1080 1081 void reinitialize_interfaces () 1082 { 1083 struct interface_info *ip; 1084 1085 for (ip = interfaces; ip; ip = ip -> next) { 1086 if_reinitialize_receive (ip); 1087 if_reinitialize_send (ip); 1088 } 1089 1090 if (fallback_interface) 1091 if_reinitialize_send (fallback_interface); 1092 1093 interfaces_invalidated = 1; 1094 } 1095 1096 isc_result_t got_one (h) 1097 omapi_object_t *h; 1098 { 1099 struct sockaddr_in from; 1100 struct hardware hfrom; 1101 struct iaddr ifrom; 1102 int result; 1103 union { 1104 unsigned char packbuf [4095]; /* Packet input buffer. 1105 Must be as large as largest 1106 possible MTU. */ 1107 struct dhcp_packet packet; 1108 } u; 1109 struct interface_info *ip; 1110 1111 if (h -> type != dhcp_type_interface) 1112 return DHCP_R_INVALIDARG; 1113 ip = (struct interface_info *)h; 1114 1115 again: 1116 if ((result = 1117 receive_packet (ip, u.packbuf, sizeof u, &from, &hfrom)) < 0) { 1118 log_error ("receive_packet failed on %s: %m", ip -> name); 1119 return ISC_R_UNEXPECTED; 1120 } 1121 if (result == 0) 1122 return ISC_R_UNEXPECTED; 1123 1124 /* 1125 * If we didn't at least get the fixed portion of the BOOTP 1126 * packet, drop the packet. 1127 * Previously we allowed packets with no sname or filename 1128 * as we were aware of at least one client that did. But 1129 * a bug caused short packets to not work and nobody has 1130 * complained, it seems rational to tighten up that 1131 * restriction. 1132 */ 1133 if (result < DHCP_FIXED_NON_UDP) 1134 return ISC_R_UNEXPECTED; 1135 1136 #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) 1137 { 1138 /* We retrieve the ifindex from the unused hfrom variable */ 1139 unsigned int ifindex; 1140 1141 memcpy(&ifindex, hfrom.hbuf, sizeof (ifindex)); 1142 1143 /* 1144 * Seek forward from the first interface to find the matching 1145 * source interface by interface index. 1146 */ 1147 ip = interfaces; 1148 while ((ip != NULL) && (if_nametoindex(ip->name) != ifindex)) 1149 ip = ip->next; 1150 if (ip == NULL) 1151 return ISC_R_NOTFOUND; 1152 } 1153 #endif 1154 1155 if (bootp_packet_handler) { 1156 ifrom.len = 4; 1157 memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len); 1158 1159 (*bootp_packet_handler) (ip, &u.packet, (unsigned)result, 1160 from.sin_port, ifrom, &hfrom); 1161 } 1162 1163 /* If there is buffered data, read again. This is for, e.g., 1164 bpf, which may return two packets at once. */ 1165 if (ip -> rbuf_offset != ip -> rbuf_len) 1166 goto again; 1167 return ISC_R_SUCCESS; 1168 } 1169 1170 #ifdef DHCPv6 1171 isc_result_t 1172 got_one_v6(omapi_object_t *h) { 1173 struct sockaddr_in6 from; 1174 struct in6_addr to; 1175 struct iaddr ifrom; 1176 int result; 1177 char buf[65536]; /* maximum size for a UDP packet is 65536 */ 1178 struct interface_info *ip; 1179 int is_unicast; 1180 unsigned int if_idx = 0; 1181 1182 if (h->type != dhcp_type_interface) { 1183 return DHCP_R_INVALIDARG; 1184 } 1185 ip = (struct interface_info *)h; 1186 1187 result = receive_packet6(ip, (unsigned char *)buf, sizeof(buf), 1188 &from, &to, &if_idx); 1189 if (result < 0) { 1190 log_error("receive_packet6() failed on %s: %m", ip->name); 1191 return ISC_R_UNEXPECTED; 1192 } 1193 1194 /* 0 is 'any' interface. */ 1195 if (if_idx == 0) 1196 return ISC_R_NOTFOUND; 1197 1198 if (dhcpv6_packet_handler != NULL) { 1199 /* 1200 * If a packet is not multicast, we assume it is unicast. 1201 */ 1202 if (IN6_IS_ADDR_MULTICAST(&to)) { 1203 is_unicast = ISC_FALSE; 1204 } else { 1205 is_unicast = ISC_TRUE; 1206 } 1207 1208 ifrom.len = 16; 1209 memcpy(ifrom.iabuf, &from.sin6_addr, ifrom.len); 1210 1211 /* Seek forward to find the matching source interface. */ 1212 ip = interfaces; 1213 while ((ip != NULL) && (if_nametoindex(ip->name) != if_idx)) 1214 ip = ip->next; 1215 1216 if (ip == NULL) 1217 return ISC_R_NOTFOUND; 1218 1219 (*dhcpv6_packet_handler)(ip, buf, 1220 result, from.sin6_port, 1221 &ifrom, is_unicast); 1222 } 1223 1224 return ISC_R_SUCCESS; 1225 } 1226 #endif /* DHCPv6 */ 1227 1228 isc_result_t dhcp_interface_set_value (omapi_object_t *h, 1229 omapi_object_t *id, 1230 omapi_data_string_t *name, 1231 omapi_typed_data_t *value) 1232 { 1233 struct interface_info *interface; 1234 isc_result_t status; 1235 1236 if (h -> type != dhcp_type_interface) 1237 return DHCP_R_INVALIDARG; 1238 interface = (struct interface_info *)h; 1239 1240 if (!omapi_ds_strcmp (name, "name")) { 1241 if ((value -> type == omapi_datatype_data || 1242 value -> type == omapi_datatype_string) && 1243 value -> u.buffer.len < sizeof interface -> name) { 1244 memcpy (interface -> name, 1245 value -> u.buffer.value, 1246 value -> u.buffer.len); 1247 interface -> name [value -> u.buffer.len] = 0; 1248 } else 1249 return DHCP_R_INVALIDARG; 1250 return ISC_R_SUCCESS; 1251 } 1252 1253 /* Try to find some inner object that can take the value. */ 1254 if (h -> inner && h -> inner -> type -> set_value) { 1255 status = ((*(h -> inner -> type -> set_value)) 1256 (h -> inner, id, name, value)); 1257 if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED) 1258 return status; 1259 } 1260 1261 return ISC_R_NOTFOUND; 1262 } 1263 1264 1265 isc_result_t dhcp_interface_get_value (omapi_object_t *h, 1266 omapi_object_t *id, 1267 omapi_data_string_t *name, 1268 omapi_value_t **value) 1269 { 1270 return ISC_R_NOTIMPLEMENTED; 1271 } 1272 1273 isc_result_t dhcp_interface_destroy (omapi_object_t *h, 1274 const char *file, int line) 1275 { 1276 struct interface_info *interface; 1277 1278 if (h -> type != dhcp_type_interface) 1279 return DHCP_R_INVALIDARG; 1280 interface = (struct interface_info *)h; 1281 1282 if (interface -> ifp) { 1283 dfree (interface -> ifp, file, line); 1284 interface -> ifp = 0; 1285 } 1286 if (interface -> next) 1287 interface_dereference (&interface -> next, file, line); 1288 if (interface -> rbuf) { 1289 dfree (interface -> rbuf, file, line); 1290 interface -> rbuf = (unsigned char *)0; 1291 } 1292 if (interface -> client) 1293 interface -> client = (struct client_state *)0; 1294 1295 if (interface -> shared_network) 1296 omapi_object_dereference ((void *) 1297 &interface -> shared_network, MDL); 1298 1299 return ISC_R_SUCCESS; 1300 } 1301 1302 isc_result_t dhcp_interface_signal_handler (omapi_object_t *h, 1303 const char *name, va_list ap) 1304 { 1305 struct interface_info *ip, *interface; 1306 isc_result_t status; 1307 1308 if (h -> type != dhcp_type_interface) 1309 return DHCP_R_INVALIDARG; 1310 interface = (struct interface_info *)h; 1311 1312 /* If it's an update signal, see if the interface is dead right 1313 now, or isn't known at all, and if that's the case, revive it. */ 1314 if (!strcmp (name, "update")) { 1315 for (ip = dummy_interfaces; ip; ip = ip -> next) 1316 if (ip == interface) 1317 break; 1318 if (ip && dhcp_interface_startup_hook) 1319 return (*dhcp_interface_startup_hook) (ip); 1320 1321 for (ip = interfaces; ip; ip = ip -> next) 1322 if (ip == interface) 1323 break; 1324 if (!ip && dhcp_interface_startup_hook) 1325 return (*dhcp_interface_startup_hook) (ip); 1326 } 1327 1328 /* Try to find some inner object that can take the value. */ 1329 if (h -> inner && h -> inner -> type -> signal_handler) { 1330 status = ((*(h -> inner -> type -> signal_handler)) 1331 (h -> inner, name, ap)); 1332 if (status == ISC_R_SUCCESS) 1333 return status; 1334 } 1335 return ISC_R_NOTFOUND; 1336 } 1337 1338 isc_result_t dhcp_interface_stuff_values (omapi_object_t *c, 1339 omapi_object_t *id, 1340 omapi_object_t *h) 1341 { 1342 struct interface_info *interface; 1343 isc_result_t status; 1344 1345 if (h -> type != dhcp_type_interface) 1346 return DHCP_R_INVALIDARG; 1347 interface = (struct interface_info *)h; 1348 1349 /* Write out all the values. */ 1350 1351 status = omapi_connection_put_name (c, "state"); 1352 if (status != ISC_R_SUCCESS) 1353 return status; 1354 if ((interface->flags & INTERFACE_REQUESTED) != 0) 1355 status = omapi_connection_put_string (c, "up"); 1356 else 1357 status = omapi_connection_put_string (c, "down"); 1358 if (status != ISC_R_SUCCESS) 1359 return status; 1360 1361 /* Write out the inner object, if any. */ 1362 if (h -> inner && h -> inner -> type -> stuff_values) { 1363 status = ((*(h -> inner -> type -> stuff_values)) 1364 (c, id, h -> inner)); 1365 if (status == ISC_R_SUCCESS) 1366 return status; 1367 } 1368 1369 return ISC_R_SUCCESS; 1370 } 1371 1372 isc_result_t dhcp_interface_lookup (omapi_object_t **ip, 1373 omapi_object_t *id, 1374 omapi_object_t *ref) 1375 { 1376 omapi_value_t *tv = (omapi_value_t *)0; 1377 isc_result_t status; 1378 struct interface_info *interface; 1379 1380 if (!ref) 1381 return DHCP_R_NOKEYS; 1382 1383 /* First see if we were sent a handle. */ 1384 status = omapi_get_value_str (ref, id, "handle", &tv); 1385 if (status == ISC_R_SUCCESS) { 1386 status = omapi_handle_td_lookup (ip, tv -> value); 1387 1388 omapi_value_dereference (&tv, MDL); 1389 if (status != ISC_R_SUCCESS) 1390 return status; 1391 1392 /* Don't return the object if the type is wrong. */ 1393 if ((*ip) -> type != dhcp_type_interface) { 1394 omapi_object_dereference (ip, MDL); 1395 return DHCP_R_INVALIDARG; 1396 } 1397 } 1398 1399 /* Now look for an interface name. */ 1400 status = omapi_get_value_str (ref, id, "name", &tv); 1401 if (status == ISC_R_SUCCESS) { 1402 char *s; 1403 unsigned len; 1404 for (interface = interfaces; interface; 1405 interface = interface -> next) { 1406 s = memchr (interface -> name, 0, IFNAMSIZ); 1407 if (s) 1408 len = s - &interface -> name [0]; 1409 else 1410 len = IFNAMSIZ; 1411 if ((tv -> value -> u.buffer.len == len && 1412 !memcmp (interface -> name, 1413 (char *)tv -> value -> u.buffer.value, 1414 len))) 1415 break; 1416 } 1417 if (!interface) { 1418 for (interface = dummy_interfaces; 1419 interface; interface = interface -> next) { 1420 s = memchr (interface -> name, 0, IFNAMSIZ); 1421 if (s) 1422 len = s - &interface -> name [0]; 1423 else 1424 len = IFNAMSIZ; 1425 if ((tv -> value -> u.buffer.len == len && 1426 !memcmp (interface -> name, 1427 (char *) 1428 tv -> value -> u.buffer.value, 1429 len))) 1430 break; 1431 } 1432 } 1433 1434 omapi_value_dereference (&tv, MDL); 1435 if (*ip && *ip != (omapi_object_t *)interface) { 1436 omapi_object_dereference (ip, MDL); 1437 return DHCP_R_KEYCONFLICT; 1438 } else if (!interface) { 1439 if (*ip) 1440 omapi_object_dereference (ip, MDL); 1441 return ISC_R_NOTFOUND; 1442 } else if (!*ip) 1443 omapi_object_reference (ip, 1444 (omapi_object_t *)interface, 1445 MDL); 1446 } 1447 1448 /* If we get to here without finding an interface, no valid key was 1449 specified. */ 1450 if (!*ip) 1451 return DHCP_R_NOKEYS; 1452 return ISC_R_SUCCESS; 1453 } 1454 1455 /* actually just go discover the interface */ 1456 isc_result_t dhcp_interface_create (omapi_object_t **lp, 1457 omapi_object_t *id) 1458 { 1459 struct interface_info *hp; 1460 isc_result_t status; 1461 1462 hp = (struct interface_info *)0; 1463 status = interface_allocate (&hp, MDL); 1464 if (status != ISC_R_SUCCESS) 1465 return status; 1466 hp -> flags = INTERFACE_REQUESTED; 1467 status = interface_reference ((struct interface_info **)lp, hp, MDL); 1468 interface_dereference (&hp, MDL); 1469 return status; 1470 } 1471 1472 isc_result_t dhcp_interface_remove (omapi_object_t *lp, 1473 omapi_object_t *id) 1474 { 1475 struct interface_info *interface, *ip, *last; 1476 1477 interface = (struct interface_info *)lp; 1478 1479 /* remove from interfaces */ 1480 last = 0; 1481 for (ip = interfaces; ip; ip = ip -> next) { 1482 if (ip == interface) { 1483 if (last) { 1484 interface_dereference (&last -> next, MDL); 1485 if (ip -> next) 1486 interface_reference (&last -> next, 1487 ip -> next, MDL); 1488 } else { 1489 interface_dereference (&interfaces, MDL); 1490 if (ip -> next) 1491 interface_reference (&interfaces, 1492 ip -> next, MDL); 1493 } 1494 if (ip -> next) 1495 interface_dereference (&ip -> next, MDL); 1496 break; 1497 } 1498 last = ip; 1499 } 1500 if (!ip) 1501 return ISC_R_NOTFOUND; 1502 1503 /* add the interface to the dummy_interface list */ 1504 if (dummy_interfaces) { 1505 interface_reference (&interface -> next, 1506 dummy_interfaces, MDL); 1507 interface_dereference (&dummy_interfaces, MDL); 1508 } 1509 interface_reference (&dummy_interfaces, interface, MDL); 1510 1511 /* do a DHCPRELEASE */ 1512 if (dhcp_interface_shutdown_hook) 1513 (*dhcp_interface_shutdown_hook) (interface); 1514 1515 /* remove the io object */ 1516 omapi_unregister_io_object ((omapi_object_t *)interface); 1517 1518 switch(local_family) { 1519 #ifdef DHCPv6 1520 case AF_INET6: 1521 if_deregister6(interface); 1522 break; 1523 #endif /* DHCPv6 */ 1524 case AF_INET: 1525 default: 1526 if_deregister_send(interface); 1527 if_deregister_receive(interface); 1528 break; 1529 } 1530 1531 return ISC_R_SUCCESS; 1532 } 1533 1534 void interface_stash (struct interface_info *tptr) 1535 { 1536 struct interface_info **vec; 1537 int delta; 1538 1539 /* If the registerer didn't assign an index, assign one now. */ 1540 if (tptr -> index == -1) { 1541 tptr -> index = interface_count++; 1542 while (tptr -> index < interface_max && 1543 interface_vector [tptr -> index]) 1544 tptr -> index = interface_count++; 1545 } 1546 1547 if (interface_max <= tptr -> index) { 1548 delta = tptr -> index - interface_max + 10; 1549 vec = dmalloc ((interface_max + delta) * 1550 sizeof (struct interface_info *), MDL); 1551 if (!vec) { 1552 log_error ("interface_stash: allocation failed "); 1553 return; 1554 } 1555 1556 memset (&vec [interface_max], 0, 1557 (sizeof (struct interface_info *)) * delta); 1558 interface_max += delta; 1559 if (interface_vector) { 1560 memcpy (vec, interface_vector, 1561 (interface_count * 1562 sizeof (struct interface_info *))); 1563 dfree (interface_vector, MDL); 1564 } 1565 1566 interface_vector = vec; 1567 } 1568 1569 interface_reference (&interface_vector [tptr -> index], tptr, MDL); 1570 if (tptr -> index >= interface_count) 1571 interface_count = tptr -> index + 1; 1572 #if defined (TRACING) 1573 trace_interface_register (interface_trace, tptr); 1574 #endif 1575 } 1576 1577 void interface_snorf (struct interface_info *tmp, int ir) 1578 { 1579 tmp -> circuit_id = (u_int8_t *)tmp -> name; 1580 tmp -> circuit_id_len = strlen (tmp -> name); 1581 tmp -> remote_id = 0; 1582 tmp -> remote_id_len = 0; 1583 tmp -> flags = ir; 1584 if (interfaces) { 1585 interface_reference (&tmp -> next, 1586 interfaces, MDL); 1587 interface_dereference (&interfaces, MDL); 1588 } 1589 interface_reference (&interfaces, tmp, MDL); 1590 } 1591