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