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