1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/socket.h> 30 #include <net/if.h> 31 #include <stdlib.h> 32 #include <sys/sockio.h> 33 #include <netinet/in.h> 34 #include <netinet/dhcp.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include <search.h> 38 #include <libdevinfo.h> 39 #include <libdlpi.h> 40 #include <netinet/if_ether.h> 41 #include <arpa/inet.h> 42 #include <dhcpmsg.h> 43 #include <dhcp_inittab.h> 44 45 #include "agent.h" 46 #include "interface.h" 47 #include "util.h" 48 #include "packet.h" 49 #include "states.h" 50 51 dhcp_pif_t *v4root; 52 dhcp_pif_t *v6root; 53 54 static uint_t cached_v4_max_mtu, cached_v6_max_mtu; 55 56 /* 57 * Interface flags to watch: things that should be under our direct control. 58 */ 59 #define DHCP_IFF_WATCH (IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \ 60 IFF_TEMPORARY) 61 62 static void clear_lif_dhcp(dhcp_lif_t *); 63 64 /* 65 * insert_pif(): creates a new physical interface structure and chains it on 66 * the list. Initializes state that remains consistent across 67 * all use of the physical interface entry. 68 * 69 * input: const char *: the name of the physical interface 70 * boolean_t: if B_TRUE, this is DHCPv6 71 * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 72 * error code with the reason why 73 * output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure 74 */ 75 76 dhcp_pif_t * 77 insert_pif(const char *pname, boolean_t isv6, int *error) 78 { 79 dhcp_pif_t *pif; 80 struct lifreq lifr; 81 dlpi_handle_t dh = NULL; 82 83 if ((pif = calloc(1, sizeof (*pif))) == NULL) { 84 dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for " 85 "%s", pname); 86 *error = DHCP_IPC_E_MEMORY; 87 return (NULL); 88 } 89 90 pif->pif_isv6 = isv6; 91 pif->pif_hold_count = 1; 92 pif->pif_running = B_TRUE; 93 94 if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) { 95 dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long", 96 pname); 97 *error = DHCP_IPC_E_INVIF; 98 goto failure; 99 } 100 101 /* We do not use DLPI with DHCPv6 */ 102 if (!isv6) { 103 int rc; 104 dlpi_info_t dlinfo; 105 106 /* 107 * Do the allocations necessary for IPv4 DHCP. 108 * 109 * 1. open the interface using DLPI 110 * 2. get the interface hardware type and hardware length 111 * 3. get the interface hardware address 112 */ 113 114 /* step 1 */ 115 if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) { 116 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s", 117 dlpi_strerror(rc)); 118 *error = DHCP_IPC_E_INVIF; 119 goto failure; 120 } 121 122 if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) { 123 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s", 124 dlpi_strerror(rc)); 125 *error = DHCP_IPC_E_INVIF; 126 goto failure; 127 } 128 129 /* step 2 */ 130 rc = dlpi_info(dh, &dlinfo, 0); 131 if (rc != DLPI_SUCCESS) { 132 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s", 133 dlpi_strerror(rc)); 134 *error = DHCP_IPC_E_INVIF; 135 goto failure; 136 } 137 138 pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype); 139 pif->pif_hwlen = dlinfo.di_physaddrlen; 140 141 dhcpmsg(MSG_DEBUG, "insert_pif: %s: hwtype %d, hwlen %d", 142 pname, pif->pif_hwtype, pif->pif_hwlen); 143 144 /* step 3 */ 145 if (pif->pif_hwlen > 0) { 146 pif->pif_hwaddr = malloc(pif->pif_hwlen); 147 if (pif->pif_hwaddr == NULL) { 148 dhcpmsg(MSG_ERR, "insert_pif: cannot allocate " 149 "pif_hwaddr for %s", pname); 150 *error = DHCP_IPC_E_MEMORY; 151 goto failure; 152 } 153 (void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr, 154 pif->pif_hwlen); 155 } 156 157 dlpi_close(dh); 158 dh = NULL; 159 } 160 161 /* 162 * This is a bit gross, but IP has a confused interface. We must 163 * assume that the zeroth LIF is plumbed, and must query there to get 164 * the interface index number. 165 */ 166 (void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ); 167 168 if (ioctl(isv6 ? v6_sock_fd : v4_sock_fd, SIOCGLIFINDEX, &lifr) == -1) { 169 *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT; 170 dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname); 171 goto failure; 172 } 173 pif->pif_index = lifr.lifr_index; 174 175 if (ioctl(isv6 ? v6_sock_fd : v4_sock_fd, SIOCGLIFMTU, &lifr) == -1) { 176 *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT; 177 dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFMTU for %s", pname); 178 goto failure; 179 } 180 pif->pif_max = lifr.lifr_mtu; 181 182 if (pif->pif_max < DHCP_DEF_MAX_SIZE) { 183 dhcpmsg(MSG_ERROR, "insert_pif: MTU of %s is too small to " 184 "support DHCP (%u < %u)", pname, pif->pif_max, 185 DHCP_DEF_MAX_SIZE); 186 *error = DHCP_IPC_E_INVIF; 187 goto failure; 188 } 189 190 insque(pif, isv6 ? &v6root : &v4root); 191 192 return (pif); 193 194 failure: 195 if (dh != NULL) 196 dlpi_close(dh); 197 release_pif(pif); 198 return (NULL); 199 } 200 201 /* 202 * hold_pif(): acquire a hold on a physical interface structure. 203 * 204 * input: dhcp_pif_t *: a pointer to the PIF structure 205 * output: none 206 */ 207 208 void 209 hold_pif(dhcp_pif_t *pif) 210 { 211 pif->pif_hold_count++; 212 dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name, 213 pif->pif_hold_count); 214 } 215 216 /* 217 * release_pif(): release a hold on a physical interface structure; will 218 * destroy the structure on the last hold removed. 219 * 220 * input: dhcp_pif_t *: a pointer to the PIF structure 221 * output: none 222 */ 223 224 void 225 release_pif(dhcp_pif_t *pif) 226 { 227 if (pif->pif_hold_count == 0) { 228 dhcpmsg(MSG_CRIT, "release_pif: extraneous release"); 229 return; 230 } 231 232 if (--pif->pif_hold_count == 0) { 233 dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s", 234 pif->pif_name); 235 236 remque(pif); 237 free(pif->pif_hwaddr); 238 free(pif); 239 } else { 240 dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u", 241 pif->pif_name, pif->pif_hold_count); 242 } 243 } 244 245 /* 246 * lookup_pif_by_index(): Looks up PIF entries given regular ifIndex. 247 * 248 * input: uint_t: the interface index 249 * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 250 * output: dhcp_pif_t *: the matching PIF, or NULL if not found 251 */ 252 253 dhcp_pif_t * 254 lookup_pif_by_index(uint_t ifindex, boolean_t isv6) 255 { 256 dhcp_pif_t *pif; 257 258 for (pif = isv6 ? v6root : v4root; pif != NULL; pif = pif->pif_next) { 259 if (pif->pif_index == ifindex) 260 break; 261 } 262 263 return (pif); 264 } 265 266 /* 267 * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and 268 * previous PIF pointer (or NULL for list start). 269 * Caller is expected to iterate through all 270 * potential matches to find interface of interest. 271 * 272 * input: uint16_t: the interface index (truncated) 273 * dhcp_pif_t *: the previous PIF, or NULL for list start 274 * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 275 * output: dhcp_pif_t *: the next matching PIF, or NULL if not found 276 * note: This operates using the 'truncated' (16-bit) ifindex as seen by 277 * routing socket clients. The value stored in pif_index is the 278 * 32-bit ifindex from the ioctl interface. 279 */ 280 281 dhcp_pif_t * 282 lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6) 283 { 284 if (pif == NULL) 285 pif = isv6 ? v6root : v4root; 286 else 287 pif = pif->pif_next; 288 289 for (; pif != NULL; pif = pif->pif_next) { 290 if ((pif->pif_index & 0xffff) == ifindex) 291 break; 292 } 293 294 return (pif); 295 } 296 297 /* 298 * lookup_pif_by_name(): Looks up a physical interface entry given a name. 299 * 300 * input: const char *: the physical interface name 301 * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 302 * output: dhcp_pif_t *: the matching PIF, or NULL if not found 303 */ 304 305 dhcp_pif_t * 306 lookup_pif_by_name(const char *pname, boolean_t isv6) 307 { 308 dhcp_pif_t *pif; 309 310 pif = isv6 ? v6root : v4root; 311 312 for (; pif != NULL; pif = pif->pif_next) { 313 if (strcmp(pif->pif_name, pname) == 0) 314 break; 315 } 316 317 return (pif); 318 } 319 320 /* 321 * pif_status(): update the physical interface up/down status. 322 * 323 * input: dhcp_pif_t *: the physical interface to be updated 324 * boolean_t: B_TRUE if the interface is going up 325 * output: none 326 */ 327 328 void 329 pif_status(dhcp_pif_t *pif, boolean_t isup) 330 { 331 dhcp_lif_t *lif; 332 dhcp_smach_t *dsmp; 333 334 pif->pif_running = isup; 335 dhcpmsg(MSG_DEBUG, "interface %s has %s", pif->pif_name, 336 isup ? "come back up" : "gone down"); 337 for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 338 for (dsmp = lif->lif_smachs; dsmp != NULL; 339 dsmp = dsmp->dsm_next) { 340 if (isup) 341 refresh_smach(dsmp); 342 else 343 remove_default_routes(dsmp); 344 } 345 } 346 } 347 348 /* Helper for insert_lif: extract addresses as defined */ 349 #define ASSIGN_ADDR(v4, v6, lf) \ 350 if (pif->pif_isv6) { \ 351 lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \ 352 } else { \ 353 lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \ 354 } 355 356 /* 357 * insert_lif(): Creates a new logical interface structure and chains it on 358 * the list for a given physical interface. Initializes state 359 * that remains consistent across all use of the logical 360 * interface entry. Caller's PIF hold is transferred to the 361 * LIF on success, and is dropped on failure. 362 * 363 * input: dhcp_pif_t *: pointer to the physical interface for this LIF 364 * const char *: the name of the logical interface 365 * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 366 * error code with the reason why 367 * output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure 368 */ 369 370 dhcp_lif_t * 371 insert_lif(dhcp_pif_t *pif, const char *lname, int *error) 372 { 373 dhcp_lif_t *lif; 374 int fd; 375 struct lifreq lifr; 376 377 if ((lif = calloc(1, sizeof (*lif))) == NULL) { 378 dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for " 379 "%s", lname); 380 *error = DHCP_IPC_E_MEMORY; 381 return (NULL); 382 } 383 384 lif->lif_sock_ip_fd = -1; 385 lif->lif_packet_id = -1; 386 lif->lif_iaid_id = -1; 387 lif->lif_hold_count = 1; 388 lif->lif_pif = pif; 389 lif->lif_removed = B_TRUE; 390 init_timer(&lif->lif_preferred, 0); 391 init_timer(&lif->lif_expire, 0); 392 393 if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) { 394 dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long", 395 lname); 396 *error = DHCP_IPC_E_INVIF; 397 goto failure; 398 } 399 400 (void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ); 401 402 fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 403 404 if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) 405 lif->lif_max = 1024; 406 else 407 lif->lif_max = lifr.lifr_mtu; 408 409 if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) { 410 if (errno == ENXIO) 411 *error = DHCP_IPC_E_INVIF; 412 else 413 *error = DHCP_IPC_E_INT; 414 dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname); 415 goto failure; 416 } 417 ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr); 418 419 if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) { 420 if (errno == ENXIO) 421 *error = DHCP_IPC_E_INVIF; 422 else 423 *error = DHCP_IPC_E_INT; 424 dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname); 425 goto failure; 426 } 427 ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr); 428 429 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 430 *error = DHCP_IPC_E_INT; 431 dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname); 432 goto failure; 433 } 434 lif->lif_flags = lifr.lifr_flags; 435 436 /* 437 * If we've just detected the interface going up or down, then signal 438 * an appropriate action. There may be other state machines here. 439 */ 440 if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) { 441 pif_status(pif, B_TRUE); 442 } else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) { 443 pif_status(pif, B_FALSE); 444 } 445 446 if (lifr.lifr_flags & IFF_POINTOPOINT) { 447 if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) { 448 *error = DHCP_IPC_E_INT; 449 dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s", 450 lname); 451 goto failure; 452 } 453 ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr); 454 } else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) { 455 if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) { 456 *error = DHCP_IPC_E_INT; 457 dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s", 458 lname); 459 goto failure; 460 } 461 lif->lif_broadcast = 462 ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr. 463 s_addr; 464 } 465 466 if (pif->pif_isv6) 467 cached_v6_max_mtu = 0; 468 else 469 cached_v4_max_mtu = 0; 470 471 lif->lif_removed = B_FALSE; 472 insque(lif, &pif->pif_lifs); 473 474 return (lif); 475 476 failure: 477 release_lif(lif); 478 return (NULL); 479 } 480 481 /* 482 * hold_lif(): acquire a hold on a logical interface structure. 483 * 484 * input: dhcp_lif_t *: a pointer to the LIF structure 485 * output: none 486 */ 487 488 void 489 hold_lif(dhcp_lif_t *lif) 490 { 491 lif->lif_hold_count++; 492 dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name, 493 lif->lif_hold_count); 494 } 495 496 /* 497 * release_lif(): release a hold on a logical interface structure; will 498 * destroy the structure on the last hold removed. 499 * 500 * input: dhcp_lif_t *: a pointer to the LIF structure 501 * output: none 502 */ 503 504 void 505 release_lif(dhcp_lif_t *lif) 506 { 507 if (lif->lif_hold_count == 0) { 508 dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s", 509 lif->lif_name); 510 return; 511 } 512 513 if (lif->lif_hold_count == 1 && !lif->lif_removed) { 514 unplumb_lif(lif); 515 return; 516 } 517 518 if (--lif->lif_hold_count == 0) { 519 dhcp_pif_t *pif; 520 521 dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s", 522 lif->lif_name); 523 524 if (lif->lif_lease != NULL) 525 dhcpmsg(MSG_CRIT, 526 "release_lif: still holding lease at last hold!"); 527 close_ip_lif(lif); 528 pif = lif->lif_pif; 529 if (pif->pif_isv6) 530 cached_v6_max_mtu = 0; 531 else 532 cached_v4_max_mtu = 0; 533 release_pif(pif); 534 free(lif); 535 } else { 536 dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u", 537 lif->lif_name, lif->lif_hold_count); 538 } 539 } 540 541 /* 542 * remove_lif(): remove a logical interface from its PIF and lease (if any) and 543 * the lease's hold on the LIF. Assumes that we did not plumb 544 * the interface. 545 * 546 * input: dhcp_lif_t *: a pointer to the LIF structure 547 * output: none 548 */ 549 550 void 551 remove_lif(dhcp_lif_t *lif) 552 { 553 if (lif->lif_plumbed) { 554 dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s", 555 lif->lif_name); 556 return; 557 } 558 if (lif->lif_removed) { 559 dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s", 560 lif->lif_name); 561 } else { 562 dhcp_lif_t *lifnext; 563 dhcp_lease_t *dlp; 564 565 dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name); 566 lif->lif_removed = B_TRUE; 567 lifnext = lif->lif_next; 568 clear_lif_dhcp(lif); 569 cancel_lif_timers(lif); 570 if (lif->lif_iaid_id != -1 && 571 iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) { 572 lif->lif_iaid_id = -1; 573 release_lif(lif); 574 } 575 576 /* Remove from PIF list */ 577 remque(lif); 578 579 /* If we were part of a lease, then remove ourselves */ 580 if ((dlp = lif->lif_lease) != NULL) { 581 if (--dlp->dl_nlifs == 0) 582 dlp->dl_lifs = NULL; 583 else if (dlp->dl_lifs == lif) 584 dlp->dl_lifs = lifnext; 585 if (lif->lif_flags & IFF_DHCPRUNNING) 586 clear_lif_dhcp(lif); 587 if (lif->lif_declined != NULL) { 588 dlp->dl_smach->dsm_lif_down--; 589 lif->lif_declined = NULL; 590 } 591 lif->lif_lease = NULL; 592 release_lif(lif); 593 } 594 } 595 } 596 597 /* 598 * lookup_lif_by_name(): Looks up a logical interface entry given a name and 599 * a physical interface. 600 * 601 * input: const char *: the logical interface name 602 * const dhcp_pif_t *: the physical interface 603 * output: dhcp_lif_t *: the matching LIF, or NULL if not found 604 */ 605 606 dhcp_lif_t * 607 lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif) 608 { 609 dhcp_lif_t *lif; 610 611 for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 612 if (strcmp(lif->lif_name, lname) == 0) 613 break; 614 } 615 616 return (lif); 617 } 618 619 /* 620 * checkaddr(): checks if the given address is still set on the given LIF 621 * 622 * input: const dhcp_lif_t *: the LIF to check 623 * int: the address to look up on the interface (ioctl) 624 * const in6_addr_t *: the address to compare to 625 * const char *: name of the address for logging purposes 626 * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not 627 */ 628 629 static boolean_t 630 checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr, 631 const char *aname) 632 { 633 boolean_t isv6; 634 int fd; 635 struct lifreq lifr; 636 char abuf1[INET6_ADDRSTRLEN]; 637 char abuf2[INET6_ADDRSTRLEN]; 638 639 (void) memset(&lifr, 0, sizeof (struct lifreq)); 640 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 641 642 isv6 = lif->lif_pif->pif_isv6; 643 fd = isv6 ? v6_sock_fd : v4_sock_fd; 644 645 if (ioctl(fd, ioccmd, &lifr) == -1) { 646 if (errno == ENXIO) { 647 dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone", 648 lif->lif_name); 649 return (B_FALSE); 650 } 651 dhcpmsg(MSG_DEBUG, 652 "checkaddr: ignoring ioctl error on %s %x: %s", 653 lif->lif_name, ioccmd, strerror(errno)); 654 } else if (isv6) { 655 struct sockaddr_in6 *sin6 = 656 (struct sockaddr_in6 *)&lifr.lifr_addr; 657 658 if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) { 659 dhcpmsg(MSG_WARNING, 660 "checkaddr: expected %s %s on %s, have %s", aname, 661 inet_ntop(AF_INET6, addr, abuf1, sizeof (abuf1)), 662 lif->lif_name, inet_ntop(AF_INET6, &sin6->sin6_addr, 663 abuf2, sizeof (abuf2))); 664 return (B_FALSE); 665 } 666 } else { 667 struct sockaddr_in *sinp = 668 (struct sockaddr_in *)&lifr.lifr_addr; 669 ipaddr_t v4addr; 670 671 IN6_V4MAPPED_TO_IPADDR(addr, v4addr); 672 if (sinp->sin_addr.s_addr != v4addr) { 673 dhcpmsg(MSG_WARNING, 674 "checkaddr: expected %s %s on %s, have %s", aname, 675 inet_ntop(AF_INET, &v4addr, abuf1, sizeof (abuf1)), 676 lif->lif_name, inet_ntop(AF_INET, &sinp->sin_addr, 677 abuf2, sizeof (abuf2))); 678 return (B_FALSE); 679 } 680 } 681 return (B_TRUE); 682 } 683 684 /* 685 * verify_lif(): verifies than a LIF is still valid (i.e., has not been 686 * explicitly or implicitly dropped or released) 687 * 688 * input: const dhcp_lif_t *: the LIF to verify 689 * output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise 690 */ 691 692 boolean_t 693 verify_lif(const dhcp_lif_t *lif) 694 { 695 boolean_t isv6; 696 int fd; 697 struct lifreq lifr; 698 699 (void) memset(&lifr, 0, sizeof (struct lifreq)); 700 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 701 702 isv6 = lif->lif_pif->pif_isv6; 703 fd = isv6 ? v6_sock_fd : v4_sock_fd; 704 705 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 706 if (errno != ENXIO) { 707 dhcpmsg(MSG_ERR, 708 "verify_lif: SIOCGLIFFLAGS failed on %s", 709 lif->lif_name); 710 } 711 return (B_FALSE); 712 } 713 714 /* 715 * If important flags have changed, then abandon the interface. 716 */ 717 if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) { 718 dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: " 719 "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags, 720 lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) & 721 DHCP_IFF_WATCH); 722 return (B_FALSE); 723 } 724 725 /* 726 * Special case: if the interface has gone down as a duplicate, then 727 * this alone does _not_ mean that we're abandoning it just yet. Allow 728 * the state machine to handle this normally by trying to get a new 729 * lease. 730 */ 731 if ((lifr.lifr_flags & (IFF_UP|IFF_DUPLICATE)) == IFF_DUPLICATE) { 732 dhcpmsg(MSG_DEBUG, "verify_lif: duplicate address on %s", 733 lif->lif_name); 734 return (B_TRUE); 735 } 736 737 /* 738 * If the user has torn down or started up the interface manually, then 739 * abandon the lease. 740 */ 741 if ((lif->lif_flags ^ lifr.lifr_flags) & IFF_UP) { 742 dhcpmsg(MSG_DEBUG, "verify_lif: user has %s %s", 743 lifr.lifr_flags & IFF_UP ? "started up" : "shut down", 744 lif->lif_name); 745 return (B_FALSE); 746 } 747 748 /* 749 * Check for delete and recreate. 750 */ 751 if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 752 dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed on %s", 753 lif->lif_name); 754 return (B_FALSE); 755 } 756 if (lifr.lifr_index != lif->lif_pif->pif_index) { 757 dhcpmsg(MSG_DEBUG, 758 "verify_lif: ifindex on %s changed: %u to %u", 759 lif->lif_name, lif->lif_pif->pif_index, lifr.lifr_index); 760 return (B_FALSE); 761 } 762 763 /* 764 * If the IP address, netmask, or broadcast address have changed, or 765 * the interface has been unplumbed, then we act like there has been an 766 * implicit drop. (Note that the netmask is under DHCP control for 767 * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast 768 * addresses.) 769 */ 770 771 if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address")) 772 return (B_FALSE); 773 774 if (isv6) { 775 /* 776 * If it's not point-to-point, we're done. If it is, then 777 * check the peer's address as well. 778 */ 779 return (!(lif->lif_flags & IFF_POINTOPOINT) || 780 checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer, 781 "peer address")); 782 } else { 783 if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask, 784 "netmask")) 785 return (B_FALSE); 786 787 return (checkaddr(lif, 788 (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR : 789 SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address")); 790 } 791 } 792 793 /* 794 * canonize_lif(): puts the interface in a canonical (zeroed) form. This is 795 * used only on the "main" LIF for IPv4. All other interfaces 796 * are under dhcpagent control and are removed using 797 * unplumb_lif(). 798 * 799 * input: dhcp_lif_t *: the interface to canonize 800 * boolean_t: only canonize lif if it's under DHCP control 801 * output: none 802 */ 803 804 static void 805 canonize_lif(dhcp_lif_t *lif, boolean_t dhcponly) 806 { 807 boolean_t isv6; 808 int fd; 809 struct lifreq lifr; 810 811 /* 812 * If there's nothing here, then don't touch the interface. This can 813 * happen when an already-canonized LIF is recanonized. 814 */ 815 if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr)) 816 return; 817 818 isv6 = lif->lif_pif->pif_isv6; 819 dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s", 820 isv6 ? 6 : 4, lif->lif_name); 821 822 lif->lif_v6addr = my_in6addr_any; 823 lif->lif_v6mask = my_in6addr_any; 824 lif->lif_v6peer = my_in6addr_any; 825 826 (void) memset(&lifr, 0, sizeof (struct lifreq)); 827 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 828 829 fd = isv6 ? v6_sock_fd : v4_sock_fd; 830 831 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 832 if (errno != ENXIO) { 833 dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s", 834 lif->lif_name); 835 } 836 return; 837 } 838 839 if (dhcponly && !(lifr.lifr_flags & IFF_DHCPRUNNING)) { 840 dhcpmsg(MSG_INFO, 841 "canonize_lif: cannot clear %s; flags are %llx", 842 lif->lif_name, lifr.lifr_flags); 843 return; 844 } 845 846 /* 847 * clear the UP flag, but don't clear DHCPRUNNING since 848 * that should only be done when the interface is removed 849 * (see clear_lif_dhcp() and remove_lif()) 850 */ 851 852 lif->lif_flags = lifr.lifr_flags &= ~IFF_UP; 853 854 if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 855 dhcpmsg(MSG_ERR, "canonize_lif: can't set flags for %s", 856 lif->lif_name); 857 return; 858 } 859 860 (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); 861 if (isv6) { 862 struct sockaddr_in6 *sin6 = 863 (struct sockaddr_in6 *)&lifr.lifr_addr; 864 865 sin6->sin6_family = AF_INET6; 866 sin6->sin6_addr = my_in6addr_any; 867 } else { 868 struct sockaddr_in *sinv = 869 (struct sockaddr_in *)&lifr.lifr_addr; 870 871 sinv->sin_family = AF_INET; 872 sinv->sin_addr.s_addr = htonl(INADDR_ANY); 873 } 874 875 if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) { 876 dhcpmsg(MSG_ERR, 877 "canonize_lif: can't clear local address on %s", 878 lif->lif_name); 879 } 880 881 /* Netmask is under in.ndpd control with IPv6 */ 882 if (!isv6 && ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) { 883 dhcpmsg(MSG_ERR, "canonize_lif: can't clear netmask on %s", 884 lif->lif_name); 885 } 886 887 if (lif->lif_flags & IFF_POINTOPOINT) { 888 if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) { 889 dhcpmsg(MSG_ERR, 890 "canonize_lif: can't clear remote address on %s", 891 lif->lif_name); 892 } 893 } else if (!isv6) { 894 if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) { 895 dhcpmsg(MSG_ERR, 896 "canonize_lif: can't clear broadcast address on %s", 897 lif->lif_name); 898 } 899 } 900 } 901 902 /* 903 * plumb_lif(): Adds the LIF to the system. This is used for all 904 * DHCPv6-derived interfaces. The returned LIF has a hold 905 * on it. 906 * 907 * input: dhcp_lif_t *: the interface to unplumb 908 * output: none 909 */ 910 911 dhcp_lif_t * 912 plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr) 913 { 914 dhcp_lif_t *lif; 915 char abuf[INET6_ADDRSTRLEN]; 916 struct lifreq lifr; 917 struct sockaddr_in6 *sin6; 918 int error; 919 920 (void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)); 921 922 for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 923 if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) { 924 dhcpmsg(MSG_ERR, 925 "plumb_lif: entry for %s already exists!", abuf); 926 return (NULL); 927 } 928 } 929 930 /* First, create a new zero-address logical interface */ 931 (void) memset(&lifr, 0, sizeof (lifr)); 932 (void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name)); 933 if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) { 934 dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name); 935 return (NULL); 936 } 937 938 /* Next, set the netmask to all ones */ 939 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; 940 sin6->sin6_family = AF_INET6; 941 (void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr)); 942 if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) { 943 dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s", 944 lifr.lifr_name); 945 goto failure; 946 } 947 948 /* Now set the interface address */ 949 sin6->sin6_addr = *addr; 950 if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) { 951 dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s", 952 lifr.lifr_name, abuf); 953 goto failure; 954 } 955 956 /* Mark the interface up */ 957 if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 958 dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s", 959 lifr.lifr_name); 960 goto failure; 961 } 962 lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING; 963 if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 964 dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s", 965 lifr.lifr_name); 966 goto failure; 967 } 968 969 /* Now we can create the internal LIF structure */ 970 hold_pif(pif); 971 if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL) 972 goto failure; 973 974 dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf, 975 lif->lif_name); 976 lif->lif_plumbed = B_TRUE; 977 978 return (lif); 979 980 failure: 981 if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 982 errno != ENXIO) { 983 dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s", 984 lifr.lifr_name); 985 } 986 return (NULL); 987 } 988 989 /* 990 * unplumb_lif(): Removes the LIF from dhcpagent and the system. This is used 991 * for all interfaces configured by DHCP (those in leases). 992 * 993 * input: dhcp_lif_t *: the interface to unplumb 994 * output: none 995 */ 996 997 void 998 unplumb_lif(dhcp_lif_t *lif) 999 { 1000 dhcp_lease_t *dlp; 1001 1002 if (lif->lif_plumbed) { 1003 struct lifreq lifr; 1004 1005 (void) memset(&lifr, 0, sizeof (lifr)); 1006 (void) strlcpy(lifr.lifr_name, lif->lif_name, 1007 sizeof (lifr.lifr_name)); 1008 if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 1009 errno != ENXIO) { 1010 dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s", 1011 lif->lif_name); 1012 } 1013 lif->lif_plumbed = B_FALSE; 1014 } 1015 lif->lif_flags = 0; 1016 /* 1017 * Special case: if we're "unplumbing" the main LIF for DHCPv4, then 1018 * just canonize it and remove it from the lease. 1019 */ 1020 if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) { 1021 canonize_lif(lif, B_TRUE); 1022 cancel_lif_timers(lif); 1023 if (lif->lif_declined != NULL) { 1024 dlp->dl_smach->dsm_lif_down--; 1025 lif->lif_declined = NULL; 1026 } 1027 dlp->dl_nlifs = 0; 1028 dlp->dl_lifs = NULL; 1029 lif->lif_lease = NULL; 1030 release_lif(lif); 1031 } else { 1032 remove_lif(lif); 1033 } 1034 } 1035 1036 /* 1037 * attach_lif(): create a new logical interface, creating the physical 1038 * interface as necessary. 1039 * 1040 * input: const char *: the logical interface name 1041 * boolean_t: B_TRUE for IPv6 1042 * int *: set to DHCP_IPC_E_* if creation fails 1043 * output: dhcp_lif_t *: pointer to new entry, or NULL on failure 1044 */ 1045 1046 dhcp_lif_t * 1047 attach_lif(const char *lname, boolean_t isv6, int *error) 1048 { 1049 dhcp_pif_t *pif; 1050 char pname[LIFNAMSIZ], *cp; 1051 1052 (void) strlcpy(pname, lname, sizeof (pname)); 1053 if ((cp = strchr(pname, ':')) != NULL) 1054 *cp = '\0'; 1055 1056 if ((pif = lookup_pif_by_name(pname, isv6)) != NULL) 1057 hold_pif(pif); 1058 else if ((pif = insert_pif(pname, isv6, error)) == NULL) 1059 return (NULL); 1060 1061 if (lookup_lif_by_name(lname, pif) != NULL) { 1062 dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!", 1063 lname); 1064 release_pif(pif); 1065 *error = DHCP_IPC_E_INVIF; 1066 return (NULL); 1067 } 1068 1069 /* If LIF creation fails, then insert_lif discards our PIF hold */ 1070 return (insert_lif(pif, lname, error)); 1071 } 1072 1073 /* 1074 * set_lif_dhcp(): Set logical interface flags to show that it's managed 1075 * by DHCP. 1076 * 1077 * input: dhcp_lif_t *: the logical interface 1078 * boolean_t: B_TRUE if adopting 1079 * output: int: set to DHCP_IPC_E_* if operation fails 1080 */ 1081 1082 int 1083 set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting) 1084 { 1085 int fd; 1086 int err; 1087 struct lifreq lifr; 1088 1089 fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1090 1091 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1092 1093 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 1094 err = errno; 1095 dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s", 1096 lif->lif_name); 1097 return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT); 1098 } 1099 lif->lif_flags = lifr.lifr_flags; 1100 1101 /* 1102 * Check for conflicting sources of address control, and other 1103 * unacceptable configurations. 1104 */ 1105 if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY| 1106 IFF_VIRTUAL)) { 1107 dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx", 1108 lif->lif_name, lifr.lifr_flags); 1109 return (DHCP_IPC_E_INVIF); 1110 } 1111 1112 /* 1113 * if DHCPRUNNING is already set on the interface and we're 1114 * not adopting it, the agent probably crashed and burned. 1115 * note it, but don't let it stop the proceedings. we're 1116 * pretty sure we're not already running, since we wouldn't 1117 * have been able to bind to our IPC port. 1118 */ 1119 1120 if (lifr.lifr_flags & IFF_DHCPRUNNING) { 1121 if (!is_adopting) { 1122 dhcpmsg(MSG_WARNING, "set_lif_dhcp: DHCP flag already " 1123 "set on %s", lif->lif_name); 1124 } 1125 } else { 1126 lifr.lifr_flags |= IFF_DHCPRUNNING; 1127 if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 1128 dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s", 1129 lif->lif_name); 1130 return (DHCP_IPC_E_INT); 1131 } 1132 lif->lif_flags = lifr.lifr_flags; 1133 } 1134 return (DHCP_IPC_SUCCESS); 1135 } 1136 1137 /* 1138 * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer 1139 * managed by DHCP. 1140 * 1141 * input: dhcp_lif_t *: the logical interface 1142 * output: none 1143 */ 1144 1145 static void 1146 clear_lif_dhcp(dhcp_lif_t *lif) 1147 { 1148 int fd; 1149 struct lifreq lifr; 1150 1151 fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1152 1153 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1154 1155 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 1156 return; 1157 1158 if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) 1159 return; 1160 1161 lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING; 1162 (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 1163 } 1164 1165 /* 1166 * set_lif_deprecated(): Set the "deprecated" flag to tell users that this 1167 * address will be going away. As the interface is 1168 * going away, we don't care if there are errors. 1169 * 1170 * input: dhcp_lif_t *: the logical interface 1171 * output: none 1172 */ 1173 1174 void 1175 set_lif_deprecated(dhcp_lif_t *lif) 1176 { 1177 int fd; 1178 struct lifreq lifr; 1179 1180 if (lif->lif_flags & IFF_DEPRECATED) 1181 return; 1182 1183 fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1184 1185 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1186 1187 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 1188 return; 1189 1190 if (lifr.lifr_flags & IFF_DEPRECATED) 1191 return; 1192 1193 lifr.lifr_flags |= IFF_DEPRECATED; 1194 (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 1195 lif->lif_flags = lifr.lifr_flags; 1196 } 1197 1198 /* 1199 * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this 1200 * address will not be going away. This happens if we 1201 * get a renewal after preferred lifetime but before 1202 * the valid lifetime. 1203 * 1204 * input: dhcp_lif_t *: the logical interface 1205 * output: boolean_t: B_TRUE on success. 1206 */ 1207 1208 boolean_t 1209 clear_lif_deprecated(dhcp_lif_t *lif) 1210 { 1211 int fd; 1212 struct lifreq lifr; 1213 1214 fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1215 1216 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1217 1218 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 1219 dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s", 1220 lif->lif_name); 1221 return (B_FALSE); 1222 } 1223 1224 /* 1225 * Check for conflicting sources of address control, and other 1226 * unacceptable configurations. 1227 */ 1228 if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY| 1229 IFF_VIRTUAL)) { 1230 dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags " 1231 "are %llx", lif->lif_name, lifr.lifr_flags); 1232 return (B_FALSE); 1233 } 1234 1235 if (!(lifr.lifr_flags & IFF_DEPRECATED)) 1236 return (B_TRUE); 1237 1238 lifr.lifr_flags &= ~IFF_DEPRECATED; 1239 if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 1240 dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s", 1241 lif->lif_name); 1242 return (B_FALSE); 1243 } else { 1244 lif->lif_flags = lifr.lifr_flags; 1245 return (B_TRUE); 1246 } 1247 } 1248 1249 /* 1250 * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only). 1251 * 1252 * input: dhcp_lif_t *: the logical interface to operate on 1253 * in_addr_t: the address the socket will be bound to (in hbo) 1254 * output: boolean_t: B_TRUE if the socket was opened successfully. 1255 */ 1256 1257 boolean_t 1258 open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo) 1259 { 1260 const char *errmsg; 1261 struct lifreq lifr; 1262 int on = 1; 1263 1264 if (lif->lif_sock_ip_fd != -1) { 1265 dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s", 1266 lif->lif_name); 1267 return (B_FALSE); 1268 } 1269 1270 lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0); 1271 if (lif->lif_sock_ip_fd == -1) { 1272 errmsg = "cannot create v4 socket"; 1273 goto failure; 1274 } 1275 1276 if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, addr_hbo)) { 1277 errmsg = "cannot bind v4 socket"; 1278 goto failure; 1279 } 1280 1281 /* 1282 * If we bound to INADDR_ANY, we have no IFF_UP source address to use. 1283 * Thus, enable IP_UNSPEC_SRC so that we can send packets with an 1284 * unspecified (0.0.0.0) address. Also, enable IP_DHCPINIT_IF so that 1285 * the IP module will accept unicast DHCP traffic regardless of the IP 1286 * address it's sent to. (We'll then figure out which packets are 1287 * ours based on the xid.) 1288 */ 1289 if (addr_hbo == INADDR_ANY) { 1290 if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_UNSPEC_SRC, 1291 &on, sizeof (int)) == -1) { 1292 errmsg = "cannot set IP_UNSPEC_SRC"; 1293 goto failure; 1294 } 1295 1296 if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF, 1297 &lif->lif_pif->pif_index, sizeof (int)) == -1) { 1298 errmsg = "cannot set IP_DHCPINIT_IF"; 1299 goto failure; 1300 } 1301 } 1302 1303 if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF, 1304 &lif->lif_pif->pif_index, sizeof (int)) == -1) { 1305 errmsg = "cannot set IP_BOUND_IF"; 1306 goto failure; 1307 } 1308 1309 /* 1310 * Make sure at least one lif on the interface we used in IP_BOUND_IF 1311 * is IFF_UP so that we can send and receive IP packets. 1312 */ 1313 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1314 if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 1315 errmsg = "cannot get interface flags"; 1316 goto failure; 1317 } 1318 1319 if (!(lifr.lifr_flags & IFF_UP)) { 1320 /* 1321 * Start from a clean slate. 1322 */ 1323 canonize_lif(lif, B_FALSE); 1324 1325 lifr.lifr_flags |= IFF_UP; 1326 if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 1327 errmsg = "cannot bring up"; 1328 goto failure; 1329 } 1330 lif->lif_flags = lifr.lifr_flags; 1331 1332 /* 1333 * When bringing 0.0.0.0 IFF_UP, the kernel changes the 1334 * netmask to 255.0.0.0, so re-fetch our expected netmask. 1335 */ 1336 if (ioctl(v4_sock_fd, SIOCGLIFNETMASK, &lifr) == -1) { 1337 errmsg = "cannot get netmask"; 1338 goto failure; 1339 } 1340 1341 lif->lif_netmask = 1342 ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr; 1343 } 1344 1345 lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN, 1346 dhcp_packet_lif, lif); 1347 if (lif->lif_packet_id == -1) { 1348 errmsg = "cannot register to receive DHCP packets"; 1349 goto failure; 1350 } 1351 1352 return (B_TRUE); 1353 failure: 1354 dhcpmsg(MSG_ERR, "open_ip_lif: %s: %s", lif->lif_name, errmsg); 1355 close_ip_lif(lif); 1356 return (B_FALSE); 1357 } 1358 1359 /* 1360 * close_ip_lif(): close an IP socket for I/O on a given LIF. 1361 * 1362 * input: dhcp_lif_t *: the logical interface to operate on 1363 * output: none 1364 */ 1365 1366 void 1367 close_ip_lif(dhcp_lif_t *lif) 1368 { 1369 if (lif->lif_packet_id != -1) { 1370 (void) iu_unregister_event(eh, lif->lif_packet_id, NULL); 1371 lif->lif_packet_id = -1; 1372 } 1373 if (lif->lif_sock_ip_fd != -1) { 1374 (void) close(lif->lif_sock_ip_fd); 1375 lif->lif_sock_ip_fd = -1; 1376 } 1377 } 1378 1379 /* 1380 * lif_mark_decline(): mark a LIF as having been declined due to a duplicate 1381 * address or some other conflict. This is used in 1382 * send_declines() to report failure back to the server. 1383 * 1384 * input: dhcp_lif_t *: the logical interface to operate on 1385 * const char *: text string explaining why the address is declined 1386 * output: none 1387 */ 1388 1389 void 1390 lif_mark_decline(dhcp_lif_t *lif, const char *reason) 1391 { 1392 if (lif->lif_declined == NULL) { 1393 dhcp_lease_t *dlp; 1394 1395 lif->lif_declined = reason; 1396 if ((dlp = lif->lif_lease) != NULL) 1397 dlp->dl_smach->dsm_lif_down++; 1398 } 1399 } 1400 1401 /* 1402 * schedule_lif_timer(): schedules the LIF-related timer 1403 * 1404 * input: dhcp_lif_t *: the logical interface to operate on 1405 * dhcp_timer_t *: the timer to schedule 1406 * iu_tq_callback_t *: the callback to call upon firing 1407 * output: boolean_t: B_TRUE if the timer was scheduled successfully 1408 */ 1409 1410 boolean_t 1411 schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire) 1412 { 1413 /* 1414 * If there's a timer running, cancel it and release its lease 1415 * reference. 1416 */ 1417 if (dt->dt_id != -1) { 1418 if (!cancel_timer(dt)) 1419 return (B_FALSE); 1420 release_lif(lif); 1421 } 1422 1423 if (schedule_timer(dt, expire, lif)) { 1424 hold_lif(lif); 1425 return (B_TRUE); 1426 } else { 1427 dhcpmsg(MSG_WARNING, 1428 "schedule_lif_timer: cannot schedule timer"); 1429 return (B_FALSE); 1430 } 1431 } 1432 1433 /* 1434 * cancel_lif_timer(): cancels a LIF-related timer 1435 * 1436 * input: dhcp_lif_t *: the logical interface to operate on 1437 * dhcp_timer_t *: the timer to cancel 1438 * output: none 1439 */ 1440 1441 static void 1442 cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt) 1443 { 1444 if (dt->dt_id == -1) 1445 return; 1446 if (cancel_timer(dt)) { 1447 dhcpmsg(MSG_DEBUG2, 1448 "cancel_lif_timer: canceled expiry timer on %s", 1449 lif->lif_name); 1450 release_lif(lif); 1451 } else { 1452 dhcpmsg(MSG_WARNING, 1453 "cancel_lif_timer: cannot cancel timer on %s", 1454 lif->lif_name); 1455 } 1456 } 1457 1458 /* 1459 * cancel_lif_timers(): cancels the LIF-related timers 1460 * 1461 * input: dhcp_lif_t *: the logical interface to operate on 1462 * output: none 1463 */ 1464 1465 void 1466 cancel_lif_timers(dhcp_lif_t *lif) 1467 { 1468 cancel_lif_timer(lif, &lif->lif_preferred); 1469 cancel_lif_timer(lif, &lif->lif_expire); 1470 } 1471 1472 /* 1473 * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common 1474 * file descriptors (v4_sock_fd and v6_sock_fd). 1475 * 1476 * input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4 1477 * output: none 1478 */ 1479 1480 uint_t 1481 get_max_mtu(boolean_t isv6) 1482 { 1483 uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu; 1484 1485 if (*mtup == 0) { 1486 dhcp_pif_t *pif; 1487 dhcp_lif_t *lif; 1488 struct lifreq lifr; 1489 1490 /* Set an arbitrary lower bound */ 1491 *mtup = 1024; 1492 pif = isv6 ? v6root : v4root; 1493 for (; pif != NULL; pif = pif->pif_next) { 1494 for (lif = pif->pif_lifs; lif != NULL; 1495 lif = lif->lif_next) { 1496 (void) strlcpy(lifr.lifr_name, lif->lif_name, 1497 LIFNAMSIZ); 1498 if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) != 1499 -1 && lifr.lifr_mtu > *mtup) { 1500 *mtup = lifr.lifr_mtu; 1501 } 1502 } 1503 } 1504 } 1505 return (*mtup); 1506 } 1507 1508 /* 1509 * expired_lif_state(): summarize the state of expired LIFs on a given state 1510 * machine. 1511 * 1512 * input: dhcp_smach_t *: the state machine to scan 1513 * output: dhcp_expire_t: overall state 1514 */ 1515 1516 dhcp_expire_t 1517 expired_lif_state(dhcp_smach_t *dsmp) 1518 { 1519 dhcp_lease_t *dlp; 1520 dhcp_lif_t *lif; 1521 uint_t nlifs; 1522 uint_t numlifs; 1523 uint_t numexp; 1524 1525 numlifs = numexp = 0; 1526 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 1527 lif = dlp->dl_lifs; 1528 nlifs = dlp->dl_nlifs; 1529 numlifs += nlifs; 1530 for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 1531 if (lif->lif_expired) 1532 numexp++; 1533 } 1534 } 1535 if (numlifs == 0) 1536 return (DHCP_EXP_NOLIFS); 1537 else if (numexp == 0) 1538 return (DHCP_EXP_NOEXP); 1539 else if (numlifs == numexp) 1540 return (DHCP_EXP_ALLEXP); 1541 else 1542 return (DHCP_EXP_SOMEEXP); 1543 } 1544 1545 /* 1546 * find_expired_lif(): find the first expired LIF on a given state machine 1547 * 1548 * input: dhcp_smach_t *: the state machine to scan 1549 * output: dhcp_lif_t *: the first expired LIF, or NULL if none. 1550 */ 1551 1552 dhcp_lif_t * 1553 find_expired_lif(dhcp_smach_t *dsmp) 1554 { 1555 dhcp_lease_t *dlp; 1556 dhcp_lif_t *lif; 1557 uint_t nlifs; 1558 1559 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 1560 lif = dlp->dl_lifs; 1561 nlifs = dlp->dl_nlifs; 1562 for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 1563 if (lif->lif_expired) 1564 return (lif); 1565 } 1566 } 1567 return (NULL); 1568 } 1569 1570 /* 1571 * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING. Used 1572 * only for DHCPv6. 1573 * 1574 * input: none 1575 * output: none 1576 */ 1577 1578 void 1579 remove_v6_strays(void) 1580 { 1581 struct lifnum lifn; 1582 struct lifconf lifc; 1583 struct lifreq *lifrp, *lifrmax; 1584 uint_t numifs; 1585 uint64_t flags; 1586 1587 /* 1588 * Get the approximate number of interfaces in the system. It's only 1589 * approximate because the system is dynamic -- interfaces may be 1590 * plumbed or unplumbed at any time. This is also the reason for the 1591 * "+ 10" fudge factor: we're trying to avoid unnecessary looping. 1592 */ 1593 (void) memset(&lifn, 0, sizeof (lifn)); 1594 lifn.lifn_family = AF_INET6; 1595 lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 1596 if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) { 1597 dhcpmsg(MSG_ERR, 1598 "remove_v6_strays: cannot read number of interfaces"); 1599 numifs = 10; 1600 } else { 1601 numifs = lifn.lifn_count + 10; 1602 } 1603 1604 /* 1605 * Get the interface information. We do this in a loop so that we can 1606 * recover from EINVAL from the kernel -- delivered when the buffer is 1607 * too small. 1608 */ 1609 (void) memset(&lifc, 0, sizeof (lifc)); 1610 lifc.lifc_family = AF_INET6; 1611 lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 1612 for (;;) { 1613 lifc.lifc_len = numifs * sizeof (*lifrp); 1614 lifrp = realloc(lifc.lifc_buf, lifc.lifc_len); 1615 if (lifrp == NULL) { 1616 dhcpmsg(MSG_ERR, 1617 "remove_v6_strays: cannot allocate memory"); 1618 free(lifc.lifc_buf); 1619 return; 1620 } 1621 lifc.lifc_buf = (caddr_t)lifrp; 1622 errno = 0; 1623 if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 && 1624 lifc.lifc_len < numifs * sizeof (*lifrp)) 1625 break; 1626 if (errno == 0 || errno == EINVAL) { 1627 numifs <<= 1; 1628 } else { 1629 dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF"); 1630 free(lifc.lifc_buf); 1631 return; 1632 } 1633 } 1634 1635 lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp); 1636 for (; lifrp < lifrmax; lifrp++) { 1637 /* 1638 * Get the interface flags; we're interested in the DHCP ones. 1639 */ 1640 if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1) 1641 continue; 1642 flags = lifrp->lifr_flags; 1643 if (!(flags & IFF_DHCPRUNNING)) 1644 continue; 1645 /* 1646 * If the interface has a link-local address, then we don't 1647 * control it. Just remove the flag. 1648 */ 1649 if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1) 1650 continue; 1651 if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp-> 1652 lifr_addr)->sin6_addr)) { 1653 lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING; 1654 (void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp); 1655 continue; 1656 } 1657 /* 1658 * All others are (or were) under our control. Clean up by 1659 * removing them. 1660 */ 1661 if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) { 1662 dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s", 1663 lifrp->lifr_name); 1664 } else if (errno != ENXIO) { 1665 dhcpmsg(MSG_ERR, 1666 "remove_v6_strays: SIOCLIFREMOVEIF %s", 1667 lifrp->lifr_name); 1668 } 1669 } 1670 free(lifc.lifc_buf); 1671 } 1672