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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * This file contains the routines that manipulate interfaces, the 29 * list of interfaces present on the system, and upper layer profiles; 30 * and various support functions. It also contains a set of functions 31 * to read property values stored in the SMF repository. Finally, it 32 * contains the functions required for the "gather info" threads. 33 * 34 * The daemon maintains a list of structures that represent each IPv4 35 * interface found on the system (after doing 'ifconfig -a plumb'). 36 * This list represents the objects manipulated by the daemon; while 37 * the list of llp_t structures represents the configuration details 38 * requested by the user (either the automatic defaults or entries in 39 * /etc/nwam/llp). IPv6 interfaces are not tracked in the interfaces 40 * list; rather, when the decision is made to make an interface active, 41 * IPv6 is brought up in addition to IPv4 (assuming the LLP configuration 42 * includes IPv6; this is the default for automatic configuration). 43 * 44 * When an interface is taken down, we unplumb the IPv6 link-local interface 45 * completely, so that dhcpagent and in.ndpd will remove any addresses they've 46 * added. Events are watched on the IPv4 interface alone, which is always 47 * present for this version of NWAM. 48 * 49 * Interfaces are brought up and torn down by a sequence of ifconfig 50 * commands (currently posix_spawn'd() by nwamd; the longer-term direction 51 * here is to use libinetcfg). 52 * 53 * Upper Layer Profile management is controlled by user-provided scripts, 54 * which should be created in /etc/nwam/ulp. One script, 55 * /etc/nwam/ulp/check-conditions, checks the current network setup and 56 * returns the name of the ULP which should be active under the current 57 * conditions. A ULP is specified by two scripts, found in 58 * /etc/nwam/ulp/<ulp name>: bringup and teardown. All scripts are 59 * optional; if they do not exist or are not executable, nwamd will 60 * simply move on. 61 * 62 * When an interface has been successfully brought up (signalled by the 63 * assignment of an IP address to the interface), the daemon will first 64 * teardown the existing ULP (if there is one) by running the teardown 65 * script for that ULP. It will then run the check-conditions script; 66 * if the name of a ULP is returned, it runs the bringup script for that 67 * ULP. 68 * 69 * A "gather info" thread is initiated for an interface when it becomes 70 * available. For a wired interface, "available" means the IFF_RUNNING 71 * flag is set; wireless interfaces are considered to always be available, 72 * so a wireless interface's gather info thread will run once, when it is 73 * found at startup. This thread will do a scan on a wireless interface, 74 * and initiate DHCP on a wired interface. It will then generate an event 75 * for the state machine that indicates the availability of a new interface. 76 * 77 * The ifs_head and associated list pointers are protected by ifs_lock. Only 78 * the main thread may modify the list (single writer), and it does so with the 79 * lock held. As a consequence, the main thread alone may read the list (and 80 * examine pointers) without holding any locks. All other threads must hold 81 * ifs_lock for the duration of any examination of the data structures, and 82 * must not deal directly in interface pointers. (A thread may also hold 83 * machine_lock to block the main thread entirely in order to manipulate the 84 * data; such use is isolated to the door interface.) 85 * 86 * Functions in this file have comments noting where the main thread alone is 87 * the caller. These functions do not need to acquire the lock. 88 * 89 * If you hold both ifs_lock and llp_lock, you must take ifs_lock first. 90 */ 91 92 #include <errno.h> 93 #include <netdb.h> 94 #include <stdio.h> 95 #include <stdlib.h> 96 #include <string.h> 97 #include <unistd.h> 98 #include <pthread.h> 99 #include <syslog.h> 100 #include <unistd.h> 101 #include <libscf.h> 102 #include <sys/types.h> 103 #include <arpa/inet.h> 104 #include <inetcfg.h> 105 #include <signal.h> 106 #include <stdarg.h> 107 #include <sys/sysmacros.h> 108 #include <sys/wait.h> 109 #include <libdllink.h> 110 111 #include "defines.h" 112 #include "structures.h" 113 #include "functions.h" 114 #include "variables.h" 115 116 static pthread_mutex_t ifs_lock = PTHREAD_MUTEX_INITIALIZER; 117 118 static struct interface *ifs_head; 119 static struct interface *ifs_wired, *ifs_wired_last; 120 static struct interface *ifs_wireless, *ifs_wireless_last; 121 122 static char upper_layer_profile[MAXHOSTNAMELEN]; 123 124 #define LOOPBACK_IF "lo0" 125 126 void 127 show_if_status(const char *ifname) 128 { 129 icfg_if_t intf; 130 icfg_handle_t h; 131 struct sockaddr_in sin; 132 socklen_t addrlen = sizeof (struct sockaddr_in); 133 int prefixlen = 0; 134 135 (void) strlcpy(intf.if_name, ifname, sizeof (intf.if_name)); 136 /* We only display new addr info for v4 interfaces */ 137 intf.if_protocol = AF_INET; 138 if (icfg_open(&h, &intf) != ICFG_SUCCESS) { 139 syslog(LOG_ERR, "icfg_open failed on interface %s", ifname); 140 return; 141 } 142 if (icfg_get_addr(h, (struct sockaddr *)&sin, &addrlen, &prefixlen, 143 B_TRUE) != ICFG_SUCCESS) { 144 syslog(LOG_ERR, "icfg_get_addr failed on interface %s", ifname); 145 icfg_close(h); 146 return; 147 } 148 icfg_close(h); 149 report_interface_up(ifname, sin.sin_addr, prefixlen); 150 } 151 152 /* 153 * If this interface matches the currently active llp, return B_TRUE. 154 * Otherwise, return B_FALSE. 155 * Called only from main thread. 156 */ 157 boolean_t 158 interface_is_active(const struct interface *ifp) 159 { 160 if (link_layer_profile == NULL || ifp == NULL) 161 return (B_FALSE); 162 163 return (strcmp(ifp->if_name, link_layer_profile->llp_lname) == 0); 164 } 165 166 /* 167 * Execute 'ifconfig ifname dhcp wait 0'. 168 */ 169 static void 170 start_dhcp(struct interface *ifp) 171 { 172 int res; 173 uint32_t now_s; 174 uint64_t timer_s; 175 176 if (ifp->if_lflags & IF_DHCPSTARTED) { 177 dprintf("start_dhcp: already started; returning"); 178 return; 179 } 180 ifp->if_lflags |= IF_DHCPSTARTED; 181 182 /* 183 * If we need to use DHCP and DHCP is already controlling the 184 * interface, we don't need to do anything. Otherwise, start it now. 185 */ 186 if (!(ifp->if_flags & IFF_DHCPRUNNING)) { 187 dprintf("launching DHCP on %s", ifp->if_name); 188 (void) start_child(IFCONFIG, ifp->if_name, "dhcp", "wait", "0", 189 NULL); 190 } else { 191 dprintf("DHCP already running on %s; resetting timer", 192 ifp->if_name); 193 } 194 ifp->if_lflags &= ~IF_DHCPFAILED; 195 196 /* start dhcp timer */ 197 res = lookup_count_property(OUR_PG, "dhcp_wait_time", &timer_s); 198 if (res == -1) 199 timer_s = NWAM_DEFAULT_DHCP_WAIT_TIME; 200 201 now_s = NSEC_TO_SEC(gethrtime()); 202 ifp->if_timer_expire = now_s + timer_s; 203 204 start_timer(now_s, timer_s); 205 } 206 207 static boolean_t 208 check_svc_up(const char *fmri, int wait_time) 209 { 210 int i; 211 char *state; 212 213 for (i = 1; i <= wait_time; i++) { 214 state = smf_get_state(fmri); 215 if (state == NULL) { 216 syslog(LOG_ERR, "smf_get_state(%s) returned \"%s\"", 217 fmri, scf_strerror(scf_error())); 218 } else { 219 if (strcmp(SCF_STATE_STRING_ONLINE, state) == 0) { 220 free(state); 221 return (B_TRUE); 222 } 223 free(state); 224 } 225 (void) sleep(1); 226 } 227 return (B_FALSE); 228 } 229 230 boolean_t 231 ulp_is_active(void) 232 { 233 return (upper_layer_profile[0] != '\0'); 234 } 235 236 /* 237 * Inputs: 238 * res is a pointer to the scf_resources_t to be released. 239 */ 240 static void 241 release_scf_resources(scf_resources_t *res) 242 { 243 scf_value_destroy(res->sr_val); 244 scf_property_destroy(res->sr_prop); 245 scf_pg_destroy(res->sr_pg); 246 scf_snapshot_destroy(res->sr_snap); 247 scf_instance_destroy(res->sr_inst); 248 (void) scf_handle_unbind(res->sr_handle); 249 scf_handle_destroy(res->sr_handle); 250 } 251 252 /* 253 * Inputs: 254 * lpg is the property group to look up 255 * lprop is the property within that group to look up 256 * Outputs: 257 * res is a pointer to an scf_resources_t. This is an internal 258 * structure that holds all the handles needed to get a specific 259 * property from the running snapshot; on a successful return it 260 * contains the scf_value_t that should be passed to the desired 261 * scf_value_get_foo() function, and must be freed after use by 262 * calling release_scf_resources(). On a failure return, any 263 * resources that may have been assigned to res are released, so 264 * the caller does not need to do any cleanup in the failure case. 265 * Returns: 266 * 0 on success 267 * -1 on failure 268 */ 269 static int 270 get_property_value(const char *lpg, const char *lprop, scf_resources_t *res) 271 { 272 res->sr_inst = NULL; 273 res->sr_snap = NULL; 274 res->sr_pg = NULL; 275 res->sr_prop = NULL; 276 res->sr_val = NULL; 277 278 if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) { 279 syslog(LOG_ERR, "scf_handle_create() failed: %s", 280 scf_strerror(scf_error())); 281 return (-1); 282 } 283 284 if (scf_handle_bind(res->sr_handle) != 0) { 285 scf_handle_destroy(res->sr_handle); 286 syslog(LOG_ERR, "scf_handle_destroy() failed: %s", 287 scf_strerror(scf_error())); 288 return (-1); 289 } 290 if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) { 291 syslog(LOG_ERR, "scf_instance_create() failed: %s", 292 scf_strerror(scf_error())); 293 goto failure; 294 } 295 if (scf_handle_decode_fmri(res->sr_handle, OUR_FMRI, NULL, NULL, 296 res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { 297 syslog(LOG_ERR, "scf_handle_decode_fmri() failed: %s", 298 scf_strerror(scf_error())); 299 goto failure; 300 } 301 if ((res->sr_snap = scf_snapshot_create(res->sr_handle)) == NULL) { 302 syslog(LOG_ERR, "scf_snapshot_create() failed: %s", 303 scf_strerror(scf_error())); 304 goto failure; 305 } 306 if (scf_instance_get_snapshot(res->sr_inst, "running", 307 res->sr_snap) != 0) { 308 syslog(LOG_ERR, "scf_instance_get_snapshot() failed: %s", 309 scf_strerror(scf_error())); 310 goto failure; 311 } 312 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) { 313 syslog(LOG_ERR, "scf_pg_create() failed: %s", 314 scf_strerror(scf_error())); 315 goto failure; 316 } 317 if (scf_instance_get_pg_composed(res->sr_inst, res->sr_snap, lpg, 318 res->sr_pg) != 0) { 319 syslog(LOG_ERR, "scf_instance_get_pg_composed(%s) failed: %s", 320 lpg, scf_strerror(scf_error())); 321 goto failure; 322 } 323 if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) { 324 syslog(LOG_ERR, "scf_property_create() failed: %s", 325 scf_strerror(scf_error())); 326 goto failure; 327 } 328 if (scf_pg_get_property(res->sr_pg, lprop, res->sr_prop) != 0) { 329 syslog(LOG_ERR, "scf_pg_get_property(%s) failed: %s", 330 lprop, scf_strerror(scf_error())); 331 goto failure; 332 } 333 if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) { 334 syslog(LOG_ERR, "scf_value_create() failed: %s", 335 scf_strerror(scf_error())); 336 goto failure; 337 } 338 if (scf_property_get_value(res->sr_prop, res->sr_val) != 0) { 339 syslog(LOG_ERR, "scf_property_get_value() failed: %s", 340 scf_strerror(scf_error())); 341 goto failure; 342 } 343 return (0); 344 345 failure: 346 release_scf_resources(res); 347 return (-1); 348 } 349 350 /* 351 * Inputs: 352 * lpg is the property group to look up 353 * lprop is the property within that group to look up 354 * Outputs: 355 * answer is a pointer to the property value 356 * Returns: 357 * 0 on success 358 * -1 on failure 359 * If successful, the property value is retured in *answer. 360 * Otherwise, *answer is undefined, and it is up to the caller to decide 361 * how to handle that case. 362 */ 363 int 364 lookup_boolean_property(const char *lpg, const char *lprop, boolean_t *answer) 365 { 366 int result = -1; 367 scf_resources_t res; 368 uint8_t prop_val; 369 370 if (get_property_value(lpg, lprop, &res) != 0) { 371 /* 372 * an error was already logged by get_property_value, 373 * and it released any resources assigned to res before 374 * returning. 375 */ 376 return (result); 377 } 378 if (scf_value_get_boolean(res.sr_val, &prop_val) != 0) { 379 syslog(LOG_ERR, "scf_value_get_boolean() failed: %s", 380 scf_strerror(scf_error())); 381 goto cleanup; 382 } 383 *answer = (boolean_t)prop_val; 384 dprintf("lookup_boolean_property(%s, %s) returns %s", lpg, lprop, 385 *answer ? "TRUE" : "FALSE"); 386 result = 0; 387 cleanup: 388 release_scf_resources(&res); 389 return (result); 390 } 391 392 /* 393 * Inputs: 394 * lpg is the property group to look up 395 * lprop is the property within that group to look up 396 * Outputs: 397 * answer is a pointer to the property value 398 * Returns: 399 * 0 on success 400 * -1 on failure 401 * If successful, the property value is retured in *answer. 402 * Otherwise, *answer is undefined, and it is up to the caller to decide 403 * how to handle that case. 404 */ 405 int 406 lookup_count_property(const char *lpg, const char *lprop, uint64_t *answer) 407 { 408 int result = -1; 409 scf_resources_t res; 410 411 if (get_property_value(lpg, lprop, &res) != 0) { 412 /* 413 * an error was already logged by get_property_value, 414 * and it released any resources assigned to res before 415 * returning. 416 */ 417 return (result); 418 } 419 if (scf_value_get_count(res.sr_val, answer) != 0) { 420 syslog(LOG_ERR, "scf_value_get_count() failed: %s", 421 scf_strerror(scf_error())); 422 goto cleanup; 423 } 424 dprintf("lookup_count_property(%s, %s) returns %lld", lpg, lprop, 425 *answer); 426 result = 0; 427 cleanup: 428 release_scf_resources(&res); 429 return (result); 430 } 431 432 void 433 activate_upper_layer_profile(boolean_t do_dhcp, const char *ifname) 434 { 435 FILE *f; 436 char buffer[1024], *cp; 437 size_t buflen; 438 size_t offset; 439 const char bringup[] = "/bringup"; 440 boolean_t should; 441 int res; 442 443 /* 444 * exec the net-svc script to update local config with 445 * any DNS information learned from the DHCP server. 446 */ 447 if (do_dhcp) { 448 res = lookup_boolean_property(OUR_PG, "use_net_svc", &should); 449 /* 450 * If the look-up failed, try anyway: only avoid this if we 451 * know for sure not to. 452 */ 453 if ((res == 0 && should) || (res == -1)) { 454 (void) start_child(NET_SVC_METHOD, "start", ifname, 455 NULL); 456 } 457 } 458 f = popen(ULP_DIR "/check-conditions", "r"); 459 if (f == NULL) { 460 /* note that this doesn't happen if the file is missing */ 461 syslog(LOG_ERR, "popen: check-conditions: %m"); 462 return; 463 } 464 /* 465 * We want to build a path to the user's upper layer profile script 466 * that looks like ULP_DIR "/<string we read here>/bringup". If we 467 * leave some space at the beginning of this buffer for ULP_DIR "/" 468 * that saves us some shuffling later. 469 */ 470 offset = strlcpy(buffer, ULP_DIR "/", sizeof (buffer)); 471 cp = fgets(buffer + offset, 472 MIN(sizeof (upper_layer_profile), sizeof (buffer) - offset), 473 f); 474 buflen = strlen(buffer); 475 if (buffer[buflen - 1] == '\n') 476 buffer[--buflen] = '\0'; 477 478 /* Need to check for script error before interpreting result */ 479 res = pclose(f); 480 if (res == -1) { 481 syslog(LOG_ERR, "check-conditions: pclose: %m"); 482 return; 483 } 484 if (WIFEXITED(res)) { 485 if (WEXITSTATUS(res) == 0) { 486 if (cp == NULL || *cp == '\0') { 487 syslog(LOG_DEBUG, 488 "check-conditions returned no information"); 489 } else { 490 (void) strlcpy(upper_layer_profile, 491 buffer + offset, 492 sizeof (upper_layer_profile)); 493 (void) strlcpy(buffer + buflen, bringup, 494 sizeof (buffer) - buflen); 495 (void) start_child(PFEXEC, "-P", "basic", 496 buffer, NULL); 497 syslog(LOG_NOTICE, 498 "upper layer profile %s activated", 499 upper_layer_profile); 500 report_ulp_activated(upper_layer_profile); 501 } 502 } else if (access(ULP_DIR "/check-conditions", X_OK) == 0) { 503 syslog(LOG_ERR, 504 "check-conditions exited with status %d", 505 WEXITSTATUS(res)); 506 } else if (errno == ENOENT) { 507 syslog(LOG_DEBUG, "check-conditions not present"); 508 } else { 509 syslog(LOG_ERR, "check-conditions: %m"); 510 } 511 } else if (WIFSIGNALED(res)) { 512 syslog(LOG_ERR, "check-conditions exit on SIG%s", 513 strsignal(WTERMSIG(res))); 514 } else { 515 syslog(LOG_ERR, 516 "check-conditions terminated in unknown manner"); 517 } 518 } 519 520 void 521 deactivate_upper_layer_profile(void) 522 { 523 char buffer[1024]; 524 525 /* 526 * If ULP wasn't defined... 527 */ 528 if (!ulp_is_active()) 529 return; 530 531 (void) snprintf(buffer, sizeof (buffer), ULP_DIR "/%s/teardown", 532 upper_layer_profile); 533 (void) start_child(PFEXEC, "-P", "basic", buffer, NULL); 534 535 syslog(LOG_NOTICE, "upper layer profile %s deactivated", 536 upper_layer_profile); 537 538 report_ulp_deactivated(upper_layer_profile); 539 540 upper_layer_profile[0] = '\0'; 541 } 542 543 /* 544 * Returns SUCCESS if the interface is successfully brought up, 545 * FAILURE if bringup fails, or WAITING if we'll need to wait on the GUI to run. 546 * Called only in the main thread or a thread holding machine_lock. 547 */ 548 return_vals_t 549 bringupinterface(const char *ifname, const char *host, const char *ipv6addr, 550 boolean_t ipv6onlink) 551 { 552 struct interface *intf; 553 554 intf = get_interface(ifname); 555 if (intf == NULL) { 556 syslog(LOG_ERR, "could not bring up interface %s: not in list", 557 ifname); 558 return (FAILURE); 559 } 560 561 /* check current state; no point going on if flags are 0 */ 562 if ((intf->if_flags = get_ifflags(ifname, intf->if_family)) == 0) { 563 dprintf("bringupinterface(%s): get_ifflags() returned 0", 564 ifname); 565 return (FAILURE); 566 } 567 568 if (intf->if_type == IF_WIRELESS) { 569 switch (handle_wireless_lan(ifname)) { 570 case WAITING: 571 intf->if_up_attempted = B_TRUE; 572 return (WAITING); 573 case FAILURE: 574 syslog(LOG_INFO, "Could not connect to any WLAN, not " 575 "bringing %s up", ifname); 576 return (FAILURE); 577 } 578 } 579 intf->if_up_attempted = B_TRUE; 580 581 /* physical level must now be up; bail out if not */ 582 intf->if_flags = get_ifflags(ifname, intf->if_family); 583 if (!(intf->if_flags & IFF_RUNNING)) { 584 dprintf("bringupinterface(%s): physical layer down", ifname); 585 return (FAILURE); 586 } 587 588 /* 589 * If the link layer profile says that we want v6 then plumb it and 590 * bring it up; if there's a static address, configure it as well. 591 */ 592 if (ipv6onlink) { 593 dprintf("bringupinterface: configuring ipv6"); 594 (void) start_child(IFCONFIG, ifname, "inet6", "plumb", "up", 595 NULL); 596 if (ipv6addr) { 597 (void) start_child(IFCONFIG, ifname, "inet6", "addif", 598 ipv6addr, "up", NULL); 599 } 600 } 601 intf->if_v6onlink = ipv6onlink; 602 603 if (strcmp(host, "dhcp") == 0) { 604 start_dhcp(intf); 605 } else { 606 (void) start_child(IFCONFIG, ifname, host, NULL); 607 (void) start_child(IFCONFIG, ifname, "up", NULL); 608 } 609 610 syslog(LOG_DEBUG, "brought up %s", ifname); 611 612 return (SUCCESS); 613 } 614 615 /* Called only in the main thread */ 616 void 617 takedowninterface(const char *ifname, libnwam_diag_cause_t cause) 618 { 619 uint64_t flags; 620 struct interface *ifp; 621 622 dprintf("takedowninterface(%s, %d)", ifname, (int)cause); 623 624 if ((ifp = get_interface(ifname)) == NULL) { 625 dprintf("takedowninterface: can't find interface struct for %s", 626 ifname); 627 } 628 629 flags = get_ifflags(ifname, AF_INET); 630 if (flags & IFF_DHCPRUNNING) { 631 /* 632 * We generally prefer doing a release, as that tells the 633 * server that it can relinquish the lease, whereas drop is 634 * just a client-side operation. But if we never came up, 635 * release will fail, because dhcpagent does not allow an 636 * interface without a lease to release, so we have to drop in 637 * that case. So try release first, then fall back to drop. 638 */ 639 if (start_child(IFCONFIG, ifname, "dhcp", "wait", "2", 640 "release", NULL) != 0) { 641 (void) start_child(IFCONFIG, ifname, "dhcp", "wait", 642 "2", "drop", NULL); 643 } 644 } else { 645 if (flags & IFF_UP) 646 (void) start_child(IFCONFIG, ifname, "down", NULL); 647 /* need to unset a statically configured addr */ 648 (void) start_child(IFCONFIG, ifname, "0.0.0.0", "netmask", 649 "0", "broadcast", "0.0.0.0", NULL); 650 } 651 652 if (ifp == NULL || ifp->if_v6onlink) { 653 /* 654 * Unplumbing the link local interface causes dhcp and ndpd to 655 * remove other addresses they have added. 656 */ 657 (void) start_child(IFCONFIG, ifname, "inet6", "unplumb", NULL); 658 } 659 660 if (ifp == NULL || ifp->if_up_attempted) 661 report_interface_down(ifname, cause); 662 663 if (ifp != NULL) { 664 /* We're no longer expecting the interface to be up */ 665 ifp->if_flags = flags & ~IFF_UP; 666 if (ifp->if_type == IF_WIRELESS) { 667 /* and if it's wireless, it's not running, either */ 668 ifp->if_flags &= ~IFF_RUNNING; 669 disconnect_wlan(ifp->if_name); 670 } 671 dprintf("takedown interface, zero cached ip address"); 672 ifp->if_lflags &= ~IF_DHCPSTARTED & ~IF_DHCPACQUIRED; 673 ifp->if_ipv4addr = INADDR_ANY; 674 ifp->if_up_attempted = B_FALSE; 675 } 676 } 677 678 /* 679 * Called only in the main thread 680 * 681 * For IPv6, unplumbing the link local interface causes dhcp and ndpd to remove 682 * other addresses they have added. We watch for routing socket events on the 683 * IPv4 interface, which is always enabled, so no need to keep IPv6 around on a 684 * switch. 685 */ 686 void 687 clear_cached_address(const char *ifname) 688 { 689 struct interface *ifp; 690 uint64_t ifflags; 691 692 if ((ifp = get_interface(ifname)) == NULL) { 693 dprintf("clear_cached_address: can't find interface struct " 694 "for %s", ifname); 695 (void) start_child(IFCONFIG, ifname, "inet6", "unplumb", NULL); 696 return; 697 } 698 if (ifp->if_v6onlink) 699 (void) start_child(IFCONFIG, ifname, "inet6", "unplumb", NULL); 700 ifflags = get_ifflags(ifname, AF_INET); 701 if ((ifflags & IFF_UP) && !(ifflags & IFF_RUNNING)) 702 zero_out_v4addr(ifname); 703 ifp->if_ipv4addr = INADDR_ANY; 704 ifp->if_lflags &= ~IF_DHCPFLAGS; 705 } 706 707 /* 708 * Add an interface struct to the interface list. The list is 709 * partially ordered; all the wired interfaces appear first, 710 * followed by all the wireless interfaces. New interfaces are 711 * added at the end of the appropriate list section. 712 */ 713 static void 714 interface_list_insert(struct interface *ifp) 715 { 716 struct interface **headpp, **lastpp; 717 struct interface *pchain, *nextp; 718 719 if (pthread_mutex_lock(&ifs_lock) != 0) 720 return; 721 722 switch (ifp->if_type) { 723 case IF_WIRELESS: 724 /* 725 * Wireless entries are in the wireless list, and are chained 726 * after the wired entries. If there are no wired entries, then 727 * chain on main list. 728 */ 729 headpp = &ifs_wireless; 730 lastpp = &ifs_wireless_last; 731 pchain = ifs_wired_last; 732 nextp = NULL; 733 break; 734 735 case IF_WIRED: 736 /* 737 * Wired entries are on the wired list, and are chained before 738 * the wireless entries. 739 */ 740 headpp = &ifs_wired; 741 lastpp = &ifs_wired_last; 742 pchain = NULL; 743 nextp = ifs_wireless; 744 break; 745 746 default: 747 /* don't add to the list */ 748 (void) pthread_mutex_unlock(&ifs_lock); 749 return; 750 } 751 752 /* Connect into the correct list */ 753 if (*lastpp == NULL) { 754 /* 755 * If there's a previous list, then wire to the end of 756 * that, as we're the new head here. 757 */ 758 if (pchain != NULL) 759 pchain->if_next = ifp; 760 *headpp = ifp; 761 } else { 762 (*lastpp)->if_next = ifp; 763 } 764 *lastpp = ifp; 765 766 ifp->if_next = nextp; 767 768 /* Fix up the main list; it's always wired-first */ 769 ifs_head = ifs_wired == NULL ? ifs_wireless : ifs_wired; 770 771 (void) pthread_mutex_unlock(&ifs_lock); 772 } 773 774 /* 775 * Returns the interface structure upon success. Returns NULL and sets 776 * errno upon error. 777 */ 778 struct interface * 779 add_interface(sa_family_t family, const char *name, uint64_t flags) 780 { 781 struct interface *i; 782 libnwam_interface_type_t iftype; 783 784 if (name == NULL) 785 return (NULL); 786 787 dprintf("add_interface: found interface %s", name); 788 if (family == AF_INET6) { 789 /* 790 * we don't track IPv6 interfaces separately from their 791 * v4 counterparts; a link either has v4 only, or both 792 * v4 and v6, so we only maintain a v4 interface struct. 793 */ 794 dprintf("not adding v6 interface for %s", name); 795 return (NULL); 796 } else if (family != AF_INET) { 797 /* 798 * the classic "shouldn't happen"... 799 */ 800 dprintf("not adding af %d interface for %s", family, name); 801 return (NULL); 802 } 803 804 if ((iftype = find_if_type(name)) == IF_TUN) { 805 /* 806 * for now, we're ignoring tunnel interfaces (we expect 807 * them to be entirely manipulated by higher layer profile 808 * activation/deactivation scripts) 809 */ 810 dprintf("%s is a tunnel interface; ignoring", name); 811 return (NULL); 812 } 813 814 if ((i = calloc(1, sizeof (*i))) == NULL) { 815 dprintf("add_interface: malloc failed"); 816 return (NULL); 817 } 818 819 (void) strlcpy(i->if_name, name, sizeof (i->if_name)); 820 i->if_family = family; 821 i->if_type = iftype; 822 i->if_flags = flags == 0 ? get_ifflags(name, family) : flags; 823 824 dprintf("added interface %s of type %s af %d; is %savailable", 825 i->if_name, if_type_str(i->if_type), i->if_family, 826 (i->if_flags & IFF_RUNNING) ? "" : "not "); 827 828 interface_list_insert(i); 829 830 if (iftype == IF_WIRELESS) 831 add_wireless_if(name); 832 833 return (i); 834 } 835 836 /* 837 * This is called only by the main thread. 838 */ 839 void 840 remove_interface(const char *ifname) 841 { 842 struct interface *ifp, *prevp = NULL; 843 844 if (pthread_mutex_lock(&ifs_lock) != 0) 845 return; 846 for (ifp = ifs_head; ifp != NULL; ifp = ifp->if_next) { 847 if (strcmp(ifname, ifp->if_name) == 0) { 848 if (prevp == NULL) 849 ifs_head = ifp->if_next; 850 else 851 prevp->if_next = ifp->if_next; 852 if (ifp == ifs_wired_last) { 853 if ((ifs_wired_last = prevp) == NULL) 854 ifs_wired = NULL; 855 } else if (ifp == ifs_wired) { 856 ifs_wired = ifp->if_next; 857 } 858 if (ifp == ifs_wireless_last) { 859 if (prevp != NULL && 860 prevp->if_type != IF_WIRELESS) 861 prevp = NULL; 862 if ((ifs_wireless_last = prevp) == NULL) 863 ifs_wireless = NULL; 864 } else if (ifp == ifs_wireless) { 865 ifs_wireless = ifp->if_next; 866 } 867 break; 868 } 869 prevp = ifp; 870 } 871 (void) pthread_mutex_unlock(&ifs_lock); 872 873 remove_wireless_if(ifname); 874 875 if (ifp != NULL && ifp->if_thr != 0) { 876 (void) pthread_cancel(ifp->if_thr); 877 (void) pthread_join(ifp->if_thr, NULL); 878 } 879 free(ifp); 880 } 881 882 /* 883 * Searches for an interface and returns the interface structure if found. 884 * Returns NULL otherwise. The caller must either be holding ifs_lock, or be 885 * in the main thread. 886 */ 887 struct interface * 888 get_interface(const char *name) 889 { 890 struct interface *ifp; 891 892 if (name == NULL) 893 return (NULL); 894 895 for (ifp = ifs_head; ifp != NULL; ifp = ifp->if_next) { 896 if (strcmp(name, ifp->if_name) == 0) 897 break; 898 } 899 return (ifp); 900 } 901 902 /* 903 * Check to see whether the interface could be started. If the IFF_RUNNING 904 * flag is set, then we're in good shape. Otherwise, wireless interfaces are 905 * special: we'll attempt to connect to an Access Point as part of the start-up 906 * procedure, and IFF_RUNNING won't be present until that's done, so assume 907 * that all wireless interfaces are good to go. This is just an optimization; 908 * we could start everything. 909 */ 910 static boolean_t 911 is_startable(struct interface *ifp) 912 { 913 ifp->if_flags = get_ifflags(ifp->if_name, ifp->if_family); 914 if (ifp->if_flags & IFF_RUNNING) 915 return (B_TRUE); 916 return (ifp->if_type == IF_WIRELESS); 917 } 918 919 /* 920 * For wireless interface, we will try to find out available wireless 921 * network; for wired, if dhcp should be used, start it now to try to 922 * avoid delays there. 923 * 924 * For the real code, we should pass back the network information 925 * gathered. Note that the state engine will then use the llp to 926 * determine which interface should be set up. 927 * 928 * ifs_lock is not held on entry. The caller will cancel this thread and wait 929 * for it to exit if the interface is to be deleted. 930 */ 931 static void * 932 gather_interface_info(void *arg) 933 { 934 struct interface *i = arg; 935 int retv; 936 937 dprintf("Start gathering info for %s", i->if_name); 938 939 switch (i->if_type) { 940 case IF_WIRELESS: 941 /* This generates EV_NEWAP when successful */ 942 retv = launch_wireless_scan(i->if_name); 943 if (retv != 0) 944 dprintf("didn't launch wireless scan: %s", 945 strerror(retv)); 946 break; 947 case IF_WIRED: 948 if (llp_get_ipv4src(i->if_name) == IPV4SRC_DHCP) { 949 /* 950 * The following is to avoid locking up the state 951 * machine as it is currently the choke point. We 952 * start dhcp with a wait time of 0; later, if we see 953 * the link go down (IFF_RUNNING is cleared), we will 954 * drop the attempt. 955 */ 956 if (is_startable(i)) 957 start_dhcp(i); 958 } 959 (void) np_queue_add_event(EV_LINKUP, i->if_name); 960 break; 961 } 962 963 dprintf("Done gathering info for %s", i->if_name); 964 i->if_thr = 0; 965 return (NULL); 966 } 967 968 /* 969 * Caller uses this function to walk through the whole interface list. 970 * For each interface, the caller provided walker is called with 971 * the interface and arg as parameters, and with the ifs_lock held. 972 */ 973 void 974 walk_interface(void (*walker)(struct interface *, void *), void *arg) 975 { 976 struct interface *ifp; 977 978 if (pthread_mutex_lock(&ifs_lock) != 0) 979 return; 980 for (ifp = ifs_head; ifp != NULL; ifp = ifp->if_next) 981 walker(ifp, arg); 982 (void) pthread_mutex_unlock(&ifs_lock); 983 } 984 985 static void 986 print_interface_list(void) 987 { 988 struct interface *wp; 989 990 dprintf("Walking interface list; starting with wired interfaces"); 991 for (wp = ifs_head; wp != NULL; wp = wp->if_next) { 992 if (wp == ifs_wireless) 993 dprintf("Now wireless interfaces"); 994 dprintf("==> %s", wp->if_name); 995 } 996 } 997 998 /* 999 * Walker function passed to icfg_iterate_if() below - the icfg_if_it * 1000 * argument is guaranteed to be non-NULL by icfg_iterate_if(), 1001 * since the function it uses to generate the list - icfg_get_if_list()) - 1002 * guarantees this. 1003 */ 1004 /* ARGSUSED */ 1005 static int 1006 do_add_interface(icfg_if_t *intf, void *arg) 1007 { 1008 uint64_t flags = get_ifflags(intf->if_name, intf->if_protocol); 1009 1010 /* We don't touch loopback interface. */ 1011 if (flags & IFF_LOOPBACK) 1012 return (ICFG_SUCCESS); 1013 1014 /* If adding fails, just ignore that interface... */ 1015 (void) add_interface(intf->if_protocol, intf->if_name, flags); 1016 1017 return (ICFG_SUCCESS); 1018 } 1019 1020 /* 1021 * Walker function passed to icfg_iterate_if() below - the icfg_if_it * 1022 * argument is guaranteed to be non-NULL by icfg_iterate_if(), 1023 * since the function it uses to generate the list - icfg_get_if_list()) - 1024 * guarantees this. 1025 */ 1026 /* ARGSUSED */ 1027 static int 1028 do_unplumb_if(icfg_if_t *intf, void *arg) 1029 { 1030 uint64_t flags = get_ifflags(intf->if_name, intf->if_protocol); 1031 1032 /* We don't touch loopback interface. */ 1033 if (flags & IFF_LOOPBACK) 1034 return (ICFG_SUCCESS); 1035 1036 (void) start_child(IFCONFIG, intf->if_name, 1037 intf->if_protocol == AF_INET6 ? "inet6" : "inet", "unplumb", NULL); 1038 1039 return (ICFG_SUCCESS); 1040 } 1041 1042 void 1043 initialize_interfaces(void) 1044 { 1045 int numifs; 1046 unsigned int wait_time = 1; 1047 boolean_t found_nonlo_if; 1048 1049 /* 1050 * Bring down all interfaces bar lo0. 1051 */ 1052 (void) icfg_iterate_if(AF_INET, ICFG_PLUMBED, NULL, do_unplumb_if); 1053 (void) icfg_iterate_if(AF_INET6, ICFG_PLUMBED, NULL, do_unplumb_if); 1054 1055 /* 1056 * In case dhcpagent is running... If it is running, when 1057 * we do another DHCP command on the same interface later, it may 1058 * be confused. Just kill dhcpagent to simplify handling. 1059 */ 1060 dprintf("killing dhcpagent"); 1061 (void) start_child(PKILL, "-z", zonename, "dhcpagent", NULL); 1062 1063 /* 1064 * Really we should walk the device tree instead of doing 1065 * the 'ifconfig -a plumb'. On the first reconfigure boot 1066 * (after install) 'ifconfig -a plumb' comes back quickly 1067 * without any devices configured if we start before 1068 * 'svc:/system/device/local' finishes. We can't create a 1069 * dependency on device/local because that would create a 1070 * dependency loop through 'svc:/system/filesystem/usr'. So 1071 * instead we wait on device/local. 1072 */ 1073 if (!check_svc_up(DEV_LOCAL_SVC_FMRI, 60)) 1074 syslog(LOG_WARNING, DEV_LOCAL_SVC_FMRI " never came up"); 1075 1076 for (;;) { 1077 icfg_if_t *if_list; 1078 1079 (void) start_child(IFCONFIG, "-a", "plumb", NULL); 1080 1081 /* 1082 * There are cases where we get here and the devices list 1083 * still isn't initialized yet. Hang out until we see 1084 * something other than loopback. 1085 */ 1086 if (icfg_get_if_list(&if_list, &numifs, AF_INET, ICFG_PLUMBED) 1087 != ICFG_SUCCESS) { 1088 syslog(LOG_ERR, "couldn't get the interface list: %m"); 1089 numifs = 0; 1090 if_list = NULL; 1091 } else { 1092 dprintf("found %d plumbed interfaces", numifs); 1093 } 1094 1095 found_nonlo_if = B_FALSE; 1096 while (numifs > 0 && !found_nonlo_if) { 1097 if (strcmp(if_list[--numifs].if_name, LOOPBACK_IF) != 0) 1098 found_nonlo_if = B_TRUE; 1099 } 1100 icfg_free_if_list(if_list); 1101 1102 if (found_nonlo_if) 1103 break; 1104 1105 (void) sleep(wait_time); 1106 wait_time *= 2; 1107 if (wait_time > NWAM_IF_WAIT_DELTA_MAX) 1108 wait_time = NWAM_IF_WAIT_DELTA_MAX; 1109 } 1110 1111 (void) icfg_iterate_if(AF_INET, ICFG_PLUMBED, NULL, do_add_interface); 1112 1113 print_interface_list(); 1114 1115 } 1116 1117 /* 1118 * Walker function used to start info gathering of each interface. Caller 1119 * holds ifs_lock. 1120 */ 1121 void 1122 start_if_info_collect(struct interface *ifp, void *arg) 1123 { 1124 int retv; 1125 pthread_attr_t attr; 1126 1127 /* 1128 * In certain cases we need to refresh the cached flags value as 1129 * it may be stale. Notably, we can miss a DL_NOTE_LINK_DOWN 1130 * event after we initialize interfaces before the routing thread 1131 * is launched. 1132 */ 1133 if (arg != NULL) 1134 ifp->if_flags = get_ifflags(ifp->if_name, ifp->if_family); 1135 1136 /* 1137 * Only if the cable of the wired interface is 1138 * plugged in, start gathering info from it. 1139 */ 1140 if (!is_startable(ifp)) { 1141 dprintf("not gathering info on %s; not running", ifp->if_name); 1142 return; 1143 } 1144 1145 /* 1146 * This is a "fresh start" for the interface, so clear old DHCP flags. 1147 */ 1148 ifp->if_lflags &= ~IF_DHCPFLAGS; 1149 1150 (void) pthread_attr_init(&attr); 1151 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 1152 if ((retv = pthread_create(&ifp->if_thr, &attr, gather_interface_info, 1153 ifp)) != 0) { 1154 syslog(LOG_ERR, "create interface gathering thread: %s", 1155 strerror(retv)); 1156 exit(EXIT_FAILURE); 1157 } else { 1158 dprintf("interface info thread for %s: %d", ifp->if_name, 1159 ifp->if_thr); 1160 } 1161 } 1162 1163 /* 1164 * Walker function used to check timer for each interface. 1165 * If timer has expired, generate a timer event for the 1166 * interface. 1167 */ 1168 static void 1169 iftimer(struct interface *ifp, void *arg) 1170 { 1171 uint32_t now = (uint32_t)(uintptr_t)arg; 1172 1173 if (ifp->if_timer_expire == 0) 1174 return; 1175 1176 if (ifp->if_timer_expire > now) { 1177 start_timer(now, ifp->if_timer_expire - now); 1178 return; 1179 } 1180 1181 ifp->if_timer_expire = 0; 1182 1183 (void) np_queue_add_event(EV_TIMER, ifp->if_name); 1184 } 1185 1186 void 1187 check_interface_timers(uint32_t now) 1188 { 1189 walk_interface(iftimer, (void *)(uint32_t)now); 1190 } 1191 1192 libnwam_interface_type_t 1193 find_if_type(const char *name) 1194 { 1195 uint32_t media; 1196 libnwam_interface_type_t type; 1197 1198 if (name == NULL) { 1199 dprintf("find_if_type: no ifname; returning IF_UNKNOWN"); 1200 return (IF_UNKNOWN); 1201 } 1202 1203 type = IF_WIRED; 1204 if (dladm_name2info(dld_handle, name, NULL, NULL, NULL, &media) != 1205 DLADM_STATUS_OK) { 1206 if (strncmp(name, "ip.tun", 6) == 0 || 1207 strncmp(name, "ip6.tun", 7) == 0 || 1208 strncmp(name, "ip.6to4tun", 10) == 0) 1209 /* 1210 * We'll need to update our tunnel detection once 1211 * the clearview/tun project is integrated; tunnel 1212 * names won't necessarily be ip.tunN. 1213 */ 1214 type = IF_TUN; 1215 } else if (media == DL_WIFI) { 1216 type = IF_WIRELESS; 1217 } 1218 1219 return (type); 1220 } 1221 1222 const char * 1223 if_type_str(libnwam_interface_type_t type) 1224 { 1225 switch (type) { 1226 case IF_WIRED: 1227 return ("wired"); 1228 case IF_WIRELESS: 1229 return ("wireless"); 1230 case IF_TUN: 1231 return ("tunnel"); 1232 case IF_UNKNOWN: 1233 default: 1234 return ("unknown type"); 1235 } 1236 } 1237 1238 /* 1239 * This is called by the routing socket thread to update the IPv4 address on an 1240 * interface. The routing socket thread cannot touch the interface structures 1241 * without holding the global lock, because interface structures can be 1242 * deleted. 1243 */ 1244 void 1245 update_interface_v4_address(const char *ifname, in_addr_t addr) 1246 { 1247 struct in_addr in; 1248 struct interface *ifp; 1249 1250 if (pthread_mutex_lock(&ifs_lock) == 0) { 1251 if ((ifp = get_interface(ifname)) == NULL) { 1252 dprintf("no interface struct for %s; ignoring message", 1253 ifname); 1254 } else if (ifp->if_ipv4addr != addr) { 1255 ifp->if_ipv4addr = addr; 1256 in.s_addr = addr; 1257 dprintf("cached new address %s for link %s", 1258 inet_ntoa(in), ifname); 1259 (void) np_queue_add_event(EV_NEWADDR, ifname); 1260 } else { 1261 dprintf("same address on %s; no event", ifname); 1262 } 1263 (void) pthread_mutex_unlock(&ifs_lock); 1264 } 1265 } 1266 1267 /* 1268 * This is called by the routing socket thread to update the flags on a given 1269 * IPv4 interface. If the interface has changed state, then we launch an event 1270 * or a thread as appropriate. 1271 */ 1272 void 1273 update_interface_flags(const char *ifname, int newflags) 1274 { 1275 struct interface *ifp; 1276 int oldflags; 1277 1278 if (pthread_mutex_lock(&ifs_lock) == 0) { 1279 if ((ifp = get_interface(ifname)) == NULL) { 1280 dprintf("no interface data for %s; ignoring message", 1281 ifname); 1282 } else { 1283 /* 1284 * Check for toggling of the IFF_RUNNING flag. 1285 * 1286 * On any change in the flag value, we turn off the 1287 * DHCP flags; the change in the RUNNING state 1288 * indicates a "fresh start" for the interface, so we 1289 * should try dhcp again. 1290 * 1291 * If the interface was not plugged in and now it is, 1292 * start info collection. 1293 * 1294 * If it was plugged in and now it is unplugged, 1295 * generate an event. 1296 */ 1297 oldflags = ifp->if_flags; 1298 if ((oldflags & IFF_RUNNING) != 1299 (newflags & IFF_RUNNING)) { 1300 ifp->if_lflags &= ~IF_DHCPFLAGS; 1301 } 1302 if (!(newflags & IFF_DHCPRUNNING)) 1303 ifp->if_lflags &= ~IF_DHCPFLAGS; 1304 ifp->if_flags = newflags; 1305 if (!(oldflags & IFF_RUNNING) && 1306 (newflags & IFF_RUNNING)) { 1307 start_if_info_collect(ifp, NULL); 1308 } else if ((oldflags & IFF_RUNNING) && 1309 !(newflags & IFF_RUNNING)) { 1310 (void) np_queue_add_event(EV_LINKDROP, ifname); 1311 } else { 1312 dprintf("no-event flag change on %s: %x -> %x", 1313 ifp->if_name, oldflags, newflags); 1314 } 1315 } 1316 (void) pthread_mutex_unlock(&ifs_lock); 1317 } 1318 } 1319 1320 /* 1321 * Called only in main thread. Note that wireless interfaces are considered 1322 * "ok" even if the IFF_RUNNING bit isn't set. This is because AP attach 1323 * occurs as part of the LLP selection process. 1324 */ 1325 boolean_t 1326 is_interface_ok(const char *ifname) 1327 { 1328 boolean_t is_ok = B_FALSE; 1329 struct interface *ifp; 1330 1331 if ((ifp = get_interface(ifname)) != NULL && 1332 !(ifp->if_lflags & IF_DHCPFAILED) && is_startable(ifp)) 1333 is_ok = B_TRUE; 1334 return (is_ok); 1335 } 1336 1337 /* 1338 * Return the interface type for a given interface name. 1339 */ 1340 libnwam_interface_type_t 1341 get_if_type(const char *ifname) 1342 { 1343 libnwam_interface_type_t ift = IF_UNKNOWN; 1344 struct interface *ifp; 1345 1346 if (pthread_mutex_lock(&ifs_lock) == 0) { 1347 if ((ifp = get_interface(ifname)) != NULL) 1348 ift = ifp->if_type; 1349 (void) pthread_mutex_unlock(&ifs_lock); 1350 } 1351 return (ift); 1352 } 1353 1354 /* 1355 * Get the interface state for storing in llp_t. This is used only with the 1356 * doors interface to return status flags. 1357 */ 1358 void 1359 get_interface_state(const char *ifname, boolean_t *dhcp_failed, 1360 boolean_t *link_up) 1361 { 1362 struct interface *ifp; 1363 1364 *dhcp_failed = *link_up = B_FALSE; 1365 if (pthread_mutex_lock(&ifs_lock) == 0) { 1366 if ((ifp = get_interface(ifname)) != NULL) { 1367 if (ifp->if_lflags & IF_DHCPFAILED) 1368 *dhcp_failed = B_TRUE; 1369 if (ifp->if_flags & IFF_UP) 1370 *link_up = B_TRUE; 1371 } 1372 (void) pthread_mutex_unlock(&ifs_lock); 1373 } 1374 } 1375 1376 /* 1377 * Dump out the interface state via debug messages. 1378 */ 1379 void 1380 print_interface_status(void) 1381 { 1382 struct interface *ifp; 1383 struct in_addr ina; 1384 1385 if (pthread_mutex_lock(&ifs_lock) == 0) { 1386 if (upper_layer_profile[0] != '\0') 1387 dprintf("upper layer profile %s active", 1388 upper_layer_profile); 1389 else 1390 dprintf("no upper layer profile active"); 1391 for (ifp = ifs_head; ifp != NULL; ifp = ifp->if_next) { 1392 ina.s_addr = ifp->if_ipv4addr; 1393 dprintf("I/F %s af %d flags %llX lflags %X type %d " 1394 "expire %u v6 %son-link up %sattempted addr %s", 1395 ifp->if_name, ifp->if_family, ifp->if_flags, 1396 ifp->if_lflags, ifp->if_type, ifp->if_timer_expire, 1397 ifp->if_v6onlink ? "" : "not ", 1398 ifp->if_up_attempted ? "" : "not ", 1399 inet_ntoa(ina)); 1400 } 1401 (void) pthread_mutex_unlock(&ifs_lock); 1402 } 1403 } 1404