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 #include <sys/types.h> 27 #include <sys/socket.h> 28 #include <net/if.h> 29 #include <stdlib.h> 30 #include <sys/sockio.h> 31 #include <netinet/in.h> 32 #include <netinet/dhcp.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <search.h> 36 #include <libdevinfo.h> 37 #include <libdlpi.h> 38 #include <netinet/if_ether.h> 39 #include <arpa/inet.h> 40 #include <dhcpmsg.h> 41 #include <dhcp_inittab.h> 42 43 #include "agent.h" 44 #include "interface.h" 45 #include "util.h" 46 #include "packet.h" 47 #include "states.h" 48 49 dhcp_pif_t *v4root; 50 dhcp_pif_t *v6root; 51 52 static uint_t cached_v4_max_mtu, cached_v6_max_mtu; 53 54 /* 55 * Interface flags to watch: things that should be under our direct control. 56 */ 57 #define DHCP_IFF_WATCH (IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \ 58 IFF_TEMPORARY) 59 60 static void clear_lif_dhcp(dhcp_lif_t *); 61 62 /* 63 * insert_pif(): creates a new physical interface structure and chains it on 64 * the list. Initializes state that remains consistent across 65 * all use of the physical interface entry. 66 * 67 * input: const char *: the name of the physical interface 68 * boolean_t: if B_TRUE, this is DHCPv6 69 * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 70 * error code with the reason why 71 * output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure 72 */ 73 74 dhcp_pif_t * 75 insert_pif(const char *pname, boolean_t isv6, int *error) 76 { 77 dhcp_pif_t *pif; 78 struct lifreq lifr; 79 dlpi_handle_t dh = NULL; 80 int fd = isv6 ? v6_sock_fd : v4_sock_fd; 81 82 if ((pif = calloc(1, sizeof (*pif))) == NULL) { 83 dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for " 84 "%s", pname); 85 *error = DHCP_IPC_E_MEMORY; 86 return (NULL); 87 } 88 89 pif->pif_isv6 = isv6; 90 pif->pif_hold_count = 1; 91 pif->pif_running = B_TRUE; 92 93 if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) { 94 dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long", 95 pname); 96 *error = DHCP_IPC_E_INVIF; 97 goto failure; 98 } 99 100 /* 101 * This is a bit gross, but IP has a confused interface. We must 102 * assume that the zeroth LIF is plumbed, and must query there to get 103 * the interface index number. 104 */ 105 (void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ); 106 107 if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 108 *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT; 109 dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname); 110 goto failure; 111 } 112 pif->pif_index = lifr.lifr_index; 113 114 if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) { 115 *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT; 116 dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFMTU for %s", pname); 117 goto failure; 118 } 119 pif->pif_max = lifr.lifr_mtu; 120 121 if (pif->pif_max < DHCP_DEF_MAX_SIZE) { 122 dhcpmsg(MSG_ERROR, "insert_pif: MTU of %s is too small to " 123 "support DHCP (%u < %u)", pname, pif->pif_max, 124 DHCP_DEF_MAX_SIZE); 125 *error = DHCP_IPC_E_INVIF; 126 goto failure; 127 } 128 129 /* 130 * For IPv4, use DLPI to determine the hardware type, hardware 131 * address, and hardware address length. 132 */ 133 if (!isv6) { 134 int rc; 135 dlpi_info_t dlinfo; 136 137 if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) { 138 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s", 139 dlpi_strerror(rc)); 140 *error = DHCP_IPC_E_INVIF; 141 goto failure; 142 } 143 144 if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) { 145 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s", 146 dlpi_strerror(rc)); 147 *error = DHCP_IPC_E_INVIF; 148 goto failure; 149 } 150 151 if ((rc = dlpi_info(dh, &dlinfo, 0)) != DLPI_SUCCESS) { 152 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s", 153 dlpi_strerror(rc)); 154 *error = DHCP_IPC_E_INVIF; 155 goto failure; 156 } 157 158 pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype); 159 pif->pif_hwlen = dlinfo.di_physaddrlen; 160 161 dhcpmsg(MSG_DEBUG, "insert_pif: %s: hwtype %d, hwlen %d", 162 pname, pif->pif_hwtype, pif->pif_hwlen); 163 164 if (pif->pif_hwlen > 0) { 165 pif->pif_hwaddr = malloc(pif->pif_hwlen); 166 if (pif->pif_hwaddr == NULL) { 167 dhcpmsg(MSG_ERR, "insert_pif: cannot allocate " 168 "pif_hwaddr for %s", pname); 169 *error = DHCP_IPC_E_MEMORY; 170 goto failure; 171 } 172 (void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr, 173 pif->pif_hwlen); 174 } 175 176 dlpi_close(dh); 177 dh = NULL; 178 } 179 180 insque(pif, isv6 ? &v6root : &v4root); 181 182 return (pif); 183 failure: 184 if (dh != NULL) 185 dlpi_close(dh); 186 release_pif(pif); 187 return (NULL); 188 } 189 190 /* 191 * hold_pif(): acquire a hold on a physical interface structure. 192 * 193 * input: dhcp_pif_t *: a pointer to the PIF structure 194 * output: none 195 */ 196 197 void 198 hold_pif(dhcp_pif_t *pif) 199 { 200 pif->pif_hold_count++; 201 dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name, 202 pif->pif_hold_count); 203 } 204 205 /* 206 * release_pif(): release a hold on a physical interface structure; will 207 * destroy the structure on the last hold removed. 208 * 209 * input: dhcp_pif_t *: a pointer to the PIF structure 210 * output: none 211 */ 212 213 void 214 release_pif(dhcp_pif_t *pif) 215 { 216 if (pif->pif_hold_count == 0) { 217 dhcpmsg(MSG_CRIT, "release_pif: extraneous release"); 218 return; 219 } 220 221 if (--pif->pif_hold_count == 0) { 222 dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s", 223 pif->pif_name); 224 225 remque(pif); 226 free(pif->pif_hwaddr); 227 free(pif); 228 } else { 229 dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u", 230 pif->pif_name, pif->pif_hold_count); 231 } 232 } 233 234 /* 235 * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and 236 * previous PIF pointer (or NULL for list start). 237 * Caller is expected to iterate through all 238 * potential matches to find interface of interest. 239 * 240 * input: uint16_t: the interface index (truncated) 241 * dhcp_pif_t *: the previous PIF, or NULL for list start 242 * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 243 * output: dhcp_pif_t *: the next matching PIF, or NULL if not found 244 * note: This operates using the 'truncated' (16-bit) ifindex as seen by 245 * routing socket clients. The value stored in pif_index is the 246 * 32-bit ifindex from the ioctl interface. 247 */ 248 249 dhcp_pif_t * 250 lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6) 251 { 252 if (pif == NULL) 253 pif = isv6 ? v6root : v4root; 254 else 255 pif = pif->pif_next; 256 257 for (; pif != NULL; pif = pif->pif_next) { 258 if ((pif->pif_index & 0xffff) == ifindex) 259 break; 260 } 261 262 return (pif); 263 } 264 265 /* 266 * lookup_pif_by_name(): Looks up a physical interface entry given a name. 267 * 268 * input: const char *: the physical interface name 269 * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise 270 * output: dhcp_pif_t *: the matching PIF, or NULL if not found 271 */ 272 273 dhcp_pif_t * 274 lookup_pif_by_name(const char *pname, boolean_t isv6) 275 { 276 dhcp_pif_t *pif; 277 278 pif = isv6 ? v6root : v4root; 279 280 for (; pif != NULL; pif = pif->pif_next) { 281 if (strcmp(pif->pif_name, pname) == 0) 282 break; 283 } 284 285 return (pif); 286 } 287 288 /* 289 * pif_status(): update the physical interface up/down status. 290 * 291 * input: dhcp_pif_t *: the physical interface to be updated 292 * boolean_t: B_TRUE if the interface is going up 293 * output: none 294 */ 295 296 void 297 pif_status(dhcp_pif_t *pif, boolean_t isup) 298 { 299 dhcp_lif_t *lif; 300 dhcp_smach_t *dsmp; 301 302 pif->pif_running = isup; 303 dhcpmsg(MSG_DEBUG, "interface %s has %s", pif->pif_name, 304 isup ? "come back up" : "gone down"); 305 for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 306 for (dsmp = lif->lif_smachs; dsmp != NULL; 307 dsmp = dsmp->dsm_next) { 308 if (isup) 309 refresh_smach(dsmp); 310 else 311 remove_default_routes(dsmp); 312 } 313 } 314 } 315 316 /* Helper for insert_lif: extract addresses as defined */ 317 #define ASSIGN_ADDR(v4, v6, lf) \ 318 if (pif->pif_isv6) { \ 319 lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \ 320 } else { \ 321 lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \ 322 } 323 324 /* 325 * insert_lif(): Creates a new logical interface structure and chains it on 326 * the list for a given physical interface. Initializes state 327 * that remains consistent across all use of the logical 328 * interface entry. Caller's PIF hold is transferred to the 329 * LIF on success, and is dropped on failure. 330 * 331 * input: dhcp_pif_t *: pointer to the physical interface for this LIF 332 * const char *: the name of the logical interface 333 * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_* 334 * error code with the reason why 335 * output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure 336 */ 337 338 dhcp_lif_t * 339 insert_lif(dhcp_pif_t *pif, const char *lname, int *error) 340 { 341 dhcp_lif_t *lif; 342 int fd; 343 struct lifreq lifr; 344 345 if ((lif = calloc(1, sizeof (*lif))) == NULL) { 346 dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for " 347 "%s", lname); 348 *error = DHCP_IPC_E_MEMORY; 349 return (NULL); 350 } 351 352 lif->lif_sock_ip_fd = -1; 353 lif->lif_packet_id = -1; 354 lif->lif_iaid_id = -1; 355 lif->lif_hold_count = 1; 356 lif->lif_pif = pif; 357 lif->lif_removed = B_TRUE; 358 init_timer(&lif->lif_preferred, 0); 359 init_timer(&lif->lif_expire, 0); 360 361 if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) { 362 dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long", 363 lname); 364 *error = DHCP_IPC_E_INVIF; 365 goto failure; 366 } 367 368 (void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ); 369 370 fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd; 371 372 if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) 373 lif->lif_max = 1024; 374 else 375 lif->lif_max = lifr.lifr_mtu; 376 377 if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) { 378 if (errno == ENXIO) 379 *error = DHCP_IPC_E_INVIF; 380 else 381 *error = DHCP_IPC_E_INT; 382 dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname); 383 goto failure; 384 } 385 ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr); 386 387 if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) { 388 if (errno == ENXIO) 389 *error = DHCP_IPC_E_INVIF; 390 else 391 *error = DHCP_IPC_E_INT; 392 dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname); 393 goto failure; 394 } 395 ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr); 396 397 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 398 *error = DHCP_IPC_E_INT; 399 dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname); 400 goto failure; 401 } 402 lif->lif_flags = lifr.lifr_flags; 403 404 /* 405 * If we've just detected the interface going up or down, then signal 406 * an appropriate action. There may be other state machines here. 407 */ 408 if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) { 409 pif_status(pif, B_TRUE); 410 } else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) { 411 pif_status(pif, B_FALSE); 412 } 413 414 if (lifr.lifr_flags & IFF_POINTOPOINT) { 415 if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) { 416 *error = DHCP_IPC_E_INT; 417 dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s", 418 lname); 419 goto failure; 420 } 421 ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr); 422 } else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) { 423 if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) { 424 *error = DHCP_IPC_E_INT; 425 dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s", 426 lname); 427 goto failure; 428 } 429 lif->lif_broadcast = 430 ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr. 431 s_addr; 432 } 433 434 if (pif->pif_isv6) 435 cached_v6_max_mtu = 0; 436 else 437 cached_v4_max_mtu = 0; 438 439 lif->lif_removed = B_FALSE; 440 insque(lif, &pif->pif_lifs); 441 442 return (lif); 443 444 failure: 445 release_lif(lif); 446 return (NULL); 447 } 448 449 /* 450 * hold_lif(): acquire a hold on a logical interface structure. 451 * 452 * input: dhcp_lif_t *: a pointer to the LIF structure 453 * output: none 454 */ 455 456 void 457 hold_lif(dhcp_lif_t *lif) 458 { 459 lif->lif_hold_count++; 460 dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name, 461 lif->lif_hold_count); 462 } 463 464 /* 465 * release_lif(): release a hold on a logical interface structure; will 466 * destroy the structure on the last hold removed. 467 * 468 * input: dhcp_lif_t *: a pointer to the LIF structure 469 * output: none 470 */ 471 472 void 473 release_lif(dhcp_lif_t *lif) 474 { 475 if (lif->lif_hold_count == 0) { 476 dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s", 477 lif->lif_name); 478 return; 479 } 480 481 if (lif->lif_hold_count == 1 && !lif->lif_removed) { 482 unplumb_lif(lif); 483 return; 484 } 485 486 if (--lif->lif_hold_count == 0) { 487 dhcp_pif_t *pif; 488 489 dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s", 490 lif->lif_name); 491 492 if (lif->lif_lease != NULL) 493 dhcpmsg(MSG_CRIT, 494 "release_lif: still holding lease at last hold!"); 495 close_ip_lif(lif); 496 pif = lif->lif_pif; 497 if (pif->pif_isv6) 498 cached_v6_max_mtu = 0; 499 else 500 cached_v4_max_mtu = 0; 501 release_pif(pif); 502 free(lif); 503 } else { 504 dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u", 505 lif->lif_name, lif->lif_hold_count); 506 } 507 } 508 509 /* 510 * remove_lif(): remove a logical interface from its PIF and lease (if any) and 511 * the lease's hold on the LIF. Assumes that we did not plumb 512 * the interface. 513 * 514 * input: dhcp_lif_t *: a pointer to the LIF structure 515 * output: none 516 */ 517 518 void 519 remove_lif(dhcp_lif_t *lif) 520 { 521 if (lif->lif_plumbed) { 522 dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s", 523 lif->lif_name); 524 return; 525 } 526 if (lif->lif_removed) { 527 dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s", 528 lif->lif_name); 529 } else { 530 dhcp_lif_t *lifnext; 531 dhcp_lease_t *dlp; 532 533 dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name); 534 lif->lif_removed = B_TRUE; 535 lifnext = lif->lif_next; 536 clear_lif_dhcp(lif); 537 cancel_lif_timers(lif); 538 if (lif->lif_iaid_id != -1 && 539 iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) { 540 lif->lif_iaid_id = -1; 541 release_lif(lif); 542 } 543 544 /* Remove from PIF list */ 545 remque(lif); 546 547 /* If we were part of a lease, then remove ourselves */ 548 if ((dlp = lif->lif_lease) != NULL) { 549 if (--dlp->dl_nlifs == 0) 550 dlp->dl_lifs = NULL; 551 else if (dlp->dl_lifs == lif) 552 dlp->dl_lifs = lifnext; 553 if (lif->lif_declined != NULL) { 554 dlp->dl_smach->dsm_lif_down--; 555 lif->lif_declined = NULL; 556 } 557 lif->lif_lease = NULL; 558 release_lif(lif); 559 } 560 } 561 } 562 563 /* 564 * lookup_lif_by_name(): Looks up a logical interface entry given a name and 565 * a physical interface. 566 * 567 * input: const char *: the logical interface name 568 * const dhcp_pif_t *: the physical interface 569 * output: dhcp_lif_t *: the matching LIF, or NULL if not found 570 */ 571 572 dhcp_lif_t * 573 lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif) 574 { 575 dhcp_lif_t *lif; 576 577 for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 578 if (strcmp(lif->lif_name, lname) == 0) 579 break; 580 } 581 582 return (lif); 583 } 584 585 /* 586 * checkaddr(): checks if the given address is still set on the given LIF 587 * 588 * input: const dhcp_lif_t *: the LIF to check 589 * int: the address to look up on the interface (ioctl) 590 * const in6_addr_t *: the address to compare to 591 * const char *: name of the address for logging purposes 592 * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not 593 */ 594 595 static boolean_t 596 checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr, 597 const char *aname) 598 { 599 boolean_t isv6; 600 int fd; 601 struct lifreq lifr; 602 char abuf1[INET6_ADDRSTRLEN]; 603 char abuf2[INET6_ADDRSTRLEN]; 604 605 (void) memset(&lifr, 0, sizeof (struct lifreq)); 606 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 607 608 isv6 = lif->lif_pif->pif_isv6; 609 fd = isv6 ? v6_sock_fd : v4_sock_fd; 610 611 if (ioctl(fd, ioccmd, &lifr) == -1) { 612 if (errno == ENXIO) { 613 dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone", 614 lif->lif_name); 615 return (B_FALSE); 616 } 617 dhcpmsg(MSG_DEBUG, 618 "checkaddr: ignoring ioctl error on %s %x: %s", 619 lif->lif_name, ioccmd, strerror(errno)); 620 } else if (isv6) { 621 struct sockaddr_in6 *sin6 = 622 (struct sockaddr_in6 *)&lifr.lifr_addr; 623 624 if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) { 625 dhcpmsg(MSG_WARNING, 626 "checkaddr: expected %s %s on %s, have %s", aname, 627 inet_ntop(AF_INET6, addr, abuf1, sizeof (abuf1)), 628 lif->lif_name, inet_ntop(AF_INET6, &sin6->sin6_addr, 629 abuf2, sizeof (abuf2))); 630 return (B_FALSE); 631 } 632 } else { 633 struct sockaddr_in *sinp = 634 (struct sockaddr_in *)&lifr.lifr_addr; 635 ipaddr_t v4addr; 636 637 IN6_V4MAPPED_TO_IPADDR(addr, v4addr); 638 if (sinp->sin_addr.s_addr != v4addr) { 639 dhcpmsg(MSG_WARNING, 640 "checkaddr: expected %s %s on %s, have %s", aname, 641 inet_ntop(AF_INET, &v4addr, abuf1, sizeof (abuf1)), 642 lif->lif_name, inet_ntop(AF_INET, &sinp->sin_addr, 643 abuf2, sizeof (abuf2))); 644 return (B_FALSE); 645 } 646 } 647 return (B_TRUE); 648 } 649 650 /* 651 * verify_lif(): verifies than a LIF is still valid (i.e., has not been 652 * explicitly or implicitly dropped or released) 653 * 654 * input: const dhcp_lif_t *: the LIF to verify 655 * output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise 656 */ 657 658 boolean_t 659 verify_lif(const dhcp_lif_t *lif) 660 { 661 boolean_t isv6; 662 int fd; 663 struct lifreq lifr; 664 665 (void) memset(&lifr, 0, sizeof (struct lifreq)); 666 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 667 668 isv6 = lif->lif_pif->pif_isv6; 669 fd = isv6 ? v6_sock_fd : v4_sock_fd; 670 671 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 672 if (errno != ENXIO) { 673 dhcpmsg(MSG_ERR, 674 "verify_lif: SIOCGLIFFLAGS failed on %s", 675 lif->lif_name); 676 } 677 return (B_FALSE); 678 } 679 680 /* 681 * If important flags have changed, then abandon the interface. 682 */ 683 if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) { 684 dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: " 685 "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags, 686 lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) & 687 DHCP_IFF_WATCH); 688 return (B_FALSE); 689 } 690 691 /* 692 * Special case: if the interface has gone down as a duplicate, then 693 * this alone does _not_ mean that we're abandoning it just yet. Allow 694 * the state machine to handle this normally by trying to get a new 695 * lease. 696 */ 697 if ((lifr.lifr_flags & (IFF_UP|IFF_DUPLICATE)) == IFF_DUPLICATE) { 698 dhcpmsg(MSG_DEBUG, "verify_lif: duplicate address on %s", 699 lif->lif_name); 700 return (B_TRUE); 701 } 702 703 /* 704 * If the user has torn down or started up the interface manually, then 705 * abandon the lease. 706 */ 707 if ((lif->lif_flags ^ lifr.lifr_flags) & IFF_UP) { 708 dhcpmsg(MSG_DEBUG, "verify_lif: user has %s %s", 709 lifr.lifr_flags & IFF_UP ? "started up" : "shut down", 710 lif->lif_name); 711 return (B_FALSE); 712 } 713 714 /* 715 * Check for delete and recreate. 716 */ 717 if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) { 718 dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed on %s", 719 lif->lif_name); 720 return (B_FALSE); 721 } 722 if (lifr.lifr_index != lif->lif_pif->pif_index) { 723 dhcpmsg(MSG_DEBUG, 724 "verify_lif: ifindex on %s changed: %u to %u", 725 lif->lif_name, lif->lif_pif->pif_index, lifr.lifr_index); 726 return (B_FALSE); 727 } 728 729 /* 730 * If the IP address, netmask, or broadcast address have changed, or 731 * the interface has been unplumbed, then we act like there has been an 732 * implicit drop. (Note that the netmask is under DHCP control for 733 * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast 734 * addresses.) 735 */ 736 737 if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address")) 738 return (B_FALSE); 739 740 if (isv6) { 741 /* 742 * If it's not point-to-point, we're done. If it is, then 743 * check the peer's address as well. 744 */ 745 return (!(lif->lif_flags & IFF_POINTOPOINT) || 746 checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer, 747 "peer address")); 748 } else { 749 if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask, 750 "netmask")) 751 return (B_FALSE); 752 753 return (checkaddr(lif, 754 (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR : 755 SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address")); 756 } 757 } 758 759 /* 760 * canonize_lif(): puts the interface in a canonical (zeroed) form. This is 761 * used only on the "main" LIF for IPv4. All other interfaces 762 * are under dhcpagent control and are removed using 763 * unplumb_lif(). 764 * 765 * input: dhcp_lif_t *: the interface to canonize 766 * boolean_t: only canonize lif if it's under DHCP control 767 * output: none 768 */ 769 770 static void 771 canonize_lif(dhcp_lif_t *lif, boolean_t dhcponly) 772 { 773 boolean_t isv6; 774 int fd; 775 struct lifreq lifr; 776 777 /* 778 * If there's nothing here, then don't touch the interface. This can 779 * happen when an already-canonized LIF is recanonized. 780 */ 781 if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr)) 782 return; 783 784 isv6 = lif->lif_pif->pif_isv6; 785 dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s", 786 isv6 ? 6 : 4, lif->lif_name); 787 788 lif->lif_v6addr = my_in6addr_any; 789 lif->lif_v6mask = my_in6addr_any; 790 lif->lif_v6peer = my_in6addr_any; 791 792 (void) memset(&lifr, 0, sizeof (struct lifreq)); 793 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 794 795 fd = isv6 ? v6_sock_fd : v4_sock_fd; 796 797 if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { 798 if (errno != ENXIO) { 799 dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s", 800 lif->lif_name); 801 } 802 return; 803 } 804 lif->lif_flags = lifr.lifr_flags; 805 806 if (dhcponly && !(lifr.lifr_flags & IFF_DHCPRUNNING)) { 807 dhcpmsg(MSG_INFO, 808 "canonize_lif: cannot clear %s; flags are %llx", 809 lif->lif_name, lifr.lifr_flags); 810 return; 811 } 812 813 (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); 814 if (isv6) { 815 struct sockaddr_in6 *sin6 = 816 (struct sockaddr_in6 *)&lifr.lifr_addr; 817 818 sin6->sin6_family = AF_INET6; 819 sin6->sin6_addr = my_in6addr_any; 820 } else { 821 struct sockaddr_in *sinv = 822 (struct sockaddr_in *)&lifr.lifr_addr; 823 824 sinv->sin_family = AF_INET; 825 sinv->sin_addr.s_addr = htonl(INADDR_ANY); 826 } 827 828 if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) { 829 dhcpmsg(MSG_ERR, 830 "canonize_lif: can't clear local address on %s", 831 lif->lif_name); 832 } 833 834 /* Netmask is under in.ndpd control with IPv6 */ 835 if (!isv6) { 836 /* Clear the netmask */ 837 if (ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) { 838 dhcpmsg(MSG_ERR, 839 "canonize_lif: can't clear netmask on %s", 840 lif->lif_name); 841 } 842 /* 843 * When the netmask is cleared, the kernel actually sets the 844 * netmask to 255.0.0.0. So, refetch that netmask. 845 */ 846 if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) { 847 dhcpmsg(MSG_ERR, 848 "canonize_lif: can't reload cleared netmask on %s", 849 lif->lif_name); 850 } 851 lif->lif_netmask = 852 ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr; 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