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