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