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
nwamd_ncu_object_find(nwam_ncu_type_t type,const char * name)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
nwamd_set_ncu_string(nwam_ncu_handle_t ncuh,char ** strval,uint_t cnt,const char * prop)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
nwamd_set_ncu_uint(nwam_ncu_handle_t ncuh,uint64_t * uintval,uint_t cnt,const char * prop)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
nwamd_get_ncu_string(nwam_ncu_handle_t ncuh,nwam_value_t * val,char *** strval,uint_t * cnt,const char * prop)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
nwamd_get_ncu_uint(nwam_ncu_handle_t ncuh,nwam_value_t * val,uint64_t ** uintval,uint_t * cnt,const char * prop)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
nwamd_ncu_state_machine(const char * object_name)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
ncu_create_init_fini_event(nwam_ncu_handle_t ncuh,void * data)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
nwamd_init_ncus(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
nwamd_fini_ncus(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
populate_common_ncu_properties(nwam_ncu_handle_t ncuh,nwamd_ncu_t * ncu_data)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
populate_link_ncu_properties(nwam_ncu_handle_t ncuh,nwamd_ncu_t * ncu_data)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
populate_ip_ncu_properties(nwam_ncu_handle_t ncuh,nwamd_ncu_t * ncu_data)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 *
nwamd_ncu_init(nwam_ncu_type_t ncu_type,const char * name)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
nwamd_ncu_free(nwamd_ncu_t * ncu)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
nwamd_ncu_display(nwamd_object_t ncu_obj,void * data)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
nwamd_log_ncus(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
nwamd_ncu_action(const char * ncu,const char * parent,nwam_action_t action)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
add_phys_ncu_to_ncp(nwam_ncp_handle_t ncph,const char * name)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
add_ip_ncu_to_ncp(nwam_ncp_handle_t ncph,const char * name)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
remove_ncu_from_ncp(nwam_ncp_handle_t ncph,const char * name,nwam_ncu_type_t type)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
ncu_action_change_state(nwam_action_t action,nwam_ncu_type_t type,const char * name)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
nwamd_ncu_handle_link_action_event(nwamd_event_t event)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
ncu_aggr_search(const char * name,void * data)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
nwamd_link_belongs_to_an_aggr(const char * name)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
ncu_create_link_action_event(const char * name,void * data)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
nwamd_destroy_ncu(nwam_ncu_handle_t ncuh,void * data)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
nwamd_walk_physical_configuration(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
nwamd_ncu_handle_init_event(nwamd_event_t event)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
nwamd_ncu_handle_fini_event(nwamd_event_t event)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
nwamd_ncu_handle_action_event(nwamd_event_t event)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
nwamd_ncu_handle_state_event(nwamd_event_t event)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