ncu.c revision 11767:8f30d0e611c6
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 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <arpa/inet.h>
28#include <assert.h>
29#include <libdlaggr.h>
30#include <libdllink.h>
31#include <libdlstat.h>
32#include <libnwam.h>
33#include <libscf.h>
34#include <netinet/in.h>
35#include <stdlib.h>
36#include <strings.h>
37#include <sys/socket.h>
38#include <sys/time.h>
39#include <sys/types.h>
40#include <values.h>
41
42#include "conditions.h"
43#include "events.h"
44#include "objects.h"
45#include "ncp.h"
46#include "util.h"
47
48/*
49 * ncu.c - handles various NCU tasks - intialization/refresh, state machine
50 * for NCUs etc.
51 */
52
53#define	VBOX_IFACE_PREFIX	"vboxnet"
54
55/*
56 * Find ncu of specified type for link/interface name.
57 */
58nwamd_object_t
59nwamd_ncu_object_find(nwam_ncu_type_t type, const char *name)
60{
61	nwam_error_t err;
62	char *object_name;
63	nwamd_object_t ncu_obj = NULL;
64
65	if ((err = nwam_ncu_name_to_typed_name(name, type, &object_name))
66	    != NWAM_SUCCESS) {
67		nlog(LOG_ERR, "nwamd_ncu_find: nwam_ncu_name_to_typed_name "
68		    "returned %s", nwam_strerror(err));
69		return (NULL);
70	}
71	ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, object_name);
72
73	free(object_name);
74	return (ncu_obj);
75}
76
77nwam_error_t
78nwamd_set_ncu_string(nwam_ncu_handle_t ncuh, char **strval, uint_t cnt,
79    const char *prop)
80{
81	nwam_error_t err;
82	nwam_value_t val;
83
84	if ((err = nwam_value_create_string_array(strval, cnt, &val))
85	    != NWAM_SUCCESS)
86		return (err);
87	err = nwam_ncu_set_prop_value(ncuh, prop, val);
88	nwam_value_free(val);
89	return (err);
90}
91
92nwam_error_t
93nwamd_set_ncu_uint(nwam_ncu_handle_t ncuh, uint64_t *uintval, uint_t cnt,
94    const char *prop)
95{
96	nwam_error_t err;
97	nwam_value_t val;
98
99	if ((err = nwam_value_create_uint64_array(uintval, cnt, &val))
100	    != NWAM_SUCCESS)
101		return (err);
102	err = nwam_ncu_set_prop_value(ncuh, prop, val);
103	nwam_value_free(val);
104	return (err);
105}
106
107nwam_error_t
108nwamd_get_ncu_string(nwam_ncu_handle_t ncuh, nwam_value_t *val, char ***strval,
109    uint_t *cnt, const char *prop)
110{
111	nwam_error_t err;
112
113	if ((err = nwam_ncu_get_prop_value(ncuh, prop, val)) != NWAM_SUCCESS)
114		return (err);
115	return (nwam_value_get_string_array(*val, strval, cnt));
116}
117
118nwam_error_t
119nwamd_get_ncu_uint(nwam_ncu_handle_t ncuh, nwam_value_t *val,
120    uint64_t **uintval, uint_t *cnt, const char *prop)
121{
122	nwam_error_t err;
123
124	if ((err = nwam_ncu_get_prop_value(ncuh, prop, val)) != NWAM_SUCCESS)
125		return (err);
126	return (nwam_value_get_uint64_array(*val, uintval, cnt));
127}
128
129/*
130 * Run link/interface state machine in response to a state change
131 * or enable/disable action event.
132 */
133static void
134nwamd_ncu_state_machine(const char *object_name)
135{
136	nwamd_object_t object;
137	nwamd_ncu_t *ncu;
138	link_state_t link_state;
139	nwamd_event_t event;
140	nwam_wlan_t key_wlan, connected_wlan;
141	nwamd_link_t *link;
142	char linkname[NWAM_MAX_NAME_LEN];
143	boolean_t up;
144
145	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, object_name))
146	    == NULL) {
147		nlog(LOG_ERR, "nwamd_ncu_state_machine: "
148		    "request for nonexistent NCU %s", object_name);
149		return;
150	}
151
152	ncu = object->nwamd_object_data;
153	link = &ncu->ncu_node.u_link;
154
155	switch (object->nwamd_object_aux_state) {
156	case NWAM_AUX_STATE_INITIALIZED:
157		if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
158			/*
159			 * For wired/wireless links, need to get link
160			 * up/down events and even if these are not supported,
161			 * dlpi_open()ing the link prevents the driver from
162			 * being unloaded.
163			 */
164			nwamd_dlpi_add_link(object);
165
166			if (link->nwamd_link_media == DL_WIFI) {
167				/*
168				 * First, if we're unexpectedly connected,
169				 * disconnect.
170				 */
171				if (!link->nwamd_link_wifi_connected &&
172				    nwamd_wlan_connected(object)) {
173					nlog(LOG_DEBUG,
174					    "nwamd_ncu_state_machine: "
175					    "WiFi unexpectedly connected, "
176					    "disconnecting...");
177					(void) dladm_wlan_disconnect(dld_handle,
178					    link->nwamd_link_id);
179					nwamd_set_selected_connected(ncu,
180					    B_FALSE, B_FALSE);
181				}
182				/* move to scanning aux state */
183				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
184				    object_name, object->nwamd_object_state,
185				    NWAM_AUX_STATE_LINK_WIFI_SCANNING);
186			} else {
187				/*
188				 * If initial wired link state is unknown, we
189				 * will need to assume the link is up, since
190				 * we won��t get DL_NOTE_LINK_UP/DOWN events.
191				 */
192				link_state = nwamd_get_link_state
193				    (ncu->ncu_name);
194				if (link_state == LINK_STATE_UP ||
195				    link_state == LINK_STATE_UNKNOWN) {
196					nwamd_object_set_state
197					    (NWAM_OBJECT_TYPE_NCU,
198					    object_name, NWAM_STATE_ONLINE,
199					    NWAM_AUX_STATE_UP);
200				} else {
201					nwamd_object_set_state
202					    (NWAM_OBJECT_TYPE_NCU,
203					    object_name,
204					    NWAM_STATE_ONLINE_TO_OFFLINE,
205					    NWAM_AUX_STATE_DOWN);
206				}
207			}
208		} else {
209			/*
210			 * In the current implementation, initialization has to
211			 * start from scratch since the complexity of minimizing
212			 * configuration change is considerable (e.g. if we
213			 * refresh and had DHCP running on the physical
214			 * interface, and now have changed to static assignment,
215			 * we need to remove DHCP etc).  To avoid all this,
216			 * unplumb before re-plumbing the protocols and
217			 * addresses we wish to configure.  In the future, it
218			 * would be good to try and minimize configuration
219			 * changes.
220			 */
221			nwamd_unplumb_interface(ncu, 0, AF_INET);
222			nwamd_unplumb_interface(ncu, 0, AF_INET6);
223
224			/*
225			 * Enqueue a WAITING_FOR_ADDR aux state change so that
226			 * we are eligible to receive the IF_STATE events
227			 * associated with static, DHCP, DHCPv6 and autoconf
228			 * address assignment.  The latter two can happen
229			 * quite quickly after plumbing so we need to be ready.
230			 */
231			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
232			    object_name, NWAM_STATE_OFFLINE_TO_ONLINE,
233			    NWAM_AUX_STATE_IF_WAITING_FOR_ADDR);
234
235			if (ncu->ncu_node.u_if.nwamd_if_ipv4)
236				nwamd_plumb_interface(ncu, 0, AF_INET);
237
238			if (ncu->ncu_node.u_if.nwamd_if_ipv6)
239				nwamd_plumb_interface(ncu, 0, AF_INET6);
240
241			/*
242			 * Configure addresses.  Configure any static addresses
243			 * and start DHCP if required.  If DHCP is not required,
244			 * do a DHCPINFORM to get other networking config
245			 * parameters.  RTM_NEWADDRs - translated into IF_STATE
246			 * events - will then finish the job of bringing us
247			 * online.
248			 */
249			nwamd_configure_interface_addresses(ncu);
250
251			if (ncu->ncu_node.u_if.nwamd_if_dhcp_requested)
252				nwamd_start_dhcp(ncu);
253			else
254				nwamd_dhcp_inform(ncu);
255		}
256		break;
257
258	case NWAM_AUX_STATE_IF_DHCP_TIMED_OUT:
259	case NWAM_AUX_STATE_IF_WAITING_FOR_ADDR:
260		/*
261		 * nothing to do here - RTM_NEWADDRs will trigger IF_STATE
262		 * events to move us online.
263		 */
264		break;
265
266	case NWAM_AUX_STATE_LINK_WIFI_SCANNING:
267		/* launch scan thread */
268		(void) strlcpy(linkname, ncu->ncu_name, sizeof (linkname));
269		(void) nwamd_wlan_scan(linkname);
270		/* Create periodic scan event */
271		nwamd_ncu_create_periodic_scan_event(object);
272		break;
273
274	case NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION:
275		/* send "need choice" event */
276		event = nwamd_event_init_wlan
277		    (ncu->ncu_name, NWAM_EVENT_TYPE_WLAN_NEED_CHOICE, B_FALSE,
278		    link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr,
279		    link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr_num);
280		if (event == NULL)
281			break;
282		nwamd_event_enqueue(event);
283		nwamd_set_selected_connected(ncu, B_FALSE, B_FALSE);
284		break;
285
286	case NWAM_AUX_STATE_LINK_WIFI_NEED_KEY:
287		/*
288		 * Send "need key" event.  Set selected to true, connected
289		 * and have_key to false.  Do not fill in WLAN details as
290		 * multiple WLANs may match the ESSID name, and each may
291		 * have a different speed and channel.
292		 */
293		bzero(&key_wlan, sizeof (key_wlan));
294		(void) strlcpy(key_wlan.nww_essid, link->nwamd_link_wifi_essid,
295		    sizeof (key_wlan.nww_essid));
296		(void) strlcpy(key_wlan.nww_bssid, link->nwamd_link_wifi_bssid,
297		    sizeof (key_wlan.nww_bssid));
298		key_wlan.nww_security_mode =
299		    link->nwamd_link_wifi_security_mode;
300		key_wlan.nww_selected = B_TRUE;
301		key_wlan.nww_connected = B_FALSE;
302		key_wlan.nww_have_key = B_FALSE;
303		event = nwamd_event_init_wlan
304		    (ncu->ncu_name, NWAM_EVENT_TYPE_WLAN_NEED_KEY, B_FALSE,
305		    &key_wlan, 1);
306		if (event == NULL)
307			break;
308		nwamd_event_enqueue(event);
309		break;
310
311	case NWAM_AUX_STATE_LINK_WIFI_CONNECTING:
312		(void) strlcpy(linkname, ncu->ncu_name, sizeof (linkname));
313		nwamd_wlan_connect(linkname);
314		break;
315
316	case NWAM_AUX_STATE_UP:
317	case NWAM_AUX_STATE_DOWN:
318		up = (object->nwamd_object_aux_state == NWAM_AUX_STATE_UP);
319		if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
320			if (link->nwamd_link_media == DL_WIFI) {
321				/*
322				 * Connected/disconnected - send WLAN
323				 * connection report.
324				 */
325				link->nwamd_link_wifi_connected = up;
326				nwamd_set_selected_connected(ncu, B_TRUE, up);
327
328				(void) strlcpy(connected_wlan.nww_essid,
329				    link->nwamd_link_wifi_essid,
330				    sizeof (connected_wlan.nww_essid));
331				(void) strlcpy(connected_wlan.nww_bssid,
332				    link->nwamd_link_wifi_bssid,
333				    sizeof (connected_wlan.nww_bssid));
334				connected_wlan.nww_security_mode =
335				    link->nwamd_link_wifi_security_mode;
336				event = nwamd_event_init_wlan
337				    (ncu->ncu_name,
338				    NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT, up,
339				    &connected_wlan, 1);
340				if (event == NULL)
341					break;
342				nwamd_event_enqueue(event);
343
344				/*
345				 * If disconnected, restart the state machine
346				 * for the WiFi link (WiFi is always trying
347				 * to connect).
348				 *
349				 * If connected, start signal strength
350				 * monitoring thread.
351				 */
352				if (!up && ncu->ncu_enabled) {
353					nlog(LOG_DEBUG,
354					    "nwamd_ncu_state_machine: "
355					    "wifi disconnect - start over "
356					    "after %dsec interval",
357					    WIRELESS_RETRY_INTERVAL);
358					link->nwamd_link_wifi_connected =
359					    B_FALSE;
360					/* propogate down event to IP NCU */
361					nwamd_propogate_link_up_down_to_ip
362					    (ncu->ncu_name, B_FALSE);
363					nwamd_object_set_state_timed
364					    (NWAM_OBJECT_TYPE_NCU, object_name,
365					    NWAM_STATE_OFFLINE_TO_ONLINE,
366					    NWAM_AUX_STATE_INITIALIZED,
367					    WIRELESS_RETRY_INTERVAL);
368				} else {
369					nlog(LOG_DEBUG,
370					    "nwamd_ncu_state_machine: "
371					    "wifi connected, start monitoring");
372					(void) strlcpy(linkname, ncu->ncu_name,
373					    sizeof (linkname));
374					nwamd_wlan_monitor_signal(linkname);
375				}
376			}
377		}
378
379		/* If not in ONLINE/OFFLINE state yet, change state */
380		if ((up && object->nwamd_object_state != NWAM_STATE_ONLINE) ||
381		    (!up && object->nwamd_object_state != NWAM_STATE_OFFLINE)) {
382			nlog(LOG_DEBUG, "nwamd_ncu_state_machine: "
383			    "%s is moving %s", object_name,
384			    up ? "online" : "offline");
385			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
386			    object_name,
387			    up ? NWAM_STATE_ONLINE : NWAM_STATE_OFFLINE,
388			    up ? NWAM_AUX_STATE_UP : NWAM_AUX_STATE_DOWN);
389
390			if (ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE) {
391				if (up) {
392					/*
393					 * Moving online, add v4/v6 default
394					 * routes (if any).
395					 */
396					nwamd_add_default_routes(ncu);
397				} else {
398					/*
399					 * If this is an interface NCU and we
400					 * got a down event, it is a consequence
401					 * of NCU refresh, so reapply addresses
402					 * by reinitializing.
403					 */
404					nwamd_object_set_state
405					    (NWAM_OBJECT_TYPE_NCU, object_name,
406					    NWAM_STATE_OFFLINE_TO_ONLINE,
407					    NWAM_AUX_STATE_INITIALIZED);
408				}
409			}
410		} else {
411			nlog(LOG_DEBUG, "nwamd_ncu_state_machine: "
412			    "%s is %s", object_name,
413			    up ? "online" : "offline");
414		}
415		/*
416		 * NCU is UP or DOWN, trigger all condition checking, even if
417		 * the NCU is already in the ONLINE state - an ENM may depend
418		 * on NCU activity.
419		 */
420		nwamd_create_triggered_condition_check_event(NEXT_FEW_SECONDS);
421		break;
422
423	case NWAM_AUX_STATE_CONDITIONS_NOT_MET:
424		/*
425		 * Link/interface is moving offline.  Nothing to do except
426		 * for WiFi, where we disconnect.  Don't unplumb IP on
427		 * a link since it may be a transient change.
428		 */
429		if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
430			if (link->nwamd_link_media == DL_WIFI) {
431				(void) dladm_wlan_disconnect(dld_handle,
432				    link->nwamd_link_id);
433				link->nwamd_link_wifi_connected = B_FALSE;
434				nwamd_set_selected_connected(ncu, B_FALSE,
435				    B_FALSE);
436			}
437		} else {
438			/*
439			 * Unplumb here. In the future we may elaborate on
440			 * the approach used and not unplumb for WiFi
441			 * until we reconnect to a different WLAN (i.e. with
442			 * a different ESSID).
443			 */
444			nwamd_unplumb_interface(ncu, 0, AF_INET);
445			nwamd_unplumb_interface(ncu, 0, AF_INET6);
446		}
447		if (object->nwamd_object_state != NWAM_STATE_OFFLINE) {
448			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
449			    object_name, NWAM_STATE_OFFLINE,
450			    NWAM_AUX_STATE_CONDITIONS_NOT_MET);
451		}
452		break;
453
454	case NWAM_AUX_STATE_MANUAL_DISABLE:
455		/* Manual disable, set enabled state appropriately. */
456		ncu->ncu_enabled = B_FALSE;
457		/* FALLTHROUGH */
458	case NWAM_AUX_STATE_UNINITIALIZED:
459	case NWAM_AUX_STATE_NOT_FOUND:
460		/*
461		 * Link/interface NCU has been disabled/deactivated/removed.
462		 * For WiFi links disconnect, and for IP interfaces we unplumb.
463		 */
464		if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
465			if (link->nwamd_link_media == DL_WIFI) {
466				(void) dladm_wlan_disconnect(dld_handle,
467				    link->nwamd_link_id);
468				link->nwamd_link_wifi_connected = B_FALSE;
469				nwamd_set_selected_connected(ncu, B_FALSE,
470				    B_FALSE);
471			}
472			nwamd_dlpi_delete_link(object);
473		} else {
474			/* Unplumb here. */
475			if (ncu->ncu_node.u_if.nwamd_if_ipv4) {
476				nwamd_unplumb_interface(ncu, 0, AF_INET);
477			}
478			if (ncu->ncu_node.u_if.nwamd_if_ipv6) {
479				nwamd_unplumb_interface(ncu, 0, AF_INET6);
480			}
481			/* trigger location condition checking */
482			nwamd_create_triggered_condition_check_event(0);
483		}
484
485		switch (object->nwamd_object_aux_state) {
486		case NWAM_AUX_STATE_MANUAL_DISABLE:
487			/* Change state to DISABLED if manually disabled */
488			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
489			    object_name, NWAM_STATE_DISABLED,
490			    NWAM_AUX_STATE_MANUAL_DISABLE);
491			/* Note that NCU has been disabled */
492			ncu->ncu_enabled = B_FALSE;
493			break;
494		case NWAM_AUX_STATE_NOT_FOUND:
495			/* Change state to UNINITIALIZED for device removal */
496			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
497			    object_name, NWAM_STATE_UNINITIALIZED,
498			    NWAM_AUX_STATE_NOT_FOUND);
499			break;
500		default:
501			break;
502		}
503		break;
504	default:
505		nlog(LOG_ERR, "nwamd_ncu_state_machine: unexpected state");
506		break;
507	}
508
509	nwamd_object_release(object);
510}
511
512static int
513ncu_create_init_fini_event(nwam_ncu_handle_t ncuh, void *data)
514{
515	boolean_t *init = data;
516	char *name, *typedname;
517	nwam_error_t err;
518	nwam_value_t typeval = NULL;
519	uint64_t *type;
520	uint_t numvalues;
521	nwamd_event_t ncu_event;
522
523	if (nwam_ncu_get_name(ncuh, &name) != NWAM_SUCCESS) {
524		nlog(LOG_ERR,
525		    "ncu_create_init_fini_event: could not get NCU name");
526		return (0);
527	}
528
529	nlog(LOG_DEBUG, "ncu_create_init_fini_event(%s, %p)", name, data);
530
531	if ((err = nwamd_get_ncu_uint(ncuh, &typeval, &type, &numvalues,
532	    NWAM_NCU_PROP_TYPE)) != NWAM_SUCCESS) {
533		nlog(LOG_ERR, "ncu_create_init_fini_event: "
534		    "could not get NCU type: %s", nwam_strerror(err));
535		free(name);
536		nwam_value_free(typeval);
537		return (0);
538	}
539
540	/* convert name to typedname for event */
541	if ((err = nwam_ncu_name_to_typed_name(name, *type, &typedname))
542	    != NWAM_SUCCESS) {
543		nlog(LOG_ERR, "ncu_create_init_fini_event: "
544		    "NCU name translation failed: %s", nwam_strerror(err));
545		free(name);
546		return (0);
547	}
548	free(name);
549	nwam_value_free(typeval);
550
551	ncu_event = nwamd_event_init(*init ?
552	    NWAM_EVENT_TYPE_OBJECT_INIT : NWAM_EVENT_TYPE_OBJECT_FINI,
553	    NWAM_OBJECT_TYPE_NCU, 0, typedname);
554	if (ncu_event != NULL)
555		nwamd_event_enqueue(ncu_event);
556	free(typedname);
557
558	return (0);
559}
560
561/*
562 * Initialization - walk the NCUs, creating initialization events for each
563 * NCU.  nwamd_ncu_handle_init_event() will check if the associated
564 * physical link exists or not.
565 */
566void
567nwamd_init_ncus(void)
568{
569	boolean_t init = B_TRUE;
570
571	(void) pthread_mutex_lock(&active_ncp_mutex);
572	if (active_ncph != NULL) {
573		nlog(LOG_DEBUG, "nwamd_init_ncus: "
574		    "(re)intializing NCUs for NCP %s", active_ncp);
575		(void) nwam_ncp_walk_ncus(active_ncph,
576		    ncu_create_init_fini_event, &init, NWAM_FLAG_NCU_TYPE_ALL,
577		    NULL);
578	}
579	(void) pthread_mutex_unlock(&active_ncp_mutex);
580}
581
582void
583nwamd_fini_ncus(void)
584{
585	boolean_t init = B_FALSE;
586
587	/* We may not have an active NCP on initialization, so skip fini */
588	(void) pthread_mutex_lock(&active_ncp_mutex);
589	if (active_ncph != NULL) {
590		nlog(LOG_DEBUG, "nwamd_fini_ncus: deinitializing NCUs for %s",
591		    active_ncp);
592		(void) nwam_ncp_walk_ncus(active_ncph,
593		    ncu_create_init_fini_event, &init, NWAM_FLAG_NCU_TYPE_ALL,
594		    NULL);
595	}
596	(void) pthread_mutex_unlock(&active_ncp_mutex);
597}
598
599/*
600 * Most properties of this type don't need to be cached locally.  Only those
601 * interesting to the daemon are stored in an nwamd_ncu_t.
602 */
603static void
604populate_common_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
605{
606	nwam_value_t ncu_prop;
607	nwam_error_t err;
608	boolean_t enablevalue;
609	uint_t numvalues;
610	char **parent;
611
612	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_ENABLED,
613	    &ncu_prop)) != NWAM_SUCCESS) {
614		char *name;
615		(void) nwam_ncu_name_to_typed_name(ncu_data->ncu_name,
616		    ncu_data->ncu_type, &name);
617		nlog(LOG_ERR, "nwam_ncu_get_prop_value %s ENABLED failed: %s",
618		    name, nwam_strerror(err));
619		free(name);
620		ncu_data->ncu_enabled = B_TRUE;
621	} else {
622		if ((err = nwam_value_get_boolean(ncu_prop, &enablevalue)) !=
623		    NWAM_SUCCESS) {
624			nlog(LOG_ERR, "nwam_value_get_boolean ENABLED failed: "
625			    "%s", nwam_strerror(err));
626		} else {
627			ncu_data->ncu_enabled = enablevalue;
628		}
629		nwam_value_free(ncu_prop);
630	}
631
632	if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &parent,
633	    &numvalues, NWAM_NCU_PROP_PARENT_NCP)) != NWAM_SUCCESS) {
634		nlog(LOG_ERR, "nwam_ncu_get_prop_value %s PARENT failed: %s",
635		    ncu_data->ncu_name, nwam_strerror(err));
636	} else {
637		(void) strlcpy(ncu_data->ncu_parent, parent[0],
638		    sizeof (ncu_data->ncu_parent));
639		nwam_value_free(ncu_prop);
640	}
641}
642
643/*
644 * Read in link properties.
645 */
646static void
647populate_link_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
648{
649	nwam_value_t ncu_prop;
650	nwam_error_t err;
651	char **mac_addr;
652	uint64_t *uintval;
653	uint_t numvalues;
654
655	/* activation-mode */
656	if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval, &numvalues,
657	    NWAM_NCU_PROP_ACTIVATION_MODE)) != NWAM_SUCCESS) {
658		nlog(LOG_ERR,
659		    "populate_link_ncu_properties: could not get %s value: %s",
660		    NWAM_NCU_PROP_ACTIVATION_MODE, nwam_strerror(err));
661	} else {
662		ncu_data->ncu_node.u_link.nwamd_link_activation_mode =
663		    uintval[0];
664		nwam_value_free(ncu_prop);
665	}
666
667	/* priority-group and priority-mode for prioritized activation */
668	if (ncu_data->ncu_node.u_link.nwamd_link_activation_mode ==
669	    NWAM_ACTIVATION_MODE_PRIORITIZED) {
670		/* ncus with prioritized activation are always enabled */
671		ncu_data->ncu_enabled = B_TRUE;
672		if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval,
673		    &numvalues, NWAM_NCU_PROP_PRIORITY_MODE))
674		    != NWAM_SUCCESS) {
675			nlog(LOG_ERR, "populate_link_ncu_properties: "
676			    "could not get %s value: %s",
677			    NWAM_NCU_PROP_PRIORITY_MODE, nwam_strerror(err));
678		} else {
679			ncu_data->ncu_node.u_link.nwamd_link_priority_mode =
680			    uintval[0];
681			nwam_value_free(ncu_prop);
682		}
683
684		if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval,
685		    &numvalues, NWAM_NCU_PROP_PRIORITY_GROUP))
686		    != NWAM_SUCCESS) {
687			nlog(LOG_ERR, "populate_link_ncu_properties: "
688			    "could not get %s value: %s",
689			    NWAM_NCU_PROP_PRIORITY_GROUP, nwam_strerror(err));
690		} else {
691			ncu_data->ncu_node.u_link.nwamd_link_priority_group =
692			    uintval[0];
693			nwam_value_free(ncu_prop);
694		}
695	}
696
697	/* link-mac-addr */
698	if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &mac_addr, &numvalues,
699	    NWAM_NCU_PROP_LINK_MAC_ADDR)) != NWAM_SUCCESS) {
700		nlog(LOG_DEBUG,
701		    "populate_link_ncu_properties: could not get %s value: %s",
702		    NWAM_NCU_PROP_LINK_MAC_ADDR, nwam_strerror(err));
703		ncu_data->ncu_node.u_link.nwamd_link_mac_addr = NULL;
704	} else {
705		ncu_data->ncu_node.u_link.nwamd_link_mac_addr =
706		    strdup(*mac_addr);
707		ncu_data->ncu_node.u_link.nwamd_link_mac_addr_len =
708		    strlen(*mac_addr);
709		nwam_value_free(ncu_prop);
710	}
711
712	/* link-mtu */
713	if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval, &numvalues,
714	    NWAM_NCU_PROP_LINK_MTU)) != NWAM_SUCCESS) {
715		nlog(LOG_DEBUG,
716		    "populate_link_ncu_properties: could not get %s value: %s",
717		    NWAM_NCU_PROP_LINK_MTU, nwam_strerror(err));
718		ncu_data->ncu_node.u_link.nwamd_link_mtu = 0;
719	} else {
720		ncu_data->ncu_node.u_link.nwamd_link_mtu = uintval[0];
721		nwam_value_free(ncu_prop);
722	}
723
724	/* link-autopush */
725	if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop,
726	    &ncu_data->ncu_node.u_link.nwamd_link_autopush,
727	    &ncu_data->ncu_node.u_link.nwamd_link_num_autopush,
728	    NWAM_NCU_PROP_LINK_AUTOPUSH)) != NWAM_SUCCESS) {
729		nlog(LOG_DEBUG,
730		    "populate_link_ncu_properties: could not get %s value: %s",
731		    NWAM_NCU_PROP_LINK_AUTOPUSH, nwam_strerror(err));
732		ncu_data->ncu_node.u_link.nwamd_link_num_autopush = 0;
733	}
734}
735
736static void
737populate_ip_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
738{
739	nwamd_if_t *nif = &ncu_data->ncu_node.u_if;
740	struct nwamd_if_address **nifa, *nifai, *nifait;
741	char *prefix;
742	boolean_t static_addr = B_FALSE;
743	uint64_t *addrsrcvalue;
744	nwam_value_t ncu_prop;
745	nwam_error_t err;
746	char **addrvalue;
747	uint_t numvalues;
748	uint64_t *ipversion;
749	int i;
750
751	nif->nwamd_if_ipv4 = B_FALSE;
752	nif->nwamd_if_ipv6 = B_FALSE;
753	nif->nwamd_if_dhcp_requested = B_FALSE;
754	nif->nwamd_if_stateful_requested = B_FALSE;
755	nif->nwamd_if_stateless_requested = B_FALSE;
756	nif->nwamd_if_ipv4_default_route_set = B_FALSE;
757	nif->nwamd_if_ipv6_default_route_set = B_FALSE;
758
759	/* ip-version */
760	if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &ipversion, &numvalues,
761	    NWAM_NCU_PROP_IP_VERSION)) != NWAM_SUCCESS) {
762		nlog(LOG_ERR,
763		    "populate_ip_ncu_properties: could not get %s value: %s",
764		    NWAM_NCU_PROP_IP_VERSION, nwam_strerror(err));
765	} else {
766		for (i = 0; i < numvalues; i++) {
767			switch (ipversion[i]) {
768			case IPV4_VERSION:
769				nif->nwamd_if_ipv4 = B_TRUE;
770				break;
771			case IPV6_VERSION:
772				nif->nwamd_if_ipv6 = B_TRUE;
773				break;
774			default:
775				nlog(LOG_ERR, "bogus ip version %lld",
776				    ipversion[i]);
777				break;
778			}
779		}
780		nwam_value_free(ncu_prop);
781	}
782
783	/* Free the old list. */
784	for (nifai = nif->nwamd_if_list; nifai != NULL; nifai = nifait) {
785		nifait = nifai->next;
786		nifai->next = NULL;
787		free(nifai);
788	}
789	nif->nwamd_if_list = NULL;
790	nifa = &(nif->nwamd_if_list);
791
792	if (!nif->nwamd_if_ipv4)
793		goto skip_ipv4;
794
795	/* ipv4-addrsrc */
796	if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &addrsrcvalue,
797	    &numvalues, NWAM_NCU_PROP_IPV4_ADDRSRC)) != NWAM_SUCCESS) {
798		nlog(nif->nwamd_if_ipv4 ? LOG_ERR : LOG_DEBUG,
799		    "populate_ip_ncu_properties: could not get %s value: %s",
800		    NWAM_NCU_PROP_IPV4_ADDRSRC, nwam_strerror(err));
801	} else {
802		for (i = 0; i < numvalues; i++) {
803			switch (addrsrcvalue[i]) {
804			case NWAM_ADDRSRC_DHCP:
805				nif->nwamd_if_dhcp_requested = B_TRUE;
806				break;
807			case NWAM_ADDRSRC_STATIC:
808				static_addr = B_TRUE;
809				break;
810			default:
811				break;
812			}
813		}
814		nwam_value_free(ncu_prop);
815	}
816	if (nif->nwamd_if_dhcp_requested) {
817		if ((*nifa = calloc(sizeof (**nifa), 1)) != NULL) {
818			(*nifa)->address.sa_family = AF_INET;
819			(*nifa)->dhcp_if = B_TRUE;
820			nifa = &((*nifa)->next);
821			*nifa = NULL;
822		}
823	}
824
825	/* ipv4-addr */
826	if (static_addr) {
827		if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
828		    &numvalues, NWAM_NCU_PROP_IPV4_ADDR)) != NWAM_SUCCESS) {
829			nlog(LOG_ERR, "populate_ip_ncu_properties: "
830			    "could not get %s value; %s",
831			    NWAM_NCU_PROP_IPV4_ADDR, nwam_strerror(err));
832		} else {
833			struct sockaddr_in *s;
834
835			for (i = 0; i < numvalues; i++) {
836				if ((*nifa = calloc(sizeof (**nifa), 1))
837				    == NULL) {
838					nlog(LOG_ERR, "couldn't allocate nwamd"
839					    "address");
840					continue;
841				}
842				(*nifa)->address.sa_family = AF_INET;
843				/*LINTED*/
844				s = (struct sockaddr_in *)&(*nifa)->address;
845				s->sin_family = AF_INET;
846				s->sin_port = 0;
847				prefix = strchr(addrvalue[i], '/');
848				if (prefix != NULL) {
849					*prefix++ = 0;
850					(*nifa)->prefix = atoi(prefix);
851				}
852				(void) inet_pton(AF_INET, addrvalue[i],
853				    &(s->sin_addr));
854				nifa = &((*nifa)->next);
855			}
856			*nifa = NULL;
857
858			nwam_value_free(ncu_prop);
859		}
860	}
861
862	/* get default route, if any */
863	if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
864	    &numvalues, NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE)) == NWAM_SUCCESS) {
865		/* Only one default route is allowed. */
866		nif->nwamd_if_ipv4_default_route.sin_family = AF_INET;
867		(void) inet_pton(AF_INET, addrvalue[0],
868		    &(nif->nwamd_if_ipv4_default_route.sin_addr));
869		nif->nwamd_if_ipv4_default_route_set = B_TRUE;
870		nwam_value_free(ncu_prop);
871	}
872
873skip_ipv4:
874
875	if (!nif->nwamd_if_ipv6)
876		goto skip_ipv6;
877
878	/* ipv6-addrsrc */
879	static_addr = B_FALSE;
880	if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &addrsrcvalue,
881	    &numvalues, NWAM_NCU_PROP_IPV6_ADDRSRC)) != NWAM_SUCCESS) {
882		nlog(nif->nwamd_if_ipv6 ? LOG_ERR : LOG_DEBUG,
883		    "populate_ip_ncu_properties: could not get %s value: %s",
884		    NWAM_NCU_PROP_IPV6_ADDRSRC, nwam_strerror(err));
885	} else {
886		for (i = 0; i < numvalues; i++) {
887			switch (addrsrcvalue[i]) {
888			case NWAM_ADDRSRC_DHCP:
889				nif->nwamd_if_stateful_requested = B_TRUE;
890				break;
891			case NWAM_ADDRSRC_AUTOCONF:
892				nif->nwamd_if_stateless_requested = B_TRUE;
893				break;
894			case NWAM_ADDRSRC_STATIC:
895				static_addr = B_TRUE;
896				break;
897			default:
898				break;
899			}
900		}
901		nwam_value_free(ncu_prop);
902	}
903	if (nif->nwamd_if_stateful_requested) {
904		if ((*nifa = calloc(sizeof (**nifa), 1)) != NULL) {
905			(*nifa)->address.sa_family = AF_INET6;
906			(*nifa)->dhcp_if = B_TRUE;
907			nifa = &((*nifa)->next);
908			*nifa = NULL;
909		}
910	}
911	if (nif->nwamd_if_stateless_requested) {
912		if ((*nifa = calloc(sizeof (**nifa), 1)) != NULL) {
913			(*nifa)->address.sa_family = AF_INET6;
914			(*nifa)->stateless_if = B_TRUE;
915			nifa = &((*nifa)->next);
916			*nifa = NULL;
917		}
918	}
919
920	/* ipv6-addr */
921	if (static_addr) {
922		if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
923		    &numvalues, NWAM_NCU_PROP_IPV6_ADDR)) != NWAM_SUCCESS) {
924			nlog(LOG_ERR, "populate_ip_ncu_properties: "
925			    "could not get %s value; %s",
926			    NWAM_NCU_PROP_IPV6_ADDR, nwam_strerror(err));
927		} else {
928			struct sockaddr_in6 *s;
929
930			for (i = 0; i < numvalues; i++) {
931				if ((*nifa = calloc(sizeof (**nifa), 1))
932				    == NULL) {
933					nlog(LOG_ERR, "couldn't allocate nwamd"
934					    "address");
935					continue;
936				}
937				(*nifa)->address.sa_family = AF_INET6;
938				/*LINTED*/
939				s = (struct sockaddr_in6 *)&(*nifa)->address;
940				s->sin6_family = AF_INET6;
941				s->sin6_port = 0;
942				prefix = strchr(addrvalue[i], '/');
943				if (prefix != NULL) {
944					*prefix++ = 0;
945					(*nifa)->prefix = atoi(prefix);
946				}
947				(void) inet_pton(AF_INET6, addrvalue[i],
948				    &(s->sin6_addr));
949				nifa = &((*nifa)->next);
950			}
951			*nifa = NULL;
952
953			nwam_value_free(ncu_prop);
954		}
955	}
956
957	/* get default route, if any */
958	if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
959	    &numvalues, NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE)) == NWAM_SUCCESS) {
960		/* Only one default route is allowed. */
961		nif->nwamd_if_ipv6_default_route.sin6_family = AF_INET6;
962		(void) inet_pton(AF_INET6, addrvalue[0],
963		    &(nif->nwamd_if_ipv6_default_route.sin6_addr));
964		nif->nwamd_if_ipv6_default_route_set = B_TRUE;
965		nwam_value_free(ncu_prop);
966	}
967
968skip_ipv6:
969	;
970}
971
972static nwamd_ncu_t *
973nwamd_ncu_init(nwam_ncu_type_t ncu_type, const char *name)
974{
975	nwamd_ncu_t *rv;
976
977	nlog(LOG_DEBUG, "nwamd_ncu_init(%d, %s)", ncu_type, name);
978
979	if ((rv = calloc(1, sizeof (*rv))) == NULL)
980		return (NULL);
981
982	rv->ncu_type = ncu_type;
983	rv->ncu_name = strdup(name);
984	rv->ncu_enabled = B_FALSE;
985
986	/* Initialize link/interface-specific data */
987	if (rv->ncu_type == NWAM_NCU_TYPE_LINK) {
988		(void) bzero(&rv->ncu_node.u_link, sizeof (nwamd_link_t));
989		(void) dladm_name2info(dld_handle, name,
990		    &rv->ncu_node.u_link.nwamd_link_id, NULL, NULL,
991		    &rv->ncu_node.u_link.nwamd_link_media);
992		(void) pthread_mutex_init(
993		    &rv->ncu_node.u_link.nwamd_link_wifi_mutex, NULL);
994		rv->ncu_node.u_link.nwamd_link_wifi_priority = MAXINT;
995	} else {
996		(void) bzero(&rv->ncu_node.u_if, sizeof (nwamd_if_t));
997	}
998
999	return (rv);
1000}
1001
1002void
1003nwamd_ncu_free(nwamd_ncu_t *ncu)
1004{
1005	if (ncu != NULL) {
1006		assert(ncu->ncu_type == NWAM_NCU_TYPE_LINK ||
1007		    ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE);
1008		if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
1009			struct nwamd_link *l = &ncu->ncu_node.u_link;
1010			int i;
1011
1012			free(l->nwamd_link_wifi_key);
1013			free(l->nwamd_link_mac_addr);
1014			for (i = 0; i < l->nwamd_link_num_autopush; i++)
1015				free(l->nwamd_link_autopush[i]);
1016		} else if (ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE) {
1017			struct nwamd_if_address *nifa;
1018
1019			nifa = ncu->ncu_node.u_if.nwamd_if_list;
1020			while (nifa != NULL) {
1021				struct nwamd_if_address *n;
1022
1023				n = nifa;
1024				nifa = nifa->next;
1025				free(n);
1026			}
1027		}
1028		free(ncu->ncu_name);
1029		free(ncu);
1030	}
1031}
1032
1033static int
1034nwamd_ncu_display(nwamd_object_t ncu_obj, void *data)
1035{
1036	nwamd_ncu_t *ncu = (nwamd_ncu_t *)ncu_obj->nwamd_object_data;
1037	data = data;
1038	nlog(LOG_DEBUG, "NCU (%p) %s state %s, %s",
1039	    (void *)ncu, ncu_obj->nwamd_object_name,
1040	    nwam_state_to_string(ncu_obj->nwamd_object_state),
1041	    nwam_aux_state_to_string(ncu_obj->nwamd_object_aux_state));
1042	return (0);
1043}
1044
1045void
1046nwamd_log_ncus(void)
1047{
1048	nlog(LOG_DEBUG, "NCP %s", active_ncp);
1049	(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU, nwamd_ncu_display,
1050	    NULL);
1051}
1052
1053int
1054nwamd_ncu_action(const char *ncu, const char *parent, nwam_action_t action)
1055{
1056	nwamd_event_t ncu_event = nwamd_event_init_object_action
1057	    (NWAM_OBJECT_TYPE_NCU, ncu, parent, action);
1058	if (ncu_event == NULL)
1059		return (1);
1060	nwamd_event_enqueue(ncu_event);
1061	return (0);
1062}
1063
1064static void
1065add_phys_ncu_to_ncp(nwam_ncp_handle_t ncph, const char *name)
1066{
1067	dladm_status_t dlrtn;
1068	uint32_t media;
1069	boolean_t is_wireless;
1070	nwam_error_t err;
1071	nwam_ncu_handle_t ncuh;
1072	uint64_t uintval;
1073
1074	if ((dlrtn = dladm_name2info(dld_handle, name, NULL, NULL, NULL,
1075	    &media)) != DLADM_STATUS_OK) {
1076		char errmsg[DLADM_STRSIZE];
1077		nlog(LOG_ERR, "failed to get media type for %s: %s", name,
1078		    dladm_status2str(dlrtn, errmsg));
1079		return;
1080	}
1081	is_wireless = (media == DL_WIFI);
1082
1083	if ((err = nwam_ncu_create(ncph, name, NWAM_NCU_TYPE_LINK,
1084	    NWAM_NCU_CLASS_PHYS, &ncuh)) != NWAM_SUCCESS) {
1085		nlog(LOG_ERR, "failed to create link ncu for %s: %s", name,
1086		    nwam_strerror(err));
1087		if (err == NWAM_ENTITY_READ_ONLY) {
1088			nwamd_event_t retry_event;
1089
1090			/*
1091			 * Root filesystem may be read-only, retry in
1092			 * a few seconds.
1093			 */
1094			nlog(LOG_DEBUG, "Retrying addition of phys ncu for %s",
1095			    name);
1096			retry_event = nwamd_event_init_link_action(name,
1097			    NWAM_ACTION_ADD);
1098			if (retry_event != NULL) {
1099				nwamd_event_enqueue_timed(retry_event,
1100				    NWAMD_READONLY_RETRY_INTERVAL);
1101			}
1102		}
1103		return;
1104	}
1105
1106	uintval = NWAM_ACTIVATION_MODE_PRIORITIZED;
1107	if ((err = nwamd_set_ncu_uint(ncuh, &uintval, 1,
1108	    NWAM_NCU_PROP_ACTIVATION_MODE)) != NWAM_SUCCESS) {
1109		goto finish;
1110	}
1111
1112	uintval = is_wireless ? 1 : 0;
1113	if ((err = nwamd_set_ncu_uint(ncuh, &uintval, 1,
1114	    NWAM_NCU_PROP_PRIORITY_GROUP)) != NWAM_SUCCESS) {
1115		goto finish;
1116	}
1117
1118	uintval = is_wireless ? NWAM_PRIORITY_MODE_EXCLUSIVE :
1119	    NWAM_PRIORITY_MODE_SHARED;
1120	if ((err = nwamd_set_ncu_uint(ncuh, &uintval, 1,
1121	    NWAM_NCU_PROP_PRIORITY_MODE)) != NWAM_SUCCESS) {
1122		goto finish;
1123	}
1124
1125	err = nwam_ncu_commit(ncuh, 0);
1126
1127finish:
1128	nwam_ncu_free(ncuh);
1129	if (err != NWAM_SUCCESS) {
1130		nlog(LOG_ERR,
1131		    "failed to create automatic link ncu for %s: %s",
1132		    name, nwam_strerror(err));
1133	}
1134}
1135
1136static void
1137add_ip_ncu_to_ncp(nwam_ncp_handle_t ncph, const char *name)
1138{
1139	nwam_error_t err;
1140	nwam_ncu_handle_t ncuh;
1141
1142	if ((err = nwam_ncu_create(ncph, name, NWAM_NCU_TYPE_INTERFACE,
1143	    NWAM_NCU_CLASS_IP, &ncuh)) != NWAM_SUCCESS) {
1144		nlog(LOG_ERR, "failed to create ip ncu for %s: %s", name,
1145		    nwam_strerror(err));
1146		/*
1147		 * Root filesystem may be read-only, but no need to
1148		 * retry here since add_phys_ncu_to_ncp() enqueues
1149		 * a retry event which will lead to add_ip_ncu_to_ncp()
1150		 * being called.
1151		 */
1152		return;
1153	}
1154
1155	/* IP NCU has the default values, so nothing else to do */
1156	err = nwam_ncu_commit(ncuh, 0);
1157
1158finish:
1159	nwam_ncu_free(ncuh);
1160	if (err != NWAM_SUCCESS) {
1161		nlog(LOG_ERR,
1162		    "failed to create ip ncu for %s: %s", name,
1163		    nwam_strerror(err));
1164	}
1165}
1166
1167static void
1168remove_ncu_from_ncp(nwam_ncp_handle_t ncph, const char *name,
1169    nwam_ncu_type_t type)
1170{
1171	nwam_error_t err;
1172	nwam_ncu_handle_t ncuh;
1173
1174	if ((err = nwam_ncu_read(ncph, name, type, 0, &ncuh)) != NWAM_SUCCESS) {
1175		nlog(LOG_ERR, "failed to read automatic ncu %s: %s", name,
1176		    nwam_strerror(err));
1177		return;
1178	}
1179
1180	err = nwam_ncu_destroy(ncuh, 0);
1181	if (err != NWAM_SUCCESS) {
1182		nlog(LOG_ERR, "failed to delete automatic ncu %s: %s", name,
1183		    nwam_strerror(err));
1184	}
1185}
1186
1187/*
1188 * Device represented by NCU has been added or removed for the active
1189 * User NCP.  If an associated NCU of the given type is found, transition it
1190 * to the appropriate state.
1191 */
1192void
1193ncu_action_change_state(nwam_action_t action, nwam_ncu_type_t type,
1194    const char *name)
1195{
1196	nwamd_object_t ncu_obj = NULL;
1197	nwamd_ncu_t *ncu;
1198
1199	if ((ncu_obj = nwamd_ncu_object_find(type, name)) == NULL)
1200		return;
1201
1202	ncu = ncu_obj->nwamd_object_data;
1203
1204	/*
1205	 * If device has been added, transition from uninitialized to offline.
1206	 * If device has been removed, transition to uninitialized (via online*
1207	 * if the NCU is currently enabled in order to tear down config).
1208	 */
1209	if (action == NWAM_ACTION_ADD) {
1210		nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1211		    ncu_obj->nwamd_object_name,
1212		    NWAM_STATE_OFFLINE, NWAM_AUX_STATE_CONDITIONS_NOT_MET);
1213	} else {
1214		if (ncu->ncu_enabled) {
1215			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1216			    ncu_obj->nwamd_object_name,
1217			    NWAM_STATE_ONLINE_TO_OFFLINE,
1218			    NWAM_AUX_STATE_NOT_FOUND);
1219		} else {
1220			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1221			    ncu_obj->nwamd_object_name,
1222			    NWAM_STATE_UNINITIALIZED,
1223			    NWAM_AUX_STATE_NOT_FOUND);
1224		}
1225	}
1226	nwamd_object_release(ncu_obj);
1227}
1228
1229/*
1230 * Called with hotplug sysevent or when nwam is started and walking the
1231 * physical interfaces.  Add/remove both link and interface NCUs from the
1232 * Automatic NCP.  Assumes that both link and interface NCUs don't exist.
1233 */
1234void
1235nwamd_ncu_handle_link_action_event(nwamd_event_t event)
1236{
1237	nwam_ncp_handle_t ncph;
1238	nwam_ncu_type_t type;
1239	nwam_action_t action =
1240	    event->event_msg->nwe_data.nwe_link_action.nwe_action;
1241	nwam_error_t err;
1242	char *name;
1243	boolean_t automatic_ncp_active = B_FALSE;
1244
1245	if (action != NWAM_ACTION_ADD && action != NWAM_ACTION_REMOVE) {
1246		nlog(LOG_ERR, "nwamd_ncu_handle_link_action_event: "
1247		    "invalid link action %s", nwam_action_to_string(action));
1248		nwamd_event_do_not_send(event);
1249		return;
1250	}
1251
1252	nlog(LOG_DEBUG, "nwamd_ncu_handle_link_action_event: "
1253	    "link action '%s' event on %s", nwam_action_to_string(action),
1254	    event->event_object[0] == 0 ? "n/a" : event->event_object);
1255
1256	if ((err = nwam_ncu_typed_name_to_name(event->event_object, &type,
1257	    &name)) != NWAM_SUCCESS) {
1258		nlog(LOG_ERR, "nwamd_ncu_handle_link_action_event: "
1259		    "translation from typedname error: %s", nwam_strerror(err));
1260		nwamd_event_do_not_send(event);
1261		return;
1262	}
1263
1264	(void) pthread_mutex_lock(&active_ncp_mutex);
1265	if (strcmp(active_ncp, NWAM_NCP_NAME_AUTOMATIC) == 0 &&
1266	    active_ncph != NULL) {
1267		automatic_ncp_active = B_TRUE;
1268	}
1269	(void) pthread_mutex_unlock(&active_ncp_mutex);
1270
1271	/*
1272	 * We could use active_ncph for cases where the Automatic NCP is active,
1273	 * but that would involve holding the active_ncp_mutex for too long.
1274	 */
1275	if ((err = nwam_ncp_read(NWAM_NCP_NAME_AUTOMATIC, 0, &ncph))
1276	    == NWAM_ENTITY_NOT_FOUND) {
1277		/* Automatic NCP doesn't exist, create it */
1278		if ((err = nwam_ncp_create(NWAM_NCP_NAME_AUTOMATIC, 0,
1279		    &ncph)) != NWAM_SUCCESS) {
1280			nlog(LOG_ERR,
1281			    "nwamd_ncu_handle_link_action_event: "
1282			    "could not create %s NCP: %s",
1283			    NWAM_NCP_NAME_AUTOMATIC,
1284			    nwam_strerror(err));
1285			goto cleanup_exit;
1286		}
1287	} else if (err != NWAM_SUCCESS) {
1288		nlog(LOG_ERR, "nwamd_ncu_handle_link_action_event: "
1289		    "failed to read Automatic NCP: %s",
1290		    nwam_strerror(err));
1291		goto cleanup_exit;
1292	}
1293
1294	/* add or remove NCUs from Automatic NCP */
1295	if (action == NWAM_ACTION_ADD) {
1296		add_phys_ncu_to_ncp(ncph, name);
1297		add_ip_ncu_to_ncp(ncph, name);
1298	} else {
1299		/*
1300		 * Order is important here, remove IP NCU first to prevent
1301		 * propogation of down event from link to IP.  No need to
1302		 * create REFRESH or DESTROY events.  They are generated by
1303		 * nwam_ncu_commit() and nwam_ncu_destroy().
1304		 */
1305		remove_ncu_from_ncp(ncph, name, NWAM_NCU_TYPE_INTERFACE);
1306		remove_ncu_from_ncp(ncph, name, NWAM_NCU_TYPE_LINK);
1307	}
1308	nwam_ncp_free(ncph);
1309
1310	/*
1311	 * If the Automatic NCP is not active, and the associated NCUs
1312	 * exist, they must be moved into the appropriate states given the
1313	 * action that has occurred.
1314	 */
1315	if (!automatic_ncp_active) {
1316		ncu_action_change_state(action, NWAM_NCU_TYPE_INTERFACE, name);
1317		ncu_action_change_state(action, NWAM_NCU_TYPE_LINK, name);
1318	}
1319
1320	/* Need NCU check to evaluate state in light of added/removed NCUs */
1321	if (!nwamd_event_enqueued(NWAM_EVENT_TYPE_NCU_CHECK,
1322	    NWAM_OBJECT_TYPE_NCP, NULL)) {
1323		nwamd_create_ncu_check_event(NEXT_FEW_SECONDS);
1324	}
1325
1326cleanup_exit:
1327	free(name);
1328}
1329
1330/*
1331 * Figure out if this link is part of an aggregation.  This is fairly
1332 * inefficient since we generate this list for every query and search
1333 * linearly.  A better way would be to generate the list of links in an
1334 * aggregation once and then check each link against it.
1335 */
1336struct link_aggr_search_data {
1337	datalink_id_t linkid;
1338	boolean_t under;
1339};
1340
1341static int
1342ncu_aggr_search(const char *name, void *data)
1343{
1344	struct link_aggr_search_data *lasd = data;
1345	dladm_aggr_grp_attr_t ginfo;
1346	datalink_id_t linkid;
1347	int i;
1348
1349	if (dladm_name2info(dld_handle, name, &linkid, NULL, NULL, NULL) !=
1350	    DLADM_STATUS_OK)
1351		return (DLADM_WALK_CONTINUE);
1352	if (dladm_aggr_info(dld_handle, linkid, &ginfo, DLADM_OPT_ACTIVE)
1353	    != DLADM_STATUS_OK || ginfo.lg_nports == 0)
1354		return (DLADM_WALK_CONTINUE);
1355
1356	for (i = 0; i < ginfo.lg_nports; i++) {
1357		if (lasd->linkid == ginfo.lg_ports[i].lp_linkid) {
1358			lasd->under = B_TRUE;
1359			return (DLADM_WALK_TERMINATE);
1360		}
1361	}
1362	free(ginfo.lg_ports);
1363	return (DLADM_WALK_CONTINUE);
1364}
1365
1366static boolean_t
1367nwamd_link_belongs_to_an_aggr(const char *name)
1368{
1369	struct link_aggr_search_data lasd;
1370
1371	if (dladm_name2info(dld_handle, name, &lasd.linkid, NULL, NULL, NULL)
1372	    != DLADM_STATUS_OK)
1373		return (B_FALSE);
1374	lasd.under = B_FALSE;
1375	(void) dladm_walk(ncu_aggr_search, dld_handle, &lasd,
1376	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
1377	return (lasd.under);
1378}
1379
1380/*
1381 * If NCU doesn't exist for interface with given name, enqueue a ADD
1382 * LINK_ACTION event.
1383 */
1384static int
1385ncu_create_link_action_event(const char *name, void *data)
1386{
1387	nwam_ncp_handle_t ncph = data;
1388	nwam_ncu_handle_t ncuh;
1389	nwamd_event_t link_event;
1390
1391	/* Do not generate an event if this is a VirtualBox interface. */
1392	if (strncmp(name, VBOX_IFACE_PREFIX, strlen(VBOX_IFACE_PREFIX)) == 0)
1393		return (DLADM_WALK_CONTINUE);
1394
1395	/* Do not generate an event if this link belongs to another zone. */
1396	if (!nwamd_link_belongs_to_this_zone(name))
1397		return (DLADM_WALK_CONTINUE);
1398
1399	/* Do not generate an event if this link belongs to an aggregation. */
1400	if (nwamd_link_belongs_to_an_aggr(name)) {
1401		return (DLADM_WALK_CONTINUE);
1402	}
1403
1404	/* Don't create an event if the NCU already exists. */
1405	if (ncph != NULL && nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_LINK, 0,
1406	    &ncuh) == NWAM_SUCCESS) {
1407		nwam_ncu_free(ncuh);
1408		return (DLADM_WALK_CONTINUE);
1409	}
1410
1411	nlog(LOG_DEBUG, "ncu_create_link_action_event: adding ncus for %s",
1412	    name);
1413
1414	link_event = nwamd_event_init_link_action(name, NWAM_ACTION_ADD);
1415	if (link_event != NULL)
1416		nwamd_event_enqueue(link_event);
1417
1418	return (DLADM_WALK_CONTINUE);
1419}
1420
1421/*
1422 * Check if interface exists for this NCU. If not, enqueue a REMOVE
1423 * LINK_ACTION event.
1424 */
1425/* ARGSUSED */
1426static int
1427nwamd_destroy_ncu(nwam_ncu_handle_t ncuh, void *data)
1428{
1429	char *name;
1430	uint32_t flags;
1431	nwamd_event_t link_event;
1432
1433	if (nwam_ncu_get_name(ncuh, &name) != NWAM_SUCCESS) {
1434		nlog(LOG_ERR, "nwamd_destroy_ncu: could not get NCU name");
1435		return (0);
1436	}
1437
1438	/* Interfaces that exist return DLADM_OPT_ACTIVE flag */
1439	if ((dladm_name2info(dld_handle, name, NULL, &flags, NULL, NULL)
1440	    == DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) &&
1441	    !nwamd_link_belongs_to_an_aggr(name)) {
1442		free(name);
1443		return (0);
1444	}
1445
1446	nlog(LOG_DEBUG, "nwamd_destroy_ncu: destroying ncus for %s", name);
1447
1448	link_event = nwamd_event_init_link_action(name, NWAM_ACTION_REMOVE);
1449	if (link_event != NULL)
1450		nwamd_event_enqueue(link_event);
1451	free(name);
1452	return (0);
1453}
1454
1455/*
1456 * Called when nwamd is starting up.
1457 *
1458 * Walk all NCUs and destroy any NCU from the Automatic NCP without an
1459 * underlying interface (assumption here is that the interface was removed
1460 * when nwam was disabled).
1461 *
1462 * Walk the physical interfaces and create ADD LINK_ACTION event, which
1463 * will create appropriate interface and link NCUs in the Automatic NCP.
1464 */
1465void
1466nwamd_walk_physical_configuration(void)
1467{
1468	nwam_ncp_handle_t ncph;
1469
1470	(void) pthread_mutex_lock(&active_ncp_mutex);
1471	if (strcmp(active_ncp, NWAM_NCP_NAME_AUTOMATIC) == 0 &&
1472	    active_ncph != NULL) {
1473		ncph = active_ncph;
1474	} else {
1475		if (nwam_ncp_read(NWAM_NCP_NAME_AUTOMATIC, 0, &ncph)
1476		    != NWAM_SUCCESS) {
1477			ncph = NULL;
1478		}
1479	}
1480
1481	/* destroy NCUs for interfaces that don't exist */
1482	if (ncph != NULL) {
1483		(void) nwam_ncp_walk_ncus(ncph, nwamd_destroy_ncu, NULL,
1484		    NWAM_FLAG_NCU_TYPE_LINK, NULL);
1485	}
1486
1487	/* create NCUs for interfaces without NCUs */
1488	(void) dladm_walk(ncu_create_link_action_event, dld_handle, ncph,
1489	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
1490
1491	if (strcmp(active_ncp, NWAM_NCP_NAME_AUTOMATIC) != 0 ||
1492	    active_ncph == NULL) {
1493		nwam_ncp_free(ncph);
1494	}
1495	(void) pthread_mutex_unlock(&active_ncp_mutex);
1496}
1497
1498/*
1499 * Handle NCU initialization/refresh event.
1500 */
1501void
1502nwamd_ncu_handle_init_event(nwamd_event_t event)
1503{
1504	nwamd_object_t object = NULL;
1505	nwam_ncu_handle_t ncuh;
1506	nwamd_ncu_t *ncu = NULL;
1507	nwam_error_t err;
1508	nwam_ncu_type_t type;
1509	char *name;
1510	uint32_t flags;
1511	boolean_t new = B_TRUE;
1512
1513	nlog(LOG_DEBUG, "nwamd_ncu_handle_init_event(%s)",
1514	    event->event_object);
1515
1516	/* Get base linkname rather than interface:linkname or link:linkname */
1517	err = nwam_ncu_typed_name_to_name(event->event_object,
1518	    &type, &name);
1519	if (err != NWAM_SUCCESS) {
1520		nlog(LOG_ERR, "nwamd_ncu_handle_init_event: "
1521		    "nwam_ncu_typed_name_to_name returned %s",
1522		    nwam_strerror(err));
1523		nwamd_event_do_not_send(event);
1524		return;
1525	}
1526
1527	(void) pthread_mutex_lock(&active_ncp_mutex);
1528	if (active_ncph == NULL) {
1529		nlog(LOG_DEBUG,
1530		    "nwamd_ncu_handle_init_event: active NCP handle NULL");
1531		nwamd_event_do_not_send(event);
1532		(void) pthread_mutex_unlock(&active_ncp_mutex);
1533		return;
1534	}
1535	err = nwam_ncu_read(active_ncph, event->event_object,
1536	    type, 0, &ncuh);
1537	(void) pthread_mutex_unlock(&active_ncp_mutex);
1538	if (err != NWAM_SUCCESS) {
1539		nlog(LOG_ERR, "nwamd_ncu_handle_init_event: "
1540		    "could not read object '%s': %s",
1541		    event->event_object, nwam_strerror(err));
1542		free(name);
1543		nwamd_event_do_not_send(event);
1544		return;
1545	}
1546
1547	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
1548	    event->event_object)) != NULL)
1549		new = B_FALSE;
1550
1551	/*
1552	 * For new NCUs, or interface NCUs, we (re)initialize data from scratch.
1553	 * For link NCUs, we want to retain object data.
1554	 */
1555	switch (type) {
1556	case NWAM_NCU_TYPE_LINK:
1557		if (new) {
1558			ncu = nwamd_ncu_init(type, name);
1559		} else {
1560			ncu = object->nwamd_object_data;
1561			nwam_ncu_free(object->nwamd_object_handle);
1562		}
1563		populate_common_ncu_properties(ncuh, ncu);
1564		populate_link_ncu_properties(ncuh, ncu);
1565		break;
1566	case NWAM_NCU_TYPE_INTERFACE:
1567		if (!new) {
1568			nwam_ncu_free(object->nwamd_object_handle);
1569			nwamd_ncu_free(object->nwamd_object_data);
1570		}
1571		ncu = nwamd_ncu_init(type, name);
1572		populate_common_ncu_properties(ncuh, ncu);
1573		populate_ip_ncu_properties(ncuh, ncu);
1574		break;
1575	default:
1576		nlog(LOG_ERR, "unknown ncu type %d", type);
1577		free(name);
1578		nwam_ncu_free(ncuh);
1579		nwamd_event_do_not_send(event);
1580		nwamd_object_release(object);
1581		return;
1582	}
1583
1584	if (new) {
1585		nlog(LOG_DEBUG, "nwamd_ncu_handle_init_event: didn't find "
1586		    "ncu so create it %s", name);
1587		object = nwamd_object_init(NWAM_OBJECT_TYPE_NCU,
1588		    event->event_object, ncuh, ncu);
1589	} else {
1590		nlog(LOG_DEBUG, "nwamd_ncu_handle_init_event: refreshing "
1591		    "ncu %s", name);
1592		object->nwamd_object_data = ncu;
1593		object->nwamd_object_handle = ncuh;
1594	}
1595
1596	/*
1597	 * If the physical link for this NCU doesn't exist in the system,
1598	 * the state should be UNINITIALIZED/NOT_FOUND.  Interfaces that
1599	 * exist return DLADM_OPT_ACTIVE flag.
1600	 */
1601	if (dladm_name2info(dld_handle, name, NULL, &flags, NULL, NULL)
1602	    != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE)) {
1603		nlog(LOG_DEBUG, "nwam_ncu_handle_init_event: "
1604		    "interface for NCU %s doesn't exist",
1605		    event->event_object);
1606		nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1607		    object->nwamd_object_name, NWAM_STATE_UNINITIALIZED,
1608		    NWAM_AUX_STATE_NOT_FOUND);
1609		free(name);
1610		nwamd_object_release(object);
1611		return;
1612	}
1613
1614	/*
1615	 * If NCU is being initialized (rather than refreshed), the
1616	 * object_state is INITIALIZED (from nwamd_object_init()).
1617	 */
1618	if (object->nwamd_object_state == NWAM_STATE_INITIALIZED) {
1619		/*
1620		 * If the NCU is disabled, initial state should be DISABLED.
1621		 *
1622		 * Otherwise, the initial state will be
1623		 * OFFLINE/CONDITIONS_NOT_MET, and the link selection
1624		 * algorithm will do the rest.
1625		 */
1626		if (!ncu->ncu_enabled) {
1627			object->nwamd_object_state = NWAM_STATE_DISABLED;
1628			object->nwamd_object_aux_state =
1629			    NWAM_AUX_STATE_MANUAL_DISABLE;
1630		} else {
1631			object->nwamd_object_state = NWAM_STATE_OFFLINE;
1632			object->nwamd_object_aux_state =
1633			    NWAM_AUX_STATE_CONDITIONS_NOT_MET;
1634		}
1635	} else {
1636		nwamd_link_t *link = &ncu->ncu_node.u_link;
1637
1638		/*
1639		 * Refresh NCU.  Deal with disabled cases first, moving NCUs
1640		 * that are not disabled - but have the enabled value set - to
1641		 * the disabled state.  Then handle cases where the NCU was
1642		 * disabled but is no longer.  Finally,  deal with refresh of
1643		 * link and interface NCUs, as these are handled differently.
1644		 */
1645		if (!ncu->ncu_enabled) {
1646			if (object->nwamd_object_state != NWAM_STATE_DISABLED) {
1647				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1648				    object->nwamd_object_name,
1649				    NWAM_STATE_ONLINE_TO_OFFLINE,
1650				    NWAM_AUX_STATE_MANUAL_DISABLE);
1651			}
1652			goto done;
1653		} else {
1654			if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
1655				int64_t c;
1656
1657				/*
1658				 * Try to activate the NCU if manual or
1659				 * prioritized (when priority <= current).
1660				 */
1661				(void) pthread_mutex_lock(&active_ncp_mutex);
1662				c = current_ncu_priority_group;
1663				(void) pthread_mutex_unlock(&active_ncp_mutex);
1664				if (link->nwamd_link_activation_mode ==
1665				    NWAM_ACTIVATION_MODE_MANUAL ||
1666				    (link->nwamd_link_activation_mode ==
1667				    NWAM_ACTIVATION_MODE_PRIORITIZED &&
1668				    link->nwamd_link_priority_mode <= c)) {
1669					nwamd_object_set_state
1670					    (NWAM_OBJECT_TYPE_NCU,
1671					    object->nwamd_object_name,
1672					    NWAM_STATE_OFFLINE_TO_ONLINE,
1673					    NWAM_AUX_STATE_INITIALIZED);
1674				} else {
1675					nwamd_object_set_state
1676					    (NWAM_OBJECT_TYPE_NCU,
1677					    object->nwamd_object_name,
1678					    NWAM_STATE_OFFLINE_TO_ONLINE,
1679					    NWAM_AUX_STATE_INITIALIZED);
1680				}
1681				goto done;
1682			}
1683		}
1684
1685		switch (type) {
1686		case NWAM_NCU_TYPE_LINK:
1687			if (ncu->ncu_node.u_link.nwamd_link_media == DL_WIFI) {
1688				/*
1689				 * Do rescan.  If the current state and the
1690				 * active priority-group do not allow wireless
1691				 * network selection, then it won't happen.
1692				 */
1693				(void) nwamd_wlan_scan(ncu->ncu_name);
1694			}
1695			break;
1696		case NWAM_NCU_TYPE_INTERFACE:
1697			/*
1698			 * If interface NCU is offline*, online or in
1699			 * maintenance, mark it down (from there, it will be
1700			 * reinitialized to reapply addresses).
1701			 */
1702			if (object->nwamd_object_state != NWAM_STATE_OFFLINE) {
1703				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1704				    object->nwamd_object_name,
1705				    NWAM_STATE_ONLINE_TO_OFFLINE,
1706				    NWAM_AUX_STATE_DOWN);
1707			} else {
1708				object->nwamd_object_state = NWAM_STATE_OFFLINE;
1709				object->nwamd_object_aux_state =
1710				    NWAM_AUX_STATE_CONDITIONS_NOT_MET;
1711			}
1712			break;
1713		}
1714	}
1715
1716done:
1717	if (type == NWAM_NCU_TYPE_LINK &&
1718	    !nwamd_event_enqueued(NWAM_EVENT_TYPE_NCU_CHECK,
1719	    NWAM_OBJECT_TYPE_NCP, NULL)) {
1720		nwamd_create_ncu_check_event(NEXT_FEW_SECONDS);
1721	}
1722	free(name);
1723	nwamd_object_release(object);
1724}
1725
1726void
1727nwamd_ncu_handle_fini_event(nwamd_event_t event)
1728{
1729	nwamd_object_t object;
1730	nwamd_event_t state_event;
1731
1732	nlog(LOG_DEBUG, "nwamd_ncu_handle_fini_event(%s)",
1733	    event->event_object);
1734
1735	/*
1736	 * Simulate a state event so that the state machine can correctly
1737	 * disable the NCU.  Then free up allocated objects.
1738	 */
1739	state_event = nwamd_event_init_object_state(NWAM_OBJECT_TYPE_NCU,
1740	    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
1741	    NWAM_AUX_STATE_UNINITIALIZED);
1742	if (state_event == NULL) {
1743		nwamd_event_do_not_send(event);
1744		return;
1745	}
1746	nwamd_ncu_handle_state_event(state_event);
1747	nwamd_event_fini(state_event);
1748
1749	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
1750	    event->event_object)) == NULL) {
1751		nlog(LOG_ERR, "nwamd_ncu_handle_fini_event: "
1752		    "ncu %s not found", event->event_object);
1753		nwamd_event_do_not_send(event);
1754		return;
1755	}
1756	nwamd_object_release_and_destroy(object);
1757}
1758
1759void
1760nwamd_ncu_handle_action_event(nwamd_event_t event)
1761{
1762	nwamd_object_t object;
1763
1764	(void) pthread_mutex_lock(&active_ncp_mutex);
1765	if (strcmp(event->event_msg->nwe_data.nwe_object_action.nwe_parent,
1766	    active_ncp) != 0) {
1767		nlog(LOG_DEBUG, "nwamd_ncu_handle_action_event: action for "
1768		    "inactive NCP %s, nothing to do",
1769		    event->event_msg->nwe_data.nwe_object_action.nwe_parent);
1770		(void) pthread_mutex_unlock(&active_ncp_mutex);
1771		return;
1772	}
1773	(void) pthread_mutex_unlock(&active_ncp_mutex);
1774
1775	switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
1776	case NWAM_ACTION_ENABLE:
1777		object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
1778		    event->event_object);
1779		if (object == NULL) {
1780			nlog(LOG_ERR, "nwamd_ncu_handle_action_event: "
1781			    "could not find ncu %s", event->event_object);
1782			nwamd_event_do_not_send(event);
1783			return;
1784		}
1785		if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
1786			nlog(LOG_DEBUG, "nwamd_ncu_handle_action_event: "
1787			    "ncu %s already online, nothing to do",
1788			    event->event_object);
1789			nwamd_object_release(object);
1790			return;
1791		}
1792		nwamd_object_release(object);
1793
1794		nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1795		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
1796		    NWAM_AUX_STATE_INITIALIZED);
1797		break;
1798	case NWAM_ACTION_DISABLE:
1799		object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
1800		    event->event_object);
1801		if (object == NULL) {
1802			nlog(LOG_ERR, "nwamd_ncu_handle_action_event: "
1803			    "could not find ncu %s", event->event_object);
1804			nwamd_event_do_not_send(event);
1805			return;
1806		}
1807		if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
1808			nlog(LOG_DEBUG, "nwamd_ncu_handle_action_event: "
1809			    "ncu %s already disabled, nothing to do",
1810			    event->event_object);
1811			nwamd_object_release(object);
1812			return;
1813		}
1814		nwamd_object_release(object);
1815
1816		nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1817		    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
1818		    NWAM_AUX_STATE_MANUAL_DISABLE);
1819		break;
1820	case NWAM_ACTION_ADD:
1821	case NWAM_ACTION_REFRESH:
1822		nwamd_ncu_handle_init_event(event);
1823		break;
1824	case NWAM_ACTION_DESTROY:
1825		nwamd_ncu_handle_fini_event(event);
1826		break;
1827	default:
1828		nlog(LOG_INFO, "nwam_ncu_handle_action_event: "
1829		    "unexpected action");
1830		nwamd_event_do_not_send(event);
1831		break;
1832	}
1833}
1834
1835void
1836nwamd_ncu_handle_state_event(nwamd_event_t event)
1837{
1838	nwamd_object_t object;
1839	nwam_state_t old_state, new_state;
1840	nwam_aux_state_t new_aux_state;
1841	nwamd_ncu_t *ncu;
1842	boolean_t is_link, enabled, prioritized = B_FALSE;
1843	char linkname[NWAM_MAX_NAME_LEN];
1844	nwam_event_t m = event->event_msg;
1845
1846	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
1847	    event->event_object)) == NULL) {
1848		nlog(LOG_ERR, "nwamd_ncu_handle_state_event %lld: "
1849		    "state event for nonexistent NCU %s", event->event_id,
1850		    event->event_object);
1851		nwamd_event_do_not_send(event);
1852		return;
1853	}
1854	ncu = object->nwamd_object_data;
1855	old_state = object->nwamd_object_state;
1856	new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
1857	new_aux_state =
1858	    event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;
1859
1860	/*
1861	 * For NCU state changes, we need to supply the parent NCP name also,
1862	 * regardless of whether the event is handled or not.  It is best to
1863	 * fill this in here as we have the object lock - when we create
1864	 * object state events we sometimes do not have the object lock, but
1865	 * at this point in consuming the events (and prior to the associated
1866	 * event message being sent out) we do.
1867	 */
1868	(void) strlcpy(m->nwe_data.nwe_object_state.nwe_parent, ncu->ncu_parent,
1869	    sizeof (m->nwe_data.nwe_object_state.nwe_parent));
1870
1871	/*
1872	 * If we receive a state change event moving this NCU to
1873	 * DHCP_TIMED_OUT or UP state but this NCU is already ONLINE, then
1874	 * ignore this state change event.
1875	 */
1876	if ((new_aux_state == NWAM_AUX_STATE_IF_DHCP_TIMED_OUT ||
1877	    new_aux_state == NWAM_AUX_STATE_UP) &&
1878	    object->nwamd_object_state == NWAM_STATE_ONLINE) {
1879		nlog(LOG_INFO, "nwamd_ncu_handle_state_event: "
1880		    "NCU %s already online, not going to '%s' state",
1881		    object->nwamd_object_name,
1882		    nwam_aux_state_to_string(new_aux_state));
1883		nwamd_event_do_not_send(event);
1884		nwamd_object_release(object);
1885		return;
1886	}
1887
1888	if (new_state == object->nwamd_object_state &&
1889	    new_aux_state == object->nwamd_object_aux_state) {
1890		nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: "
1891		    "NCU %s already in state (%s, %s)",
1892		    object->nwamd_object_name, nwam_state_to_string(new_state),
1893		    nwam_aux_state_to_string(new_aux_state));
1894		nwamd_object_release(object);
1895		return;
1896	}
1897
1898	if (old_state == NWAM_STATE_MAINTENANCE &&
1899	    (new_state == NWAM_STATE_ONLINE ||
1900	    (new_state == NWAM_STATE_OFFLINE_TO_ONLINE &&
1901	    new_aux_state != NWAM_AUX_STATE_INITIALIZED))) {
1902		nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: "
1903		    "NCU %s cannot transition from state %s to state (%s, %s)",
1904		    object->nwamd_object_name, nwam_state_to_string(old_state),
1905		    nwam_state_to_string(new_state),
1906		    nwam_aux_state_to_string(new_aux_state));
1907		nwamd_event_do_not_send(event);
1908		nwamd_object_release(object);
1909		return;
1910	}
1911
1912	object->nwamd_object_state = new_state;
1913	object->nwamd_object_aux_state = new_aux_state;
1914
1915	nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: changing state for NCU "
1916	    "%s to (%s, %s)", object->nwamd_object_name,
1917	    nwam_state_to_string(object->nwamd_object_state),
1918	    nwam_aux_state_to_string(object->nwamd_object_aux_state));
1919
1920	is_link = (ncu->ncu_type == NWAM_NCU_TYPE_LINK);
1921	if (is_link)
1922		(void) strlcpy(linkname, ncu->ncu_name, sizeof (linkname));
1923	prioritized = (ncu->ncu_type == NWAM_NCU_TYPE_LINK &&
1924	    ncu->ncu_node.u_link.nwamd_link_activation_mode ==
1925	    NWAM_ACTIVATION_MODE_PRIORITIZED);
1926	enabled = ncu->ncu_enabled;
1927
1928	nwamd_object_release(object);
1929
1930	/*
1931	 * State machine for NCUs
1932	 */
1933	switch (new_state) {
1934	case NWAM_STATE_OFFLINE_TO_ONLINE:
1935		if (enabled) {
1936			nwamd_ncu_state_machine(event->event_object);
1937		} else {
1938			nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: "
1939			    "cannot move disabled NCU %s online",
1940			    event->event_object);
1941			nwamd_event_do_not_send(event);
1942		}
1943		break;
1944
1945	case NWAM_STATE_ONLINE_TO_OFFLINE:
1946		nwamd_ncu_state_machine(event->event_object);
1947		break;
1948
1949	case NWAM_STATE_ONLINE:
1950		/*
1951		 * We usually don't need to do anything when we're in the
1952		 * ONLINE state.  However, for  WiFi we can be in INIT or
1953		 * SCAN aux states while being ONLINE.
1954		 */
1955		nwamd_ncu_state_machine(event->event_object);
1956		break;
1957
1958	case NWAM_STATE_OFFLINE:
1959		/* Reassess priority group now member is offline */
1960		if (prioritized) {
1961			nwamd_create_ncu_check_event(0);
1962		}
1963		break;
1964
1965	case NWAM_STATE_DISABLED:
1966	case NWAM_STATE_UNINITIALIZED:
1967	case NWAM_STATE_MAINTENANCE:
1968	case NWAM_STATE_DEGRADED:
1969	default:
1970		/* do nothing */
1971		break;
1972	}
1973
1974	if (is_link) {
1975		if ((new_state == NWAM_STATE_ONLINE_TO_OFFLINE &&
1976		    new_aux_state != NWAM_AUX_STATE_UNINITIALIZED &&
1977		    new_aux_state != NWAM_AUX_STATE_NOT_FOUND) ||
1978		    new_state == NWAM_STATE_DISABLED) {
1979			/*
1980			 * Going offline, propogate down event to IP NCU.  Do
1981			 * not propogate event if new aux state is uninitialized
1982			 * or not found as these auxiliary states signify
1983			 * that an NCP switch/device removal is in progress.
1984			 */
1985			nwamd_propogate_link_up_down_to_ip(linkname, B_FALSE);
1986		}
1987		if (new_state == NWAM_STATE_ONLINE) {
1988			/* gone online, propogate up event to IP NCU */
1989			nwamd_propogate_link_up_down_to_ip(linkname, B_TRUE);
1990		}
1991	} else {
1992		/* If IP NCU is online, reasses priority group */
1993		if (new_state == NWAM_STATE_ONLINE)
1994			nwamd_create_ncu_check_event(0);
1995	}
1996}
1997