Home | History | Annotate | Download | only in nwamd
      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