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