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 lif->lif_flags = lifr.lifr_flags; 807 808 if (dhcponly && !(lifr.lifr_flags & IFF_DHCPRUNNING)) { 809 dhcpmsg(MSG_INFO, 810 "canonize_lif: cannot clear %s; flags are %llx", 811 lif->lif_name, lifr.lifr_flags); 812 return; 813 } 814 815 (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); 816 if (isv6) { 817 struct sockaddr_in6 *sin6 = 818 (struct sockaddr_in6 *)&lifr.lifr_addr; 819 820 sin6->sin6_family = AF_INET6; 821 sin6->sin6_addr = my_in6addr_any; 822 } else { 823 struct sockaddr_in *sinv = 824 (struct sockaddr_in *)&lifr.lifr_addr; 825 826 sinv->sin_family = AF_INET; 827 sinv->sin_addr.s_addr = htonl(INADDR_ANY); 828 } 829 830 if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) { 831 dhcpmsg(MSG_ERR, 832 "canonize_lif: can't clear local address on %s", 833 lif->lif_name); 834 } 835 836 /* Netmask is under in.ndpd control with IPv6 */ 837 if (!isv6 && ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) { 838 dhcpmsg(MSG_ERR, "canonize_lif: can't clear netmask on %s", 839 lif->lif_name); 840 } 841 842 if (lif->lif_flags & IFF_POINTOPOINT) { 843 if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) { 844 dhcpmsg(MSG_ERR, 845 "canonize_lif: can't clear remote address on %s", 846 lif->lif_name); 847 } 848 } else if (!isv6) { 849 if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) { 850 dhcpmsg(MSG_ERR, 851 "canonize_lif: can't clear broadcast address on %s", 852 lif->lif_name); 853 } 854 } 855 } 856 857 /* 858 * plumb_lif(): Adds the LIF to the system. This is used for all 859 * DHCPv6-derived interfaces. The returned LIF has a hold 860 * on it. 861 * 862 * input: dhcp_lif_t *: the interface to unplumb 863 * output: none 864 */ 865 866 dhcp_lif_t * 867 plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr) 868 { 869 dhcp_lif_t *lif; 870 char abuf[INET6_ADDRSTRLEN]; 871 struct lifreq lifr; 872 struct sockaddr_in6 *sin6; 873 int error; 874 875 (void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)); 876 877 for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 878 if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) { 879 dhcpmsg(MSG_ERR, 880 "plumb_lif: entry for %s already exists!", abuf); 881 return (NULL); 882 } 883 } 884 885 /* First, create a new zero-address logical interface */ 886 (void) memset(&lifr, 0, sizeof (lifr)); 887 (void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name)); 888 if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) { 889 dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name); 890 return (NULL); 891 } 892 893 /* Next, set the netmask to all ones */ 894 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; 895 sin6->sin6_family = AF_INET6; 896 (void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr)); 897 if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) { 898 dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s", 899 lifr.lifr_name); 900 goto failure; 901 } 902 903 /* Now set the interface address */ 904 sin6->sin6_addr = *addr; 905 if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) { 906 dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s", 907 lifr.lifr_name, abuf); 908 goto failure; 909 } 910 911 /* Mark the interface up */ 912 if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 913 dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s", 914 lifr.lifr_name); 915 goto failure; 916 } 917 lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING; 918 if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 919 dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s", 920 lifr.lifr_name); 921 goto failure; 922 } 923 924 /* Now we can create the internal LIF structure */ 925 hold_pif(pif); 926 if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL) 927 goto failure; 928 929 dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf, 930 lif->lif_name); 931 lif->lif_plumbed = B_TRUE; 932 933 return (lif); 934 935 failure: 936 if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 937 errno != ENXIO) { 938 dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s", 939 lifr.lifr_name); 940 } 941 return (NULL); 942 } 943 944 /* 945 * unplumb_lif(): Removes the LIF from dhcpagent and the system. This is used 946 * for all interfaces configured by DHCP (those in leases). 947 * 948 * input: dhcp_lif_t *: the interface to unplumb 949 * output: none 950 */ 951 952 void 953 unplumb_lif(dhcp_lif_t *lif) 954 { 955 dhcp_lease_t *dlp; 956 957 if (lif->lif_plumbed) { 958 struct lifreq lifr; 959 960 (void) memset(&lifr, 0, sizeof (lifr)); 961 (void) strlcpy(lifr.lifr_name, lif->lif_name, 962 sizeof (lifr.lifr_name)); 963 if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 && 964 errno != ENXIO) { 965 dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s", 966 lif->lif_name); 967 } 968 lif->lif_plumbed = B_FALSE; 969 } 970 971 /* 972 * Special case: if we're "unplumbing" the main LIF for DHCPv4, then 973 * just canonize it and remove it from the lease. 974 */ 975 if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) { 976 canonize_lif(lif, B_TRUE); 977 cancel_lif_timers(lif); 978 if (lif->lif_declined != NULL) { 979 dlp->dl_smach->dsm_lif_down--; 980 lif->lif_declined = NULL; 981 } 982 dlp->dl_nlifs = 0; 983 dlp->dl_lifs = NULL; 984 lif->lif_lease = NULL; 985 release_lif(lif); 986 } else { 987 remove_lif(lif); 988 } 989 } 990 991 /* 992 * attach_lif(): create a new logical interface, creating the physical 993 * interface as necessary. 994 * 995 * input: const char *: the logical interface name 996 * boolean_t: B_TRUE for IPv6 997 * int *: set to DHCP_IPC_E_* if creation fails 998 * output: dhcp_lif_t *: pointer to new entry, or NULL on failure 999 */ 1000 1001 dhcp_lif_t * 1002 attach_lif(const char *lname, boolean_t isv6, int *error) 1003 { 1004 dhcp_pif_t *pif; 1005 char pname[LIFNAMSIZ], *cp; 1006 1007 (void) strlcpy(pname, lname, sizeof (pname)); 1008 if ((cp = strchr(pname, ':')) != NULL) 1009 *cp = '\0'; 1010 1011 if ((pif = lookup_pif_by_name(pname, isv6)) != NULL) 1012 hold_pif(pif); 1013 else if ((pif = insert_pif(pname, isv6, error)) == NULL) 1014 return (NULL); 1015 1016 if (lookup_lif_by_name(lname, pif) != NULL) { 1017 dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!", 1018 lname); 1019 release_pif(pif); 1020 *error = DHCP_IPC_E_INVIF; 1021 return (NULL); 1022 } 1023 1024 /* If LIF creation fails, then insert_lif discards our PIF hold */ 1025 return (insert_lif(pif, lname, error)); 1026 } 1027 1028 /* 1029 * set_lif_dhcp(): Set logical interface flags to show that it's managed 1030 * by DHCP. 1031 * 1032 * input: dhcp_lif_t *: the logical interface 1033 * boolean_t: B_TRUE if adopting 1034 * output: int: set to DHCP_IPC_E_* if operation fails 1035 */ 1036 1037 int 1038 set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting) 1039 { 1040 int fd; 1041 int err; 1042 struct lifreq lifr; 1043 1044 fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1045 1046 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1047 1048 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 1049 err = errno; 1050 dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s", 1051 lif->lif_name); 1052 return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT); 1053 } 1054 lif->lif_flags = lifr.lifr_flags; 1055 1056 /* 1057 * Check for conflicting sources of address control, and other 1058 * unacceptable configurations. 1059 */ 1060 if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY| 1061 IFF_VIRTUAL)) { 1062 dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx", 1063 lif->lif_name, lifr.lifr_flags); 1064 return (DHCP_IPC_E_INVIF); 1065 } 1066 1067 /* 1068 * if DHCPRUNNING is already set on the interface and we're 1069 * not adopting it, the agent probably crashed and burned. 1070 * note it, but don't let it stop the proceedings. we're 1071 * pretty sure we're not already running, since we wouldn't 1072 * have been able to bind to our IPC port. 1073 */ 1074 1075 if (lifr.lifr_flags & IFF_DHCPRUNNING) { 1076 if (!is_adopting) { 1077 dhcpmsg(MSG_WARNING, "set_lif_dhcp: DHCP flag already " 1078 "set on %s", lif->lif_name); 1079 } 1080 } else { 1081 lifr.lifr_flags |= IFF_DHCPRUNNING; 1082 if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 1083 dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s", 1084 lif->lif_name); 1085 return (DHCP_IPC_E_INT); 1086 } 1087 lif->lif_flags = lifr.lifr_flags; 1088 } 1089 return (DHCP_IPC_SUCCESS); 1090 } 1091 1092 /* 1093 * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer 1094 * managed by DHCP. 1095 * 1096 * input: dhcp_lif_t *: the logical interface 1097 * output: none 1098 */ 1099 1100 static void 1101 clear_lif_dhcp(dhcp_lif_t *lif) 1102 { 1103 int fd; 1104 struct lifreq lifr; 1105 1106 fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1107 1108 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1109 1110 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 1111 return; 1112 1113 if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) 1114 return; 1115 1116 lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING; 1117 (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 1118 } 1119 1120 /* 1121 * set_lif_deprecated(): Set the "deprecated" flag to tell users that this 1122 * address will be going away. As the interface is 1123 * going away, we don't care if there are errors. 1124 * 1125 * input: dhcp_lif_t *: the logical interface 1126 * output: none 1127 */ 1128 1129 void 1130 set_lif_deprecated(dhcp_lif_t *lif) 1131 { 1132 int fd; 1133 struct lifreq lifr; 1134 1135 if (lif->lif_flags & IFF_DEPRECATED) 1136 return; 1137 1138 fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1139 1140 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1141 1142 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) 1143 return; 1144 1145 if (lifr.lifr_flags & IFF_DEPRECATED) 1146 return; 1147 1148 lifr.lifr_flags |= IFF_DEPRECATED; 1149 (void) ioctl(fd, SIOCSLIFFLAGS, &lifr); 1150 lif->lif_flags = lifr.lifr_flags; 1151 } 1152 1153 /* 1154 * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this 1155 * address will not be going away. This happens if we 1156 * get a renewal after preferred lifetime but before 1157 * the valid lifetime. 1158 * 1159 * input: dhcp_lif_t *: the logical interface 1160 * output: boolean_t: B_TRUE on success. 1161 */ 1162 1163 boolean_t 1164 clear_lif_deprecated(dhcp_lif_t *lif) 1165 { 1166 int fd; 1167 struct lifreq lifr; 1168 1169 fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 1170 1171 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1172 1173 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 1174 dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s", 1175 lif->lif_name); 1176 return (B_FALSE); 1177 } 1178 1179 /* 1180 * Check for conflicting sources of address control, and other 1181 * unacceptable configurations. 1182 */ 1183 if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY| 1184 IFF_VIRTUAL)) { 1185 dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags " 1186 "are %llx", lif->lif_name, lifr.lifr_flags); 1187 return (B_FALSE); 1188 } 1189 1190 if (!(lifr.lifr_flags & IFF_DEPRECATED)) 1191 return (B_TRUE); 1192 1193 lifr.lifr_flags &= ~IFF_DEPRECATED; 1194 if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) { 1195 dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s", 1196 lif->lif_name); 1197 return (B_FALSE); 1198 } else { 1199 lif->lif_flags = lifr.lifr_flags; 1200 return (B_TRUE); 1201 } 1202 } 1203 1204 /* 1205 * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only). 1206 * 1207 * input: dhcp_lif_t *: the logical interface to operate on 1208 * in_addr_t: the address the socket will be bound to (in hbo) 1209 * output: boolean_t: B_TRUE if the socket was opened successfully. 1210 */ 1211 1212 boolean_t 1213 open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo) 1214 { 1215 const char *errmsg; 1216 struct lifreq lifr; 1217 int on = 1; 1218 uchar_t ttl = 255; 1219 1220 if (lif->lif_sock_ip_fd != -1) { 1221 dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s", 1222 lif->lif_name); 1223 return (B_FALSE); 1224 } 1225 1226 lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0); 1227 if (lif->lif_sock_ip_fd == -1) { 1228 errmsg = "cannot create v4 socket"; 1229 goto failure; 1230 } 1231 1232 if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, addr_hbo)) { 1233 errmsg = "cannot bind v4 socket"; 1234 goto failure; 1235 } 1236 1237 /* 1238 * If we bound to INADDR_ANY, we have no IFF_UP source address to use. 1239 * Thus, enable IP_UNSPEC_SRC so that we can send packets with an 1240 * unspecified (0.0.0.0) address. Also, enable IP_DHCPINIT_IF so that 1241 * the IP module will accept unicast DHCP traffic regardless of the IP 1242 * address it's sent to. (We'll then figure out which packets are 1243 * ours based on the xid.) 1244 */ 1245 if (addr_hbo == INADDR_ANY) { 1246 if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_UNSPEC_SRC, 1247 &on, sizeof (int)) == -1) { 1248 errmsg = "cannot set IP_UNSPEC_SRC"; 1249 goto failure; 1250 } 1251 1252 if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF, 1253 &lif->lif_pif->pif_index, sizeof (int)) == -1) { 1254 errmsg = "cannot set IP_DHCPINIT_IF"; 1255 goto failure; 1256 } 1257 } 1258 1259 /* 1260 * Unfortunately, some hardware (such as the Linksys WRT54GC) 1261 * decrements the TTL *prior* to accepting DHCP traffic destined 1262 * for it. To workaround this, tell IP to use a TTL of 255 for 1263 * broadcast packets sent from this socket. 1264 */ 1265 if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BROADCAST_TTL, &ttl, 1266 sizeof (uchar_t)) == -1) { 1267 errmsg = "cannot set IP_BROADCAST_TTL"; 1268 goto failure; 1269 } 1270 1271 if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF, 1272 &lif->lif_pif->pif_index, sizeof (int)) == -1) { 1273 errmsg = "cannot set IP_BOUND_IF"; 1274 goto failure; 1275 } 1276 1277 /* 1278 * Make sure at least one lif on the interface we used in IP_BOUND_IF 1279 * is IFF_UP so that we can send and receive IP packets. 1280 */ 1281 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1282 if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 1283 errmsg = "cannot get interface flags"; 1284 goto failure; 1285 } 1286 1287 if (!(lifr.lifr_flags & IFF_UP)) { 1288 /* 1289 * Start from a clean slate. 1290 */ 1291 canonize_lif(lif, B_FALSE); 1292 1293 lifr.lifr_flags |= IFF_UP; 1294 if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 1295 errmsg = "cannot bring up"; 1296 goto failure; 1297 } 1298 lif->lif_flags = lifr.lifr_flags; 1299 1300 /* 1301 * When bringing 0.0.0.0 IFF_UP, the kernel changes the 1302 * netmask to 255.0.0.0, so re-fetch our expected netmask. 1303 */ 1304 if (ioctl(v4_sock_fd, SIOCGLIFNETMASK, &lifr) == -1) { 1305 errmsg = "cannot get netmask"; 1306 goto failure; 1307 } 1308 1309 lif->lif_netmask = 1310 ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr; 1311 } 1312 1313 lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN, 1314 dhcp_packet_lif, lif); 1315 if (lif->lif_packet_id == -1) { 1316 errmsg = "cannot register to receive DHCP packets"; 1317 goto failure; 1318 } 1319 1320 return (B_TRUE); 1321 failure: 1322 dhcpmsg(MSG_ERR, "open_ip_lif: %s: %s", lif->lif_name, errmsg); 1323 close_ip_lif(lif); 1324 return (B_FALSE); 1325 } 1326 1327 /* 1328 * close_ip_lif(): close an IP socket for I/O on a given LIF. 1329 * 1330 * input: dhcp_lif_t *: the logical interface to operate on 1331 * output: none 1332 */ 1333 1334 void 1335 close_ip_lif(dhcp_lif_t *lif) 1336 { 1337 if (lif->lif_packet_id != -1) { 1338 (void) iu_unregister_event(eh, lif->lif_packet_id, NULL); 1339 lif->lif_packet_id = -1; 1340 } 1341 if (lif->lif_sock_ip_fd != -1) { 1342 (void) close(lif->lif_sock_ip_fd); 1343 lif->lif_sock_ip_fd = -1; 1344 } 1345 } 1346 1347 /* 1348 * lif_mark_decline(): mark a LIF as having been declined due to a duplicate 1349 * address or some other conflict. This is used in 1350 * send_declines() to report failure back to the server. 1351 * 1352 * input: dhcp_lif_t *: the logical interface to operate on 1353 * const char *: text string explaining why the address is declined 1354 * output: none 1355 */ 1356 1357 void 1358 lif_mark_decline(dhcp_lif_t *lif, const char *reason) 1359 { 1360 if (lif->lif_declined == NULL) { 1361 dhcp_lease_t *dlp; 1362 1363 lif->lif_declined = reason; 1364 if ((dlp = lif->lif_lease) != NULL) 1365 dlp->dl_smach->dsm_lif_down++; 1366 } 1367 } 1368 1369 /* 1370 * schedule_lif_timer(): schedules the LIF-related timer 1371 * 1372 * input: dhcp_lif_t *: the logical interface to operate on 1373 * dhcp_timer_t *: the timer to schedule 1374 * iu_tq_callback_t *: the callback to call upon firing 1375 * output: boolean_t: B_TRUE if the timer was scheduled successfully 1376 */ 1377 1378 boolean_t 1379 schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire) 1380 { 1381 /* 1382 * If there's a timer running, cancel it and release its lease 1383 * reference. 1384 */ 1385 if (dt->dt_id != -1) { 1386 if (!cancel_timer(dt)) 1387 return (B_FALSE); 1388 release_lif(lif); 1389 } 1390 1391 if (schedule_timer(dt, expire, lif)) { 1392 hold_lif(lif); 1393 return (B_TRUE); 1394 } else { 1395 dhcpmsg(MSG_WARNING, 1396 "schedule_lif_timer: cannot schedule timer"); 1397 return (B_FALSE); 1398 } 1399 } 1400 1401 /* 1402 * cancel_lif_timer(): cancels a LIF-related timer 1403 * 1404 * input: dhcp_lif_t *: the logical interface to operate on 1405 * dhcp_timer_t *: the timer to cancel 1406 * output: none 1407 */ 1408 1409 static void 1410 cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt) 1411 { 1412 if (dt->dt_id == -1) 1413 return; 1414 if (cancel_timer(dt)) { 1415 dhcpmsg(MSG_DEBUG2, 1416 "cancel_lif_timer: canceled expiry timer on %s", 1417 lif->lif_name); 1418 release_lif(lif); 1419 } else { 1420 dhcpmsg(MSG_WARNING, 1421 "cancel_lif_timer: cannot cancel timer on %s", 1422 lif->lif_name); 1423 } 1424 } 1425 1426 /* 1427 * cancel_lif_timers(): cancels the LIF-related timers 1428 * 1429 * input: dhcp_lif_t *: the logical interface to operate on 1430 * output: none 1431 */ 1432 1433 void 1434 cancel_lif_timers(dhcp_lif_t *lif) 1435 { 1436 cancel_lif_timer(lif, &lif->lif_preferred); 1437 cancel_lif_timer(lif, &lif->lif_expire); 1438 } 1439 1440 /* 1441 * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common 1442 * file descriptors (v4_sock_fd and v6_sock_fd). 1443 * 1444 * input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4 1445 * output: none 1446 */ 1447 1448 uint_t 1449 get_max_mtu(boolean_t isv6) 1450 { 1451 uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu; 1452 1453 if (*mtup == 0) { 1454 dhcp_pif_t *pif; 1455 dhcp_lif_t *lif; 1456 struct lifreq lifr; 1457 1458 /* Set an arbitrary lower bound */ 1459 *mtup = 1024; 1460 pif = isv6 ? v6root : v4root; 1461 for (; pif != NULL; pif = pif->pif_next) { 1462 for (lif = pif->pif_lifs; lif != NULL; 1463 lif = lif->lif_next) { 1464 (void) strlcpy(lifr.lifr_name, lif->lif_name, 1465 LIFNAMSIZ); 1466 if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) != 1467 -1 && lifr.lifr_mtu > *mtup) { 1468 *mtup = lifr.lifr_mtu; 1469 } 1470 } 1471 } 1472 } 1473 return (*mtup); 1474 } 1475 1476 /* 1477 * expired_lif_state(): summarize the state of expired LIFs on a given state 1478 * machine. 1479 * 1480 * input: dhcp_smach_t *: the state machine to scan 1481 * output: dhcp_expire_t: overall state 1482 */ 1483 1484 dhcp_expire_t 1485 expired_lif_state(dhcp_smach_t *dsmp) 1486 { 1487 dhcp_lease_t *dlp; 1488 dhcp_lif_t *lif; 1489 uint_t nlifs; 1490 uint_t numlifs; 1491 uint_t numexp; 1492 1493 numlifs = numexp = 0; 1494 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 1495 lif = dlp->dl_lifs; 1496 nlifs = dlp->dl_nlifs; 1497 numlifs += nlifs; 1498 for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 1499 if (lif->lif_expired) 1500 numexp++; 1501 } 1502 } 1503 if (numlifs == 0) 1504 return (DHCP_EXP_NOLIFS); 1505 else if (numexp == 0) 1506 return (DHCP_EXP_NOEXP); 1507 else if (numlifs == numexp) 1508 return (DHCP_EXP_ALLEXP); 1509 else 1510 return (DHCP_EXP_SOMEEXP); 1511 } 1512 1513 /* 1514 * find_expired_lif(): find the first expired LIF on a given state machine 1515 * 1516 * input: dhcp_smach_t *: the state machine to scan 1517 * output: dhcp_lif_t *: the first expired LIF, or NULL if none. 1518 */ 1519 1520 dhcp_lif_t * 1521 find_expired_lif(dhcp_smach_t *dsmp) 1522 { 1523 dhcp_lease_t *dlp; 1524 dhcp_lif_t *lif; 1525 uint_t nlifs; 1526 1527 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 1528 lif = dlp->dl_lifs; 1529 nlifs = dlp->dl_nlifs; 1530 for (; nlifs > 0; nlifs--, lif = lif->lif_next) { 1531 if (lif->lif_expired) 1532 return (lif); 1533 } 1534 } 1535 return (NULL); 1536 } 1537 1538 /* 1539 * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING. Used 1540 * only for DHCPv6. 1541 * 1542 * input: none 1543 * output: none 1544 */ 1545 1546 void 1547 remove_v6_strays(void) 1548 { 1549 struct lifnum lifn; 1550 struct lifconf lifc; 1551 struct lifreq *lifrp, *lifrmax; 1552 uint_t numifs; 1553 uint64_t flags; 1554 1555 /* 1556 * Get the approximate number of interfaces in the system. It's only 1557 * approximate because the system is dynamic -- interfaces may be 1558 * plumbed or unplumbed at any time. This is also the reason for the 1559 * "+ 10" fudge factor: we're trying to avoid unnecessary looping. 1560 */ 1561 (void) memset(&lifn, 0, sizeof (lifn)); 1562 lifn.lifn_family = AF_INET6; 1563 lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 1564 if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) { 1565 dhcpmsg(MSG_ERR, 1566 "remove_v6_strays: cannot read number of interfaces"); 1567 numifs = 10; 1568 } else { 1569 numifs = lifn.lifn_count + 10; 1570 } 1571 1572 /* 1573 * Get the interface information. We do this in a loop so that we can 1574 * recover from EINVAL from the kernel -- delivered when the buffer is 1575 * too small. 1576 */ 1577 (void) memset(&lifc, 0, sizeof (lifc)); 1578 lifc.lifc_family = AF_INET6; 1579 lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY; 1580 for (;;) { 1581 lifc.lifc_len = numifs * sizeof (*lifrp); 1582 lifrp = realloc(lifc.lifc_buf, lifc.lifc_len); 1583 if (lifrp == NULL) { 1584 dhcpmsg(MSG_ERR, 1585 "remove_v6_strays: cannot allocate memory"); 1586 free(lifc.lifc_buf); 1587 return; 1588 } 1589 lifc.lifc_buf = (caddr_t)lifrp; 1590 errno = 0; 1591 if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 && 1592 lifc.lifc_len < numifs * sizeof (*lifrp)) 1593 break; 1594 if (errno == 0 || errno == EINVAL) { 1595 numifs <<= 1; 1596 } else { 1597 dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF"); 1598 free(lifc.lifc_buf); 1599 return; 1600 } 1601 } 1602 1603 lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp); 1604 for (; lifrp < lifrmax; lifrp++) { 1605 /* 1606 * Get the interface flags; we're interested in the DHCP ones. 1607 */ 1608 if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1) 1609 continue; 1610 flags = lifrp->lifr_flags; 1611 if (!(flags & IFF_DHCPRUNNING)) 1612 continue; 1613 /* 1614 * If the interface has a link-local address, then we don't 1615 * control it. Just remove the flag. 1616 */ 1617 if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1) 1618 continue; 1619 if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp-> 1620 lifr_addr)->sin6_addr)) { 1621 lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING; 1622 (void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp); 1623 continue; 1624 } 1625 /* 1626 * All others are (or were) under our control. Clean up by 1627 * removing them. 1628 */ 1629 if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) { 1630 dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s", 1631 lifrp->lifr_name); 1632 } else if (errno != ENXIO) { 1633 dhcpmsg(MSG_ERR, 1634 "remove_v6_strays: SIOCLIFREMOVEIF %s", 1635 lifrp->lifr_name); 1636 } 1637 } 1638 free(lifc.lifc_buf); 1639 } 1640