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