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