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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/socket.h> 30 #include <net/if.h> 31 #include <sys/dlpi.h> 32 #include <stdlib.h> 33 #include <sys/sockio.h> 34 #include <netinet/in.h> 35 #include <netinet/dhcp.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <netinet/if_ether.h> 39 #include <signal.h> 40 #include <dhcpmsg.h> 41 #include <libdevinfo.h> 42 43 #include "interface.h" 44 #include "util.h" 45 #include "dlpi_io.h" 46 #include "packet.h" 47 #include "defaults.h" 48 #include "states.h" 49 #include "script_handler.h" 50 51 /* 52 * note to the reader: 53 * 54 * the terminology in here is slightly confusing. in particular, the 55 * term `ifslist' is used to refer both to the `struct ifslist' entry 56 * that makes up a specific interface entry, and the `internal 57 * ifslist' which is a linked list of struct ifslists. to reduce 58 * confusion, in the comments, a `struct ifslist' is referred to as 59 * an `ifs', and `ifslist' refers to the internal ifslist. 60 * 61 */ 62 63 static struct ifslist *ifsheadp; 64 static unsigned int ifscount; 65 66 static void init_ifs(struct ifslist *); 67 static void free_ifs(struct ifslist *); 68 static void cancel_ifs_timer(struct ifslist *, int); 69 70 static boolean_t get_prom_prop(const char *, const char *, uchar_t **, 71 unsigned int *); 72 73 /* 74 * insert_ifs(): creates a new ifs and chains it on the ifslist. initializes 75 * state which remains consistent across all use of the ifs entry 76 * 77 * input: const char *: the name of the ifs entry (interface name) 78 * boolean_t: if B_TRUE, we're adopting the interface 79 * int *: ignored on input; if insert_ifs fails, set to a DHCP_IPC_E_* 80 * error code with the reason why 81 * output: struct ifslist *: a pointer to the new ifs entry, or NULL on failure 82 */ 83 84 struct ifslist * 85 insert_ifs(const char *if_name, boolean_t is_adopting, int *error) 86 { 87 uint32_t buf[DLPI_BUF_MAX / sizeof (uint32_t)]; 88 dl_info_ack_t *dlia = (dl_info_ack_t *)buf; 89 caddr_t dl_addr; 90 struct ifreq ifr; 91 unsigned int i, client_id_len = 0; 92 uchar_t *client_id = NULL; 93 const char *prl; 94 struct ifslist *ifsp; 95 long seed; 96 97 ifsp = lookup_ifs(if_name); 98 if (ifsp != NULL) { 99 *error = DHCP_IPC_E_INT; /* should never happen */ 100 return (NULL); 101 } 102 103 /* 104 * okay, we've got a request to put a new interface under our 105 * control. it's our job to set everything that doesn't 106 * change for the life of the interface. (state that changes 107 * should be initialized in init_ifs() and reset by reset_ifs()) 108 * 109 * 1. verify the interface can support DHCP 110 * 2. get the interface mtu 111 * 3. get the interface hardware type and hardware length 112 * 4. get the interface hardware address 113 * 5. get the interface broadcast address 114 * 6. get the interface flags 115 */ 116 117 ifsp = calloc(1, sizeof (struct ifslist)); 118 if (ifsp == NULL) { 119 dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate ifs entry for " 120 "%s", if_name); 121 *error = DHCP_IPC_E_MEMORY; 122 return (NULL); 123 } 124 125 (void) strlcpy(ifsp->if_name, if_name, IFNAMSIZ); 126 127 /* step 1 */ 128 ifsp->if_dlpi_fd = dlpi_open(if_name, dlia, sizeof (buf), ETHERTYPE_IP); 129 if (ifsp->if_dlpi_fd == -1) { 130 *error = DHCP_IPC_E_INVIF; 131 goto failure; 132 } 133 134 init_ifs(ifsp); /* ifsp->if_dlpi_fd must be valid */ 135 ipc_action_init(ifsp); 136 137 /* step 2 */ 138 ifsp->if_max = dlia->dl_max_sdu; 139 ifsp->if_opt = ifsp->if_max - BASE_PKT_SIZE; 140 ifsp->if_min = dlia->dl_min_sdu; 141 142 if (ifsp->if_max < DHCP_DEF_MAX_SIZE) { 143 dhcpmsg(MSG_ERROR, "insert_ifs: %s does not have a large " 144 "enough maximum SDU to support DHCP", if_name); 145 *error = DHCP_IPC_E_INVIF; 146 goto failure; 147 } 148 149 /* step 3 */ 150 ifsp->if_hwtype = dlpi_to_arp(dlia->dl_mac_type); 151 ifsp->if_hwlen = dlia->dl_addr_length - abs(dlia->dl_sap_length); 152 153 dhcpmsg(MSG_DEBUG, "insert_ifs: %s: sdumax %d, optmax %d, hwtype %d, " 154 "hwlen %d", if_name, ifsp->if_max, ifsp->if_opt, ifsp->if_hwtype, 155 ifsp->if_hwlen); 156 157 /* step 4 */ 158 ifsp->if_hwaddr = malloc(ifsp->if_hwlen); 159 if (ifsp->if_hwaddr == NULL) { 160 dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate if_hwaddr " 161 "for %s", if_name); 162 *error = DHCP_IPC_E_MEMORY; 163 goto failure; 164 } 165 166 /* 167 * depending on the DLPI device, the sap and hardware addresses 168 * can be in either order within the dlsap address; find the 169 * location of the hardware address using dl_sap_length. see the 170 * DLPI specification for more on this braindamage. 171 */ 172 173 dl_addr = (caddr_t)dlia + dlia->dl_addr_offset; 174 if (dlia->dl_sap_length > 0) { 175 ifsp->if_sap_before++; 176 dl_addr += dlia->dl_sap_length; 177 } 178 179 (void) memcpy(ifsp->if_hwaddr, dl_addr, ifsp->if_hwlen); 180 181 /* step 5 */ 182 ifsp->if_saplen = abs(dlia->dl_sap_length); 183 ifsp->if_daddr = build_broadcast_dest(dlia, &ifsp->if_dlen); 184 if (ifsp->if_daddr == NULL) { 185 dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate if_daddr " 186 "for %s", if_name); 187 *error = DHCP_IPC_E_MEMORY; 188 goto failure; 189 } 190 191 /* step 6 */ 192 (void) strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); 193 194 if (ioctl(ifsp->if_sock_fd, SIOCGIFINDEX, &ifr) == -1) { 195 if (errno == ENXIO) 196 *error = DHCP_IPC_E_INVIF; 197 else 198 *error = DHCP_IPC_E_INT; 199 dhcpmsg(MSG_ERR, "insert_ifs: SIOCGIFINDEX for %s", if_name); 200 goto failure; 201 } 202 ifsp->if_index = ifr.ifr_index; 203 204 if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1) { 205 if (errno == ENXIO) 206 *error = DHCP_IPC_E_INVIF; 207 else 208 *error = DHCP_IPC_E_INT; 209 dhcpmsg(MSG_ERR, "insert_ifs: SIOCGIFFLAGS for %s", if_name); 210 goto failure; 211 } 212 213 /* 214 * if DHCPRUNNING is already set on the interface and we're 215 * not adopting it, the agent probably crashed and burned. 216 * note it, but don't let it stop the proceedings. we're 217 * pretty sure we're not already running, since we wouldn't 218 * have been able to bind to our IPC port. 219 */ 220 221 if ((is_adopting == B_FALSE) && (ifr.ifr_flags & IFF_DHCPRUNNING)) 222 dhcpmsg(MSG_WARNING, "insert_ifs: DHCP flag already set on %s", 223 if_name); 224 225 ifr.ifr_flags |= IFF_DHCPRUNNING; 226 (void) ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr); 227 228 ifsp->if_send_pkt.pkt = calloc(ifsp->if_max, 1); 229 if (ifsp->if_send_pkt.pkt == NULL) { 230 dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate if_send_pkt " 231 "for %s", if_name); 232 *error = DHCP_IPC_E_MEMORY; 233 goto failure; 234 } 235 236 if (is_adopting) { 237 /* 238 * if the agent is adopting a lease OBP is initially 239 * searched for a client-id 240 */ 241 242 dhcpmsg(MSG_DEBUG, "insert_ifs: getting /chosen:clientid " 243 "property"); 244 245 if (!get_prom_prop("chosen", "client-id", &ifsp->if_cid, 246 &client_id_len)) { 247 /* 248 * a failure occurred trying to acquire the client-id 249 */ 250 251 dhcpmsg(MSG_DEBUG, "insert_ifs: cannot allocate client " 252 "id for %s", if_name); 253 *error = DHCP_IPC_E_INT; 254 goto failure; 255 } else if (dlia->dl_mac_type == DL_IB && ifsp->if_cid == NULL) { 256 /* 257 * when the interface is infiniband and the agent 258 * is adopting the lease there must be an OBP 259 * client-id. 260 */ 261 262 dhcpmsg(MSG_DEBUG, "insert_ifs: no /chosen:clientid" 263 "id for %s", if_name); 264 *error = DHCP_IPC_E_INT; 265 goto failure; 266 } 267 268 ifsp->if_cidlen = client_id_len; 269 } else { 270 /* 271 * look in defaults file for the client-id 272 */ 273 274 dhcpmsg(MSG_DEBUG, "insert_ifs: getting defaults client-id " 275 "property"); 276 277 client_id = df_get_octet(if_name, DF_CLIENT_ID, &client_id_len); 278 279 /* 280 * at this point, all logical interfaces must be explicitly 281 * configured with a client id by the administrator. 282 */ 283 284 if (client_id == NULL && strchr(if_name, ':') != NULL) { 285 dhcpmsg(MSG_ERROR, "no client id configured for " 286 "logical interface %s; cannot manage", if_name); 287 *error = DHCP_IPC_E_NOIFCID; 288 goto failure; 289 } 290 291 if (client_id != NULL) { 292 /* 293 * the defaults client-id value must be copied out to 294 * another buffer 295 */ 296 297 ifsp->if_cid = calloc(client_id_len, sizeof (uchar_t)); 298 299 if (ifsp->if_cid == NULL) { 300 dhcpmsg(MSG_ERR, "insert_ifs: cannot " 301 "allocate client id for %s", if_name); 302 *error = DHCP_IPC_E_MEMORY; 303 goto failure; 304 } 305 306 (void) memcpy(ifsp->if_cid, client_id, client_id_len); 307 308 ifsp->if_cidlen = client_id_len; 309 } else if (dlia->dl_mac_type == DL_IB) { 310 /* 311 * This comes from DHCP over IPoIB spec. In the absence 312 * of an user specified client id, IPoIB automatically 313 * uses the required format, with the unique 4 octet 314 * value set to 0 (since IPoIB driver allows only a 315 * single interface on a port with a specific GID to 316 * belong to an IP subnet (PSARC 2001/289, 317 * FWARC 2002/702). 318 * 319 * Type Client-Identifier 320 * +-----+-----+-----+-----+-----+----....----+ 321 * | 0 | 0 (4 octets) | GID (16 octets)| 322 * +-----+-----+-----+-----+-----+----....----+ 323 */ 324 ifsp->if_cidlen = 1 + 4 + 16; 325 ifsp->if_cid = client_id = malloc(ifsp->if_cidlen); 326 if (ifsp->if_cid == NULL) { 327 dhcpmsg(MSG_ERR, "insert_ifs: cannot " 328 "allocate client id for %s", if_name); 329 *error = DHCP_IPC_E_MEMORY; 330 goto failure; 331 } 332 333 /* 334 * Pick the GID from the mac address. The format 335 * of the hardware address is: 336 * +-----+-----+-----+-----+----....----+ 337 * | QPN (4 octets) | GID (16 octets)| 338 * +-----+-----+-----+-----+----....----+ 339 */ 340 (void) memcpy(client_id + 5, ifsp->if_hwaddr + 4, 341 ifsp->if_hwlen - 4); 342 (void) memset(client_id, 0, 5); 343 } 344 } 345 346 /* 347 * initialize the parameter request list, if there is one. 348 */ 349 350 prl = df_get_string(if_name, DF_PARAM_REQUEST_LIST); 351 if (prl == NULL) 352 ifsp->if_prl = NULL; 353 else { 354 for (ifsp->if_prllen = 1, i = 0; prl[i] != '\0'; i++) 355 if (prl[i] == ',') 356 ifsp->if_prllen++; 357 358 ifsp->if_prl = malloc(ifsp->if_prllen); 359 if (ifsp->if_prl == NULL) { 360 dhcpmsg(MSG_WARNING, "insert_ifs: cannot allocate " 361 "parameter request list for %s (continuing)", 362 if_name); 363 } else { 364 for (i = 0; i < ifsp->if_prllen; prl++, i++) { 365 ifsp->if_prl[i] = strtoul(prl, NULL, 0); 366 while (*prl != ',' && *prl != '\0') 367 prl++; 368 if (*prl == '\0') 369 break; 370 } 371 } 372 } 373 374 ifsp->if_offer_wait = df_get_int(if_name, DF_OFFER_WAIT); 375 376 /* 377 * we're past the point of failure; chain it on. 378 */ 379 380 ifsp->next = ifsheadp; 381 ifsp->prev = NULL; 382 ifsheadp = ifsp; 383 384 if (ifsheadp->next != NULL) 385 ifsheadp->next->prev = ifsheadp; 386 387 hold_ifs(ifsp); 388 ifscount++; 389 390 if (inactivity_id != -1) { 391 if (iu_cancel_timer(tq, inactivity_id, NULL) == 1) 392 inactivity_id = -1; 393 } 394 395 /* 396 * seed the random number generator, since we're going to need it 397 * to set transaction id's and for exponential backoff. if an 398 * interface is already initialized, then we just end up harmlessly 399 * reseeding it. note that we try to spread the hardware address 400 * over as many bits of the seed as possible. 401 */ 402 seed = gethrtime(); 403 for (i = 0; i < ifsp->if_hwlen; i++) 404 seed += ifsp->if_hwaddr[i] << ((i % 7) * 4); 405 seed ^= getpid(); 406 srand48(seed); 407 408 dhcpmsg(MSG_DEBUG, "insert_ifs: inserted interface %s", if_name); 409 return (ifsp); 410 411 failure: 412 free_ifs(ifsp); 413 return (NULL); 414 } 415 416 /* 417 * init_ifs(): puts an ifs in its initial state 418 * 419 * input: struct ifslist *: the ifs to initialize 420 * output: void 421 * note: if the interface isn't fresh, use reset_ifs() 422 */ 423 424 static void 425 init_ifs(struct ifslist *ifsp) 426 { 427 /* 428 * if_sock_ip_fd is created and bound in configure_if(). 429 * if_sock_fd is bound in configure_if(); see comments in 430 * bound.c for more details on why. if creation of if_sock_fd 431 * fails, we'll need more context anyway, so don't check. 432 */ 433 434 ifsp->if_sock_fd = socket(AF_INET, SOCK_DGRAM, 0); 435 ifsp->if_sock_ip_fd = -1; 436 ifsp->if_state = INIT; 437 ifsp->if_routers = NULL; 438 ifsp->if_nrouters = 0; 439 ifsp->if_ack = NULL; 440 ifsp->if_orig_ack = NULL; 441 ifsp->if_server.s_addr = htonl(INADDR_BROADCAST); 442 ifsp->if_neg_monosec = monosec(); 443 ifsp->if_lease = 0; 444 ifsp->if_t1 = 0; 445 ifsp->if_t2 = 0; 446 ifsp->if_reqhost = NULL; 447 448 ifsp->if_script_helper_pid = -1; 449 ifsp->if_script_callback = NULL; 450 ifsp->if_script_event = NULL; 451 ifsp->if_callback_msg = NULL; 452 ifsp->if_script_event_id = -1; 453 ifsp->if_script_pid = -1; 454 ifsp->if_script_fd = -1; 455 456 ifsp->if_offer_id = -1; 457 ifsp->if_acknak_id = -1; 458 ifsp->if_acknak_bcast_id = -1; 459 ifsp->if_timer[DHCP_T1_TIMER] = -1; 460 ifsp->if_timer[DHCP_T2_TIMER] = -1; 461 ifsp->if_timer[DHCP_LEASE_TIMER] = -1; 462 ifsp->if_offer_timer = -1; 463 464 set_packet_filter(ifsp->if_dlpi_fd, dhcp_filter, NULL, "DHCP"); 465 466 dhcpmsg(MSG_DEBUG, "init_ifs: initted interface %s", ifsp->if_name); 467 } 468 469 /* 470 * remove_ifs_default_routes(): removes an ifs's default routes 471 * 472 * input: struct ifslist *: the ifs whose default routes need to be removed 473 * output: void 474 */ 475 476 static void 477 remove_ifs_default_routes(struct ifslist *ifsp) 478 { 479 if (ifsp->if_routers != NULL) { 480 while (ifsp->if_nrouters > 0) { 481 (void) del_default_route(ifsp->if_name, 482 &ifsp->if_routers[--ifsp->if_nrouters]); 483 } 484 free(ifsp->if_routers); 485 ifsp->if_routers = NULL; 486 } 487 } 488 489 /* 490 * reset_ifs(): resets an ifs to its initial state 491 * 492 * input: struct ifslist *: the ifs to reset 493 * output: void 494 */ 495 496 void 497 reset_ifs(struct ifslist *ifsp) 498 { 499 ifsp->if_dflags &= ~DHCP_IF_FAILED; 500 501 remove_ifs_default_routes(ifsp); 502 503 if (ifsp->if_sock_fd != -1) 504 (void) close(ifsp->if_sock_fd); 505 506 if (ifsp->if_orig_ack != ifsp->if_ack) 507 free_pkt_list(&ifsp->if_orig_ack); 508 509 free_pkt_list(&ifsp->if_ack); 510 511 if (ifsp->if_sock_ip_fd != -1) 512 (void) close(ifsp->if_sock_ip_fd); 513 514 if (ifsp->if_offer_id != -1) { 515 if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0) 516 (void) release_ifs(ifsp); 517 } 518 519 (void) unregister_acknak(ifsp); /* just in case */ 520 521 cancel_ifs_timers(ifsp); 522 523 if (ifsp->if_offer_timer != -1) { 524 if (iu_cancel_timer(tq, ifsp->if_offer_timer, NULL)) 525 (void) release_ifs(ifsp); 526 } 527 528 stop_pkt_retransmission(ifsp); 529 530 init_ifs(ifsp); 531 } 532 533 /* 534 * lookup_ifs(): looks up an ifs, given its name 535 * 536 * input: const char *: the name of the ifs entry (the interface name) 537 * the name "" searches for the primary interface 538 * output: struct ifslist *: the corresponding ifs, or NULL if not found 539 */ 540 541 struct ifslist * 542 lookup_ifs(const char *if_name) 543 { 544 struct ifslist *ifs; 545 546 for (ifs = ifsheadp; ifs != NULL; ifs = ifs->next) 547 if (*if_name != '\0') { 548 if (strcmp(ifs->if_name, if_name) == 0) 549 break; 550 } else if (ifs->if_dflags & DHCP_IF_PRIMARY) 551 break; 552 553 return (ifs); 554 } 555 556 /* 557 * lookup_ifs_by_xid(): looks up an ifs, given its last used transaction id 558 * 559 * input: int: the transaction id to look up 560 * output: struct ifslist *: the corresponding ifs, or NULL if not found 561 */ 562 563 struct ifslist * 564 lookup_ifs_by_xid(uint32_t xid) 565 { 566 struct ifslist *ifs; 567 568 for (ifs = ifsheadp; ifs != NULL; ifs = ifs->next) { 569 if (ifs->if_send_pkt.pkt->xid == xid) 570 break; 571 } 572 573 return (ifs); 574 } 575 576 /* 577 * lookup_ifs_by_uindex(): Looks up ifs entries given truncated index and 578 * previous ifs pointer (or NULL for list start). 579 * Caller is expected to iterate through all 580 * potential matches to find interface of interest. 581 * 582 * input: int: the interface index 583 * struct ifslist *: the previous ifs, or NULL for list start 584 * output: struct ifslist *: the next matching ifs, or NULL if not found 585 * note: This operates using the 'truncated' (16-bit) ifindex as seen by 586 * routing socket clients. The value stored in if_index is the 587 * 32-bit ifindex from the ioctl interface. 588 */ 589 590 struct ifslist * 591 lookup_ifs_by_uindex(uint16_t ifindex, struct ifslist *ifs) 592 { 593 if (ifs == NULL) 594 ifs = ifsheadp; 595 else 596 ifs = ifs->next; 597 598 for (; ifs != NULL; ifs = ifs->next) { 599 if ((ifs->if_index & 0xffff) == ifindex) 600 break; 601 } 602 603 return (ifs); 604 } 605 606 /* 607 * remove_ifs(): removes a given ifs from the ifslist. marks the ifs 608 * for being freed (but may not actually free it). 609 * 610 * input: struct ifslist *: the ifs to remove 611 * output: void 612 * note: see interface.h for a discussion of ifs memory management 613 */ 614 615 void 616 remove_ifs(struct ifslist *ifsp) 617 { 618 struct ifreq ifr; 619 620 if (ifsp->if_dflags & DHCP_IF_REMOVED) 621 return; 622 623 (void) memset(&ifr, 0, sizeof (struct ifreq)); 624 (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ); 625 626 if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == 0) { 627 ifr.ifr_flags &= ~IFF_DHCPRUNNING; 628 (void) ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr); 629 } 630 631 ifsp->if_dflags |= DHCP_IF_REMOVED; 632 633 /* 634 * if we have long term timers, cancel them so that interface 635 * resources can be reclaimed in a reasonable amount of time. 636 */ 637 638 cancel_ifs_timers(ifsp); 639 640 if (ifsp->prev != NULL) 641 ifsp->prev->next = ifsp->next; 642 else 643 ifsheadp = ifsp->next; 644 645 if (ifsp->next != NULL) 646 ifsp->next->prev = ifsp->prev; 647 648 ifscount--; 649 (void) release_ifs(ifsp); 650 651 /* no big deal if this fails */ 652 if (ifscount == 0) { 653 inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT, 654 inactivity_shutdown, NULL); 655 } 656 } 657 658 /* 659 * hold_ifs(): acquires a hold on an ifs 660 * 661 * input: struct ifslist *: the ifs entry to acquire a hold on 662 * output: void 663 */ 664 665 void 666 hold_ifs(struct ifslist *ifsp) 667 { 668 ifsp->if_hold_count++; 669 670 dhcpmsg(MSG_DEBUG2, "hold_ifs: hold count on %s: %d", 671 ifsp->if_name, ifsp->if_hold_count); 672 } 673 674 /* 675 * release_ifs(): releases a hold previously acquired on an ifs. if the 676 * hold count reaches 0, the ifs is freed 677 * 678 * input: struct ifslist *: the ifs entry to release the hold on 679 * output: int: the number of holds outstanding on the ifs 680 */ 681 682 int 683 release_ifs(struct ifslist *ifsp) 684 { 685 if (ifsp->if_hold_count == 0) { 686 dhcpmsg(MSG_CRIT, "release_ifs: extraneous release"); 687 return (0); 688 } 689 690 if (--ifsp->if_hold_count == 0) { 691 free_ifs(ifsp); 692 return (0); 693 } 694 695 dhcpmsg(MSG_DEBUG2, "release_ifs: hold count on %s: %d", 696 ifsp->if_name, ifsp->if_hold_count); 697 698 return (ifsp->if_hold_count); 699 } 700 701 /* 702 * free_ifs(): frees the memory occupied by an ifs entry 703 * 704 * input: struct ifslist *: the ifs entry to free 705 * output: void 706 */ 707 708 static void 709 free_ifs(struct ifslist *ifsp) 710 { 711 dhcpmsg(MSG_DEBUG, "free_ifs: freeing interface %s", ifsp->if_name); 712 713 free_pkt_list(&ifsp->if_recv_pkt_list); 714 if (ifsp->if_ack != ifsp->if_orig_ack) 715 free_pkt_list(&ifsp->if_orig_ack); 716 free_pkt_list(&ifsp->if_ack); 717 free(ifsp->if_send_pkt.pkt); 718 free(ifsp->if_cid); 719 free(ifsp->if_daddr); 720 free(ifsp->if_hwaddr); 721 free(ifsp->if_prl); 722 free(ifsp->if_reqhost); 723 free(ifsp->if_routers); 724 725 if (ifsp->if_sock_fd != -1) 726 (void) close(ifsp->if_sock_fd); 727 728 if (ifsp->if_sock_ip_fd != -1) 729 (void) close(ifsp->if_sock_ip_fd); 730 731 if (ifsp->if_dlpi_fd != -1) 732 (void) dlpi_close(ifsp->if_dlpi_fd); 733 734 free(ifsp); 735 } 736 737 /* 738 * checkaddr(): checks if the given address is still set on the given ifs 739 * 740 * input: struct ifslist *: the ifs to check 741 * int: the address to lookup on the interface 742 * struct in_addr *: the address to compare to 743 * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not 744 */ 745 746 static boolean_t 747 checkaddr(struct ifslist *ifsp, int ioccmd, struct in_addr *addr) 748 { 749 struct ifreq ifr; 750 struct sockaddr_in *sin; 751 752 /* LINTED [ifr_addr is a sockaddr which will be aligned] */ 753 sin = (struct sockaddr_in *)&ifr.ifr_addr; 754 755 (void) memset(&ifr, 0, sizeof (struct ifreq)); 756 (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ); 757 ifr.ifr_addr.sa_family = AF_INET; 758 759 switch (ioctl(ifsp->if_sock_fd, ioccmd, &ifr)) { 760 case 0: 761 if (sin->sin_addr.s_addr != addr->s_addr) 762 return (B_FALSE); 763 break; 764 case -1: 765 if (errno == ENXIO) 766 return (B_FALSE); 767 break; 768 } 769 return (B_TRUE); 770 } 771 772 /* 773 * verify_ifs(): verifies than an ifs is still valid (i.e., has not been 774 * explicitly or implicitly dropped or released) 775 * 776 * input: struct ifslist *: the ifs to verify 777 * output: int: 1 if the ifs is still valid, 0 if the interface is invalid 778 */ 779 780 int 781 verify_ifs(struct ifslist *ifsp) 782 { 783 struct ifreq ifr; 784 785 if (ifsp->if_dflags & DHCP_IF_REMOVED) 786 return (0); 787 788 (void) memset(&ifr, 0, sizeof (struct ifreq)); 789 (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ); 790 791 ifr.ifr_addr.sa_family = AF_INET; 792 793 switch (ifsp->if_state) { 794 795 case BOUND: 796 case RENEWING: 797 case REBINDING: 798 799 /* 800 * if the interface has gone down or been unplumbed, then we 801 * act like there has been an implicit drop. 802 */ 803 804 switch (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr)) { 805 case 0: 806 if ((ifr.ifr_flags & (IFF_UP|IFF_DHCPRUNNING)) != 807 (IFF_UP|IFF_DHCPRUNNING)) 808 goto abandon; 809 break; 810 case -1: 811 if (errno == ENXIO) 812 goto abandon; 813 break; 814 } 815 switch (ioctl(ifsp->if_sock_fd, SIOCGIFINDEX, &ifr)) { 816 case 0: 817 if (ifr.ifr_index != ifsp->if_index) 818 goto abandon; 819 break; 820 case -1: 821 if (errno == ENXIO) 822 goto abandon; 823 break; 824 } 825 /* FALLTHRU */ 826 827 case INIT_REBOOT: 828 case SELECTING: 829 case REQUESTING: 830 831 /* 832 * if the IP address, netmask, or broadcast address have 833 * changed, or the interface has been unplumbed, then we act 834 * like there has been an implicit drop. 835 */ 836 837 if (!checkaddr(ifsp, SIOCGIFADDR, &ifsp->if_addr) || 838 !checkaddr(ifsp, SIOCGIFNETMASK, &ifsp->if_netmask) || 839 !checkaddr(ifsp, SIOCGIFBRDADDR, &ifsp->if_broadcast)) 840 goto abandon; 841 } 842 843 return (1); 844 abandon: 845 dhcpmsg(MSG_WARNING, "verify_ifs: %s has changed properties, " 846 "abandoning", ifsp->if_name); 847 848 remove_ifs(ifsp); 849 return (0); 850 } 851 852 /* 853 * canonize_ifs(): puts the interface in a canonical (zeroed) form 854 * 855 * input: struct ifslist *: the interface to canonize 856 * output: int: 1 on success, 0 on failure 857 */ 858 859 int 860 canonize_ifs(struct ifslist *ifsp) 861 { 862 struct sockaddr_in *sin; 863 struct ifreq ifr; 864 865 dhcpmsg(MSG_VERBOSE, "canonizing interface %s", ifsp->if_name); 866 867 /* 868 * note that due to infelicities in the routing code, any default 869 * routes must be removed prior to clearing the UP flag. 870 */ 871 872 remove_ifs_default_routes(ifsp); 873 874 /* LINTED [ifr_addr is a sockaddr which will be aligned] */ 875 sin = (struct sockaddr_in *)&ifr.ifr_addr; 876 877 (void) memset(&ifr, 0, sizeof (struct ifreq)); 878 (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ); 879 880 if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1) 881 return (0); 882 883 /* 884 * clear the UP flag, but don't clear DHCPRUNNING since 885 * that should only be done when the interface is removed 886 * (see remove_ifs()) 887 */ 888 889 ifr.ifr_flags &= ~IFF_UP; 890 891 if (ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr) == -1) 892 return (0); 893 894 /* 895 * since ifr is actually a union, we need to explicitly zero 896 * the flags field before we reuse the structure, or otherwise 897 * cruft may leak over into other members of the union. 898 */ 899 900 ifr.ifr_flags = 0; 901 ifr.ifr_addr.sa_family = AF_INET; 902 sin->sin_addr.s_addr = htonl(INADDR_ANY); 903 904 if (ioctl(ifsp->if_sock_fd, SIOCSIFADDR, &ifr) == -1) 905 return (0); 906 907 if (ioctl(ifsp->if_sock_fd, SIOCSIFNETMASK, &ifr) == -1) 908 return (0); 909 910 if (ioctl(ifsp->if_sock_fd, SIOCSIFBRDADDR, &ifr) == -1) 911 return (0); 912 913 /* 914 * any time we change the IP address, netmask, or broadcast we 915 * must be careful to also reset bookkeeping of what these are 916 * set to. this is so we can detect if these characteristics 917 * are changed by another process. 918 */ 919 920 ifsp->if_addr.s_addr = htonl(INADDR_ANY); 921 ifsp->if_netmask.s_addr = htonl(INADDR_ANY); 922 ifsp->if_broadcast.s_addr = htonl(INADDR_ANY); 923 924 return (1); 925 } 926 927 /* 928 * check_ifs(): makes sure an ifs is still valid, and if it is, releases the 929 * ifs. otherwise, it informs the caller the ifs is going away 930 * and expects the caller to perform the release 931 * 932 * input: struct ifslist *: the ifs to check 933 * output: int: 1 if the interface is valid, 0 otherwise 934 */ 935 936 int 937 check_ifs(struct ifslist *ifsp) 938 { 939 hold_ifs(ifsp); 940 if (release_ifs(ifsp) == 1 || verify_ifs(ifsp) == 0) { 941 942 /* 943 * this interface is going away. if there's an 944 * uncancelled IPC event roaming around, cancel it 945 * now. we leave the hold on in case anyone else has 946 * any cleanup work that needs to be done before the 947 * interface goes away. 948 */ 949 950 ipc_action_finish(ifsp, DHCP_IPC_E_UNKIF); 951 async_finish(ifsp); 952 return (0); 953 } 954 955 (void) release_ifs(ifsp); 956 return (1); 957 } 958 959 /* 960 * nuke_ifslist(): delete the ifslist (for use when the dhcpagent is exiting) 961 * 962 * input: boolean_t: B_TRUE if the agent is exiting due to SIGTERM 963 * output: void 964 */ 965 966 void 967 nuke_ifslist(boolean_t onterm) 968 { 969 int status; 970 struct ifslist *ifsp, *ifsp_next; 971 972 for (ifsp = ifsheadp; ifsp != NULL; ifsp = ifsp_next) { 973 ifsp_next = ifsp->next; 974 975 cancel_ifs_timers(ifsp); 976 if (ifsp->if_script_pid != -1) { 977 /* stop a script if it is not for DROP or RELEASE */ 978 if (strcmp(ifsp->if_script_event, EVENT_DROP) == 0 || 979 strcmp(ifsp->if_script_event, EVENT_RELEASE) == 0) { 980 continue; 981 } 982 script_stop(ifsp); 983 } 984 985 /* 986 * if the script is started by script_start, dhcp_drop and 987 * dhcp_release should and will only be called after the 988 * script exits. 989 */ 990 if (onterm && 991 df_get_bool(ifsp->if_name, DF_RELEASE_ON_SIGTERM)) { 992 if (script_start(ifsp, EVENT_RELEASE, dhcp_release, 993 "DHCP agent is exiting", &status) == 1) { 994 continue; 995 } 996 if (status == 1) 997 continue; 998 } 999 (void) script_start(ifsp, EVENT_DROP, dhcp_drop, NULL, NULL); 1000 } 1001 } 1002 1003 /* 1004 * refresh_ifslist(): refreshes all finite leases under DHCP control 1005 * 1006 * input: iu_eh_t *: unused 1007 * int: unused 1008 * void *: unused 1009 * output: void 1010 */ 1011 1012 /* ARGSUSED */ 1013 void 1014 refresh_ifslist(iu_eh_t *eh, int sig, void *arg) 1015 { 1016 struct ifslist *ifsp; 1017 1018 for (ifsp = ifsheadp; ifsp != NULL; ifsp = ifsp->next) { 1019 1020 if (ifsp->if_state != BOUND && ifsp->if_state != RENEWING && 1021 ifsp->if_state != REBINDING) 1022 continue; 1023 1024 if (ifsp->if_lease == DHCP_PERM) 1025 continue; 1026 1027 /* 1028 * this interface has a finite lease and we do not know 1029 * how long the machine's been off for. refresh it. 1030 */ 1031 1032 dhcpmsg(MSG_WARNING, "refreshing lease on %s", ifsp->if_name); 1033 cancel_ifs_timer(ifsp, DHCP_T1_TIMER); 1034 cancel_ifs_timer(ifsp, DHCP_T2_TIMER); 1035 (void) iu_adjust_timer(tq, ifsp->if_timer[DHCP_LEASE_TIMER], 0); 1036 } 1037 } 1038 1039 /* 1040 * ifs_count(): returns the number of interfaces currently managed 1041 * 1042 * input: void 1043 * output: unsigned int: the number of interfaces currently managed 1044 */ 1045 1046 unsigned int 1047 ifs_count(void) 1048 { 1049 return (ifscount); 1050 } 1051 1052 /* 1053 * cancel_ifs_timer(): cancels a lease-related timer on an interface 1054 * 1055 * input: struct ifslist *: the interface to operate on 1056 * int: the timer id of the timer to cancel 1057 * output: void 1058 */ 1059 1060 static void 1061 cancel_ifs_timer(struct ifslist *ifsp, int timer_id) 1062 { 1063 if (ifsp->if_timer[timer_id] != -1) { 1064 if (iu_cancel_timer(tq, ifsp->if_timer[timer_id], NULL) == 1) { 1065 (void) release_ifs(ifsp); 1066 ifsp->if_timer[timer_id] = -1; 1067 } else 1068 dhcpmsg(MSG_WARNING, "cancel_ifs_timer: cannot cancel " 1069 "if_timer[%d]", timer_id); 1070 } 1071 } 1072 1073 /* 1074 * cancel_ifs_timers(): cancels an interface's pending lease-related timers 1075 * 1076 * input: struct ifslist *: the interface to operate on 1077 * output: void 1078 */ 1079 1080 void 1081 cancel_ifs_timers(struct ifslist *ifsp) 1082 { 1083 cancel_ifs_timer(ifsp, DHCP_T1_TIMER); 1084 cancel_ifs_timer(ifsp, DHCP_T2_TIMER); 1085 cancel_ifs_timer(ifsp, DHCP_LEASE_TIMER); 1086 } 1087 1088 /* 1089 * schedule_ifs_timer(): schedules a lease-related timer on an interface 1090 * 1091 * input: struct ifslist *: the interface to operate on 1092 * int: the timer to schedule 1093 * uint32_t: the number of seconds in the future it should fire 1094 * iu_tq_callback_t *: the callback to call upon firing 1095 * output: int: 1 if the timer was scheduled successfully, 0 on failure 1096 */ 1097 1098 int 1099 schedule_ifs_timer(struct ifslist *ifsp, int timer_id, uint32_t sec, 1100 iu_tq_callback_t *expire) 1101 { 1102 cancel_ifs_timer(ifsp, timer_id); /* just in case */ 1103 1104 ifsp->if_timer[timer_id] = iu_schedule_timer(tq, sec, expire, ifsp); 1105 if (ifsp->if_timer[timer_id] == -1) { 1106 dhcpmsg(MSG_WARNING, "schedule_ifs_timer: cannot schedule " 1107 "if_timer[%d]", timer_id); 1108 return (0); 1109 } 1110 1111 hold_ifs(ifsp); 1112 return (1); 1113 } 1114 1115 /* 1116 * Get the value of the named property on the named node in devinfo root. 1117 * 1118 * input: const char *: The name of the node containing the property. 1119 * const char *: The name of the property. 1120 * uchar_t **: The property value, modified iff B_TRUE is returned. 1121 * If no value is found the value is set to NULL. 1122 * unsigned int *: The length of the property value 1123 * output: boolean_t: Returns B_TRUE if successful (no problems), 1124 * otherwise B_FALSE. 1125 * note: The memory allocated by this function must be freed by 1126 * the caller. This code is derived from 1127 * usr/src/lib/libwanboot/common/bootinfo_aux.c. 1128 */ 1129 1130 static boolean_t 1131 get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep, 1132 unsigned int *lenp) 1133 { 1134 di_node_t root_node = DI_NODE_NIL; 1135 di_node_t node; 1136 di_prom_handle_t phdl = DI_PROM_HANDLE_NIL; 1137 di_prom_prop_t pp; 1138 uchar_t *value = NULL; 1139 unsigned int len = 0; 1140 boolean_t success = B_TRUE; 1141 1142 /* 1143 * locate root node 1144 */ 1145 1146 if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL || 1147 (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) { 1148 dhcpmsg(MSG_DEBUG, "get_prom_prop: property root node " 1149 "not found"); 1150 goto get_prom_prop_cleanup; 1151 } 1152 1153 /* 1154 * locate nodename within '/' 1155 */ 1156 1157 for (node = di_child_node(root_node); 1158 node != DI_NODE_NIL; 1159 node = di_sibling_node(node)) { 1160 if (strcmp(di_node_name(node), nodename) == 0) { 1161 break; 1162 } 1163 } 1164 1165 if (node == DI_NODE_NIL) { 1166 dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found"); 1167 goto get_prom_prop_cleanup; 1168 } 1169 1170 /* 1171 * scan all properties of /nodename for the 'propname' property 1172 */ 1173 1174 for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL); 1175 pp != DI_PROM_PROP_NIL; 1176 pp = di_prom_prop_next(phdl, node, pp)) { 1177 1178 dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s", 1179 di_prom_prop_name(pp)); 1180 1181 if (strcmp(propname, di_prom_prop_name(pp)) == 0) { 1182 break; 1183 } 1184 } 1185 1186 if (pp == DI_PROM_PROP_NIL) { 1187 dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found"); 1188 goto get_prom_prop_cleanup; 1189 } 1190 1191 /* 1192 * get the property; allocate some memory copy it out 1193 */ 1194 1195 len = di_prom_prop_data(pp, (uchar_t **)&value); 1196 1197 if (value == NULL) { 1198 /* 1199 * property data read problems 1200 */ 1201 1202 success = B_FALSE; 1203 dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data"); 1204 goto get_prom_prop_cleanup; 1205 } 1206 1207 if (propvaluep != NULL) { 1208 /* 1209 * allocate somewhere to copy the property value to 1210 */ 1211 1212 *propvaluep = calloc(len, sizeof (uchar_t)); 1213 1214 if (*propvaluep == NULL) { 1215 /* 1216 * allocation problems 1217 */ 1218 1219 success = B_FALSE; 1220 dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate " 1221 "memory for property value"); 1222 goto get_prom_prop_cleanup; 1223 } 1224 1225 /* 1226 * copy data out 1227 */ 1228 1229 (void) memcpy(*propvaluep, value, len); 1230 1231 /* 1232 * copy out the length if a suitable pointer has 1233 * been supplied 1234 */ 1235 1236 if (lenp != NULL) { 1237 *lenp = len; 1238 } 1239 1240 dhcpmsg(MSG_DEBUG, "get_prom_prop: property value " 1241 "length = %d", len); 1242 } 1243 1244 get_prom_prop_cleanup: 1245 1246 if (phdl != DI_PROM_HANDLE_NIL) { 1247 di_prom_fini(phdl); 1248 } 1249 1250 if (root_node != DI_NODE_NIL) { 1251 di_fini(root_node); 1252 } 1253 1254 return (success); 1255 } 1256