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