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