#include <arpa/inet.h>
#include <assert.h>
#include <libdlaggr.h>
#include <libdllink.h>
#include <libdlstat.h>
#include <libnwam.h>
#include <libscf.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <values.h>
#include <zone.h>
#include "conditions.h"
#include "events.h"
#include "objects.h"
#include "ncp.h"
#include "util.h"
#define VBOX_IFACE_PREFIX "vboxnet"
static void populate_ip_ncu_properties(nwam_ncu_handle_t, nwamd_ncu_t *);
nwamd_object_t
nwamd_ncu_object_find(nwam_ncu_type_t type, const char *name)
{
nwam_error_t err;
char *object_name;
nwamd_object_t ncu_obj = NULL;
if ((err = nwam_ncu_name_to_typed_name(name, type, &object_name))
!= NWAM_SUCCESS) {
nlog(LOG_ERR, "nwamd_ncu_find: nwam_ncu_name_to_typed_name "
"returned %s", nwam_strerror(err));
return (NULL);
}
ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, object_name);
free(object_name);
return (ncu_obj);
}
nwam_error_t
nwamd_set_ncu_string(nwam_ncu_handle_t ncuh, char **strval, uint_t cnt,
const char *prop)
{
nwam_error_t err;
nwam_value_t val;
if ((err = nwam_value_create_string_array(strval, cnt, &val))
!= NWAM_SUCCESS)
return (err);
err = nwam_ncu_set_prop_value(ncuh, prop, val);
nwam_value_free(val);
return (err);
}
nwam_error_t
nwamd_set_ncu_uint(nwam_ncu_handle_t ncuh, uint64_t *uintval, uint_t cnt,
const char *prop)
{
nwam_error_t err;
nwam_value_t val;
if ((err = nwam_value_create_uint64_array(uintval, cnt, &val))
!= NWAM_SUCCESS)
return (err);
err = nwam_ncu_set_prop_value(ncuh, prop, val);
nwam_value_free(val);
return (err);
}
nwam_error_t
nwamd_get_ncu_string(nwam_ncu_handle_t ncuh, nwam_value_t *val, char ***strval,
uint_t *cnt, const char *prop)
{
nwam_error_t err;
if ((err = nwam_ncu_get_prop_value(ncuh, prop, val)) != NWAM_SUCCESS)
return (err);
return (nwam_value_get_string_array(*val, strval, cnt));
}
nwam_error_t
nwamd_get_ncu_uint(nwam_ncu_handle_t ncuh, nwam_value_t *val,
uint64_t **uintval, uint_t *cnt, const char *prop)
{
nwam_error_t err;
if ((err = nwam_ncu_get_prop_value(ncuh, prop, val)) != NWAM_SUCCESS)
return (err);
return (nwam_value_get_uint64_array(*val, uintval, cnt));
}
nwam_error_t
nwamd_get_ncu_boolean(nwam_ncu_handle_t ncuh, nwam_value_t *val,
boolean_t **boolval, uint_t *cnt, const char *prop)
{
nwam_error_t err;
if ((err = nwam_ncu_get_prop_value(ncuh, prop, val)) != NWAM_SUCCESS)
return (err);
return (nwam_value_get_boolean_array(*val, boolval, cnt));
}
static void
nwamd_ncu_state_machine(const char *object_name)
{
nwamd_object_t object;
nwamd_ncu_t *ncu;
link_state_t link_state;
nwamd_event_t event;
nwam_wlan_t key_wlan, connected_wlan;
nwamd_link_t *link;
char linkname[NWAM_MAX_NAME_LEN];
boolean_t up;
if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, object_name))
== NULL) {
nlog(LOG_ERR, "nwamd_ncu_state_machine: "
"request for nonexistent NCU %s", object_name);
return;
}
ncu = object->nwamd_object_data;
link = &ncu->ncu_link;
switch (object->nwamd_object_aux_state) {
case NWAM_AUX_STATE_INITIALIZED:
if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
nwamd_dlpi_add_link(object);
if (link->nwamd_link_media == DL_WIFI) {
if (!link->nwamd_link_wifi_connected &&
nwamd_wlan_connected(object)) {
nlog(LOG_DEBUG,
"nwamd_ncu_state_machine: "
"WiFi unexpectedly connected, "
"disconnecting...");
(void) dladm_wlan_disconnect(dld_handle,
link->nwamd_link_id);
nwamd_set_selected_connected(ncu,
B_FALSE, B_FALSE);
}
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
object_name, object->nwamd_object_state,
NWAM_AUX_STATE_LINK_WIFI_SCANNING);
} else {
link_state = nwamd_get_link_state
(ncu->ncu_name);
if (link_state == LINK_STATE_UP ||
link_state == LINK_STATE_UNKNOWN) {
nwamd_object_set_state
(NWAM_OBJECT_TYPE_NCU,
object_name, NWAM_STATE_ONLINE,
NWAM_AUX_STATE_UP);
} else {
nwamd_object_set_state
(NWAM_OBJECT_TYPE_NCU,
object_name,
NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_DOWN);
}
}
} else {
nwamd_unplumb_interface(ncu, AF_INET);
nwamd_unplumb_interface(ncu, AF_INET6);
populate_ip_ncu_properties(object->nwamd_object_handle,
ncu);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
object_name, NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_IF_WAITING_FOR_ADDR);
if (ncu->ncu_if.nwamd_if_ipv4)
nwamd_plumb_interface(ncu, AF_INET);
if (ncu->ncu_if.nwamd_if_ipv6)
nwamd_plumb_interface(ncu, AF_INET6);
nwamd_configure_interface_addresses(ncu);
}
break;
case NWAM_AUX_STATE_IF_DHCP_TIMED_OUT:
case NWAM_AUX_STATE_IF_WAITING_FOR_ADDR:
break;
case NWAM_AUX_STATE_LINK_WIFI_SCANNING:
(void) strlcpy(linkname, ncu->ncu_name, sizeof (linkname));
(void) nwamd_wlan_scan(linkname);
nwamd_ncu_create_periodic_scan_event(object);
break;
case NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION:
event = nwamd_event_init_wlan
(ncu->ncu_name, NWAM_EVENT_TYPE_WLAN_NEED_CHOICE, B_FALSE,
link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr,
link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr_num);
if (event == NULL)
break;
nwamd_event_enqueue(event);
nwamd_set_selected_connected(ncu, B_FALSE, B_FALSE);
break;
case NWAM_AUX_STATE_LINK_WIFI_NEED_KEY:
bzero(&key_wlan, sizeof (key_wlan));
(void) strlcpy(key_wlan.nww_essid, link->nwamd_link_wifi_essid,
sizeof (key_wlan.nww_essid));
(void) strlcpy(key_wlan.nww_bssid, link->nwamd_link_wifi_bssid,
sizeof (key_wlan.nww_bssid));
key_wlan.nww_security_mode =
link->nwamd_link_wifi_security_mode;
key_wlan.nww_selected = B_TRUE;
key_wlan.nww_connected = B_FALSE;
key_wlan.nww_have_key = B_FALSE;
event = nwamd_event_init_wlan
(ncu->ncu_name, NWAM_EVENT_TYPE_WLAN_NEED_KEY, B_FALSE,
&key_wlan, 1);
if (event == NULL)
break;
nwamd_event_enqueue(event);
break;
case NWAM_AUX_STATE_LINK_WIFI_CONNECTING:
(void) strlcpy(linkname, ncu->ncu_name, sizeof (linkname));
nwamd_wlan_connect(linkname);
break;
case NWAM_AUX_STATE_UP:
case NWAM_AUX_STATE_DOWN:
up = (object->nwamd_object_aux_state == NWAM_AUX_STATE_UP);
if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
if (link->nwamd_link_media == DL_WIFI) {
link->nwamd_link_wifi_connected = up;
nwamd_set_selected_connected(ncu, B_TRUE, up);
(void) strlcpy(connected_wlan.nww_essid,
link->nwamd_link_wifi_essid,
sizeof (connected_wlan.nww_essid));
(void) strlcpy(connected_wlan.nww_bssid,
link->nwamd_link_wifi_bssid,
sizeof (connected_wlan.nww_bssid));
connected_wlan.nww_security_mode =
link->nwamd_link_wifi_security_mode;
event = nwamd_event_init_wlan
(ncu->ncu_name,
NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT, up,
&connected_wlan, 1);
if (event == NULL)
break;
nwamd_event_enqueue(event);
if (!up && ncu->ncu_enabled) {
nlog(LOG_DEBUG,
"nwamd_ncu_state_machine: "
"wifi disconnect - start over "
"after %dsec interval",
WIRELESS_RETRY_INTERVAL);
link->nwamd_link_wifi_connected =
B_FALSE;
nwamd_propogate_link_up_down_to_ip
(ncu->ncu_name, B_FALSE);
nwamd_object_set_state_timed
(NWAM_OBJECT_TYPE_NCU, object_name,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_INITIALIZED,
WIRELESS_RETRY_INTERVAL);
} else {
nlog(LOG_DEBUG,
"nwamd_ncu_state_machine: "
"wifi connected, start monitoring");
(void) strlcpy(linkname, ncu->ncu_name,
sizeof (linkname));
nwamd_wlan_monitor_signal(linkname);
}
}
}
if ((up && object->nwamd_object_state != NWAM_STATE_ONLINE) ||
(!up && object->nwamd_object_state != NWAM_STATE_OFFLINE)) {
nlog(LOG_DEBUG, "nwamd_ncu_state_machine: "
"%s is moving %s", object_name,
up ? "online" : "offline");
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
object_name,
up ? NWAM_STATE_ONLINE : NWAM_STATE_OFFLINE,
up ? NWAM_AUX_STATE_UP : NWAM_AUX_STATE_DOWN);
if (ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE) {
if (up) {
nwamd_add_default_routes(ncu);
} else {
nwamd_object_set_state
(NWAM_OBJECT_TYPE_NCU, object_name,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_INITIALIZED);
}
}
} else {
nlog(LOG_DEBUG, "nwamd_ncu_state_machine: "
"%s is %s", object_name,
up ? "online" : "offline");
}
nwamd_create_triggered_condition_check_event(NEXT_FEW_SECONDS);
break;
case NWAM_AUX_STATE_CONDITIONS_NOT_MET:
if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
if (link->nwamd_link_media == DL_WIFI) {
(void) dladm_wlan_disconnect(dld_handle,
link->nwamd_link_id);
link->nwamd_link_wifi_connected = B_FALSE;
nwamd_set_selected_connected(ncu, B_FALSE,
B_FALSE);
}
} else {
nwamd_unplumb_interface(ncu, AF_INET);
nwamd_unplumb_interface(ncu, AF_INET6);
}
if (object->nwamd_object_state != NWAM_STATE_OFFLINE) {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
object_name, NWAM_STATE_OFFLINE,
NWAM_AUX_STATE_CONDITIONS_NOT_MET);
}
break;
case NWAM_AUX_STATE_MANUAL_DISABLE:
ncu->ncu_enabled = B_FALSE;
case NWAM_AUX_STATE_UNINITIALIZED:
case NWAM_AUX_STATE_NOT_FOUND:
if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
if (link->nwamd_link_media == DL_WIFI) {
(void) dladm_wlan_disconnect(dld_handle,
link->nwamd_link_id);
link->nwamd_link_wifi_connected = B_FALSE;
nwamd_set_selected_connected(ncu, B_FALSE,
B_FALSE);
}
nwamd_dlpi_delete_link(object);
} else {
if (ncu->ncu_if.nwamd_if_ipv4) {
nwamd_unplumb_interface(ncu, AF_INET);
}
if (ncu->ncu_if.nwamd_if_ipv6) {
nwamd_unplumb_interface(ncu, AF_INET6);
}
nwamd_create_triggered_condition_check_event(0);
}
switch (object->nwamd_object_aux_state) {
case NWAM_AUX_STATE_MANUAL_DISABLE:
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
object_name, NWAM_STATE_DISABLED,
NWAM_AUX_STATE_MANUAL_DISABLE);
ncu->ncu_enabled = B_FALSE;
break;
case NWAM_AUX_STATE_NOT_FOUND:
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
object_name, NWAM_STATE_UNINITIALIZED,
NWAM_AUX_STATE_NOT_FOUND);
break;
default:
break;
}
break;
default:
nlog(LOG_ERR, "nwamd_ncu_state_machine: unexpected state");
break;
}
nwamd_object_release(object);
}
static int
ncu_create_init_fini_event(nwam_ncu_handle_t ncuh, void *data)
{
boolean_t *init = data;
char *name, *typedname;
nwam_error_t err;
nwam_value_t typeval = NULL;
uint64_t *type;
uint_t numvalues;
nwamd_event_t ncu_event;
if (nwam_ncu_get_name(ncuh, &name) != NWAM_SUCCESS) {
nlog(LOG_ERR,
"ncu_create_init_fini_event: could not get NCU name");
return (0);
}
nlog(LOG_DEBUG, "ncu_create_init_fini_event(%s, %p)", name, data);
if ((err = nwamd_get_ncu_uint(ncuh, &typeval, &type, &numvalues,
NWAM_NCU_PROP_TYPE)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "ncu_create_init_fini_event: "
"could not get NCU type: %s", nwam_strerror(err));
free(name);
nwam_value_free(typeval);
return (0);
}
if ((err = nwam_ncu_name_to_typed_name(name, *type, &typedname))
!= NWAM_SUCCESS) {
nlog(LOG_ERR, "ncu_create_init_fini_event: "
"NCU name translation failed: %s", nwam_strerror(err));
free(name);
return (0);
}
free(name);
nwam_value_free(typeval);
ncu_event = nwamd_event_init(*init ?
NWAM_EVENT_TYPE_OBJECT_INIT : NWAM_EVENT_TYPE_OBJECT_FINI,
NWAM_OBJECT_TYPE_NCU, 0, typedname);
if (ncu_event != NULL)
nwamd_event_enqueue(ncu_event);
free(typedname);
return (0);
}
void
nwamd_init_ncus(void)
{
boolean_t init = B_TRUE;
(void) pthread_mutex_lock(&active_ncp_mutex);
if (active_ncph != NULL) {
nlog(LOG_DEBUG, "nwamd_init_ncus: "
"(re)intializing NCUs for NCP %s", active_ncp);
(void) nwam_ncp_walk_ncus(active_ncph,
ncu_create_init_fini_event, &init, NWAM_FLAG_NCU_TYPE_ALL,
NULL);
}
(void) pthread_mutex_unlock(&active_ncp_mutex);
}
void
nwamd_fini_ncus(void)
{
boolean_t init = B_FALSE;
(void) pthread_mutex_lock(&active_ncp_mutex);
if (active_ncph != NULL) {
nlog(LOG_DEBUG, "nwamd_fini_ncus: deinitializing NCUs for %s",
active_ncp);
(void) nwam_ncp_walk_ncus(active_ncph,
ncu_create_init_fini_event, &init, NWAM_FLAG_NCU_TYPE_ALL,
NULL);
}
(void) pthread_mutex_unlock(&active_ncp_mutex);
}
static void
populate_common_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
{
nwam_value_t ncu_prop;
nwam_error_t err;
boolean_t enablevalue;
uint_t numvalues;
char **parent;
if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_ENABLED,
&ncu_prop)) != NWAM_SUCCESS) {
char *name;
(void) nwam_ncu_name_to_typed_name(ncu_data->ncu_name,
ncu_data->ncu_type, &name);
nlog(LOG_ERR, "nwam_ncu_get_prop_value %s ENABLED failed: %s",
name, nwam_strerror(err));
free(name);
ncu_data->ncu_enabled = B_TRUE;
} else {
if ((err = nwam_value_get_boolean(ncu_prop, &enablevalue)) !=
NWAM_SUCCESS) {
nlog(LOG_ERR, "nwam_value_get_boolean ENABLED failed: "
"%s", nwam_strerror(err));
} else {
ncu_data->ncu_enabled = enablevalue;
}
nwam_value_free(ncu_prop);
}
if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &parent,
&numvalues, NWAM_NCU_PROP_PARENT_NCP)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "nwam_ncu_get_prop_value %s PARENT failed: %s",
ncu_data->ncu_name, nwam_strerror(err));
} else {
(void) strlcpy(ncu_data->ncu_parent, parent[0],
sizeof (ncu_data->ncu_parent));
nwam_value_free(ncu_prop);
}
}
static void
populate_link_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
{
nwam_value_t ncu_prop;
nwam_error_t err;
char **mac_addr;
uint64_t *uintval;
uint_t numvalues;
if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval, &numvalues,
NWAM_NCU_PROP_ACTIVATION_MODE)) != NWAM_SUCCESS) {
nlog(LOG_ERR,
"populate_link_ncu_properties: could not get %s value: %s",
NWAM_NCU_PROP_ACTIVATION_MODE, nwam_strerror(err));
} else {
ncu_data->ncu_link.nwamd_link_activation_mode = uintval[0];
nwam_value_free(ncu_prop);
}
if (ncu_data->ncu_link.nwamd_link_activation_mode ==
NWAM_ACTIVATION_MODE_PRIORITIZED) {
ncu_data->ncu_enabled = B_TRUE;
if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval,
&numvalues, NWAM_NCU_PROP_PRIORITY_MODE))
!= NWAM_SUCCESS) {
nlog(LOG_ERR, "populate_link_ncu_properties: "
"could not get %s value: %s",
NWAM_NCU_PROP_PRIORITY_MODE, nwam_strerror(err));
} else {
ncu_data->ncu_link.nwamd_link_priority_mode =
uintval[0];
nwam_value_free(ncu_prop);
}
if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval,
&numvalues, NWAM_NCU_PROP_PRIORITY_GROUP))
!= NWAM_SUCCESS) {
nlog(LOG_ERR, "populate_link_ncu_properties: "
"could not get %s value: %s",
NWAM_NCU_PROP_PRIORITY_GROUP, nwam_strerror(err));
} else {
ncu_data->ncu_link.nwamd_link_priority_group =
uintval[0];
nwam_value_free(ncu_prop);
}
}
if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &mac_addr, &numvalues,
NWAM_NCU_PROP_LINK_MAC_ADDR)) != NWAM_SUCCESS) {
nlog(LOG_DEBUG,
"populate_link_ncu_properties: could not get %s value: %s",
NWAM_NCU_PROP_LINK_MAC_ADDR, nwam_strerror(err));
ncu_data->ncu_link.nwamd_link_mac_addr = NULL;
} else {
ncu_data->ncu_link.nwamd_link_mac_addr = strdup(*mac_addr);
ncu_data->ncu_link.nwamd_link_mac_addr_len = strlen(*mac_addr);
nwam_value_free(ncu_prop);
}
if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval, &numvalues,
NWAM_NCU_PROP_LINK_MTU)) != NWAM_SUCCESS) {
nlog(LOG_DEBUG,
"populate_link_ncu_properties: could not get %s value: %s",
NWAM_NCU_PROP_LINK_MTU, nwam_strerror(err));
ncu_data->ncu_link.nwamd_link_mtu = 0;
} else {
ncu_data->ncu_link.nwamd_link_mtu = uintval[0];
nwam_value_free(ncu_prop);
}
if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop,
&ncu_data->ncu_link.nwamd_link_autopush,
&ncu_data->ncu_link.nwamd_link_num_autopush,
NWAM_NCU_PROP_LINK_AUTOPUSH)) != NWAM_SUCCESS) {
nlog(LOG_DEBUG,
"populate_link_ncu_properties: could not get %s value: %s",
NWAM_NCU_PROP_LINK_AUTOPUSH, nwam_strerror(err));
ncu_data->ncu_link.nwamd_link_num_autopush = 0;
}
}
static void
populate_ip_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
{
nwamd_if_t *nif = &ncu_data->ncu_if;
struct nwamd_if_address **nifa, *nifai, *nifait;
boolean_t static_addr = B_FALSE, *boolvalue, dhcp_primary = B_FALSE;
uint64_t *addrsrcvalue;
nwam_value_t ncu_prop;
nwam_error_t err;
ipadm_addrobj_t ipaddr;
ipadm_status_t ipstatus;
char **addrvalue, ipreqhost[MAXNAMELEN];
uint_t numvalues;
uint64_t *ipversion;
int i;
nif->nwamd_if_ipv4 = B_FALSE;
nif->nwamd_if_ipv6 = B_FALSE;
nif->nwamd_if_dhcp_requested = B_FALSE;
nif->nwamd_if_stateful_requested = B_FALSE;
nif->nwamd_if_stateless_requested = B_FALSE;
nif->nwamd_if_ipv4_default_route_set = B_FALSE;
nif->nwamd_if_ipv6_default_route_set = B_FALSE;
if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &ipversion, &numvalues,
NWAM_NCU_PROP_IP_VERSION)) != NWAM_SUCCESS) {
nlog(LOG_ERR,
"populate_ip_ncu_properties: could not get %s value: %s",
NWAM_NCU_PROP_IP_VERSION, nwam_strerror(err));
} else {
for (i = 0; i < numvalues; i++) {
switch (ipversion[i]) {
case IPV4_VERSION:
nif->nwamd_if_ipv4 = B_TRUE;
break;
case IPV6_VERSION:
nif->nwamd_if_ipv6 = B_TRUE;
break;
default:
nlog(LOG_ERR, "bogus ip version %lld",
ipversion[i]);
break;
}
}
nwam_value_free(ncu_prop);
}
if ((err = nwamd_get_ncu_boolean(ncuh, &ncu_prop, &boolvalue,
&numvalues, NWAM_NCU_PROP_IP_PRIMARY)) != NWAM_SUCCESS) {
nlog(LOG_DEBUG, "populate_ip_ncu_properties: "
"could not get %s value: %s",
NWAM_NCU_PROP_IP_PRIMARY, nwam_strerror(err));
} else {
if (numvalues > 0)
dhcp_primary = boolvalue[0];
nwam_value_free(ncu_prop);
}
*ipreqhost = '\0';
if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
&numvalues, NWAM_NCU_PROP_IP_REQHOST)) != NWAM_SUCCESS) {
nlog(LOG_DEBUG, "populate_ip_ncu_properties: "
"could not get %s value: %s",
NWAM_NCU_PROP_IP_REQHOST, nwam_strerror(err));
} else {
if (numvalues > 0 && strlcpy(ipreqhost, addrvalue[0],
sizeof (ipreqhost)) >= sizeof (ipreqhost)) {
nlog(LOG_WARNING, "populate_ip_ncu_properties: "
"too long %s value: %s",
NWAM_NCU_PROP_IP_REQHOST, addrvalue[0]);
*ipreqhost = '\0';
}
nwam_value_free(ncu_prop);
}
for (nifai = nif->nwamd_if_list; nifai != NULL; nifai = nifait) {
nifait = nifai->next;
nifai->next = NULL;
ipadm_destroy_addrobj(nifai->ipaddr);
free(nifai);
}
nif->nwamd_if_list = NULL;
nifa = &(nif->nwamd_if_list);
if (!nif->nwamd_if_ipv4)
goto skip_ipv4;
if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &addrsrcvalue,
&numvalues, NWAM_NCU_PROP_IPV4_ADDRSRC)) != NWAM_SUCCESS) {
nlog(nif->nwamd_if_ipv4 ? LOG_ERR : LOG_DEBUG,
"populate_ip_ncu_properties: could not get %s value: %s",
NWAM_NCU_PROP_IPV4_ADDRSRC, nwam_strerror(err));
} else {
for (i = 0; i < numvalues; i++) {
switch (addrsrcvalue[i]) {
case NWAM_ADDRSRC_DHCP:
nif->nwamd_if_dhcp_requested = B_TRUE;
break;
case NWAM_ADDRSRC_STATIC:
static_addr = B_TRUE;
break;
default:
break;
}
}
nwam_value_free(ncu_prop);
}
if (nif->nwamd_if_dhcp_requested) {
ipstatus = ipadm_create_addrobj(IPADM_ADDR_DHCP,
ncu_data->ncu_name, &ipaddr);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"ipadm_create_addrobj failed for v4 dhcp: %s",
ipadm_status2str(ipstatus));
goto skip_ipv4_dhcp;
}
ipstatus = ipadm_set_wait_time(ipaddr, ncu_wait_time);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"ipadm_set_wait_time failed for v4 dhcp: %s",
ipadm_status2str(ipstatus));
ipadm_destroy_addrobj(ipaddr);
goto skip_ipv4_dhcp;
}
ipstatus = ipadm_set_primary(ipaddr, dhcp_primary);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"ipadm_set_primary failed for v4 dhcp: %s",
ipadm_status2str(ipstatus));
ipadm_destroy_addrobj(ipaddr);
goto skip_ipv4_dhcp;
}
ipstatus = ipadm_set_reqhost(ipaddr, ipreqhost);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"ipadm_set_reqhost failed for v4 dhcp: %s",
ipadm_status2str(ipstatus));
ipadm_destroy_addrobj(ipaddr);
goto skip_ipv4_dhcp;
}
if ((*nifa = calloc(sizeof (**nifa), 1)) != NULL) {
(*nifa)->family = AF_INET;
(*nifa)->ipaddr_atype = IPADM_ADDR_DHCP;
(*nifa)->ipaddr = ipaddr;
nifa = &((*nifa)->next);
*nifa = NULL;
} else {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"couldn't allocate nwamd address for v4 dhcp: %s",
strerror(errno));
ipadm_destroy_addrobj(ipaddr);
}
}
skip_ipv4_dhcp:
if (static_addr) {
if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
&numvalues, NWAM_NCU_PROP_IPV4_ADDR)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"could not get %s value: %s",
NWAM_NCU_PROP_IPV4_ADDR, nwam_strerror(err));
} else {
for (i = 0; i < numvalues; i++) {
ipstatus = ipadm_create_addrobj(
IPADM_ADDR_STATIC, ncu_data->ncu_name,
&ipaddr);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR,
"populate_ip_ncu_properties: "
"ipadm_create_addrobj failed "
"for %s: %s", addrvalue[i],
ipadm_status2str(ipstatus));
continue;
}
ipstatus = ipadm_set_addr(ipaddr, addrvalue[i],
AF_INET);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR,
"populate_ip_ncu_properties: "
"ipadm_set_addr failed for %s: %s",
addrvalue[i],
ipadm_status2str(ipstatus));
ipadm_destroy_addrobj(ipaddr);
continue;
}
if ((*nifa = calloc(sizeof (**nifa), 1))
!= NULL) {
(*nifa)->family = AF_INET;
(*nifa)->ipaddr_atype =
IPADM_ADDR_STATIC;
(*nifa)->ipaddr = ipaddr;
nifa = &((*nifa)->next);
} else {
nlog(LOG_ERR,
"populate_ip_ncu_properties: "
"couldn't allocate nwamd address "
"for %s: %s", addrvalue[i],
strerror(errno));
ipadm_destroy_addrobj(ipaddr);
}
}
*nifa = NULL;
nwam_value_free(ncu_prop);
}
}
if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
&numvalues, NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE)) == NWAM_SUCCESS) {
nif->nwamd_if_ipv4_default_route.sin_family = AF_INET;
(void) inet_pton(AF_INET, addrvalue[0],
&(nif->nwamd_if_ipv4_default_route.sin_addr));
nif->nwamd_if_ipv4_default_route_set = B_TRUE;
nwam_value_free(ncu_prop);
}
skip_ipv4:
if (!nif->nwamd_if_ipv6)
goto skip_ipv6;
static_addr = B_FALSE;
if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &addrsrcvalue,
&numvalues, NWAM_NCU_PROP_IPV6_ADDRSRC)) != NWAM_SUCCESS) {
nlog(nif->nwamd_if_ipv6 ? LOG_ERR : LOG_DEBUG,
"populate_ip_ncu_properties: could not get %s value: %s",
NWAM_NCU_PROP_IPV6_ADDRSRC, nwam_strerror(err));
} else {
for (i = 0; i < numvalues; i++) {
switch (addrsrcvalue[i]) {
case NWAM_ADDRSRC_DHCP:
nif->nwamd_if_stateful_requested = B_TRUE;
break;
case NWAM_ADDRSRC_AUTOCONF:
nif->nwamd_if_stateless_requested = B_TRUE;
break;
case NWAM_ADDRSRC_STATIC:
static_addr = B_TRUE;
break;
default:
break;
}
}
nwam_value_free(ncu_prop);
}
if (nif->nwamd_if_stateful_requested ||
nif->nwamd_if_stateless_requested) {
ipstatus = ipadm_create_addrobj(IPADM_ADDR_IPV6_ADDRCONF,
ncu_data->ncu_name, &ipaddr);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"ipadm_create_addrobj failed for v6 "
"stateless/stateful: %s",
ipadm_status2str(ipstatus));
goto skip_ipv6_addrconf;
}
if (!nif->nwamd_if_stateful_requested) {
ipstatus = ipadm_set_stateful(ipaddr, B_FALSE);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"ipadm_set_stateful failed for v6: %s",
ipadm_status2str(ipstatus));
ipadm_destroy_addrobj(ipaddr);
goto skip_ipv6_addrconf;
}
}
if (!nif->nwamd_if_stateless_requested) {
ipstatus = ipadm_set_stateless(ipaddr, B_FALSE);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"ipadm_set_stateless failed for v6: %s",
ipadm_status2str(ipstatus));
ipadm_destroy_addrobj(ipaddr);
goto skip_ipv6_addrconf;
}
}
if ((*nifa = calloc(sizeof (**nifa), 1)) != NULL) {
(*nifa)->family = AF_INET6;
(*nifa)->ipaddr_atype = IPADM_ADDR_IPV6_ADDRCONF;
(*nifa)->ipaddr = ipaddr;
nifa = &((*nifa)->next);
*nifa = NULL;
} else {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"couldn't allocate nwamd address for "
"v6 stateless/stateful: %s", strerror(errno));
ipadm_destroy_addrobj(ipaddr);
}
}
skip_ipv6_addrconf:
if (static_addr) {
if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
&numvalues, NWAM_NCU_PROP_IPV6_ADDR)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "populate_ip_ncu_properties: "
"could not get %s value: %s",
NWAM_NCU_PROP_IPV6_ADDR, nwam_strerror(err));
} else {
for (i = 0; i < numvalues; i++) {
ipstatus = ipadm_create_addrobj(
IPADM_ADDR_STATIC, ncu_data->ncu_name,
&ipaddr);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR,
"populate_ip_ncu_properties: "
"ipadm_create_addrobj failed "
"for %s: %s", addrvalue[i],
ipadm_status2str(ipstatus));
continue;
}
ipstatus = ipadm_set_addr(ipaddr, addrvalue[i],
AF_INET6);
if (ipstatus != IPADM_SUCCESS) {
nlog(LOG_ERR,
"populate_ip_ncu_properties: "
"ipadm_set_addr failed for %s: %s",
addrvalue[i],
ipadm_status2str(ipstatus));
ipadm_destroy_addrobj(ipaddr);
continue;
}
if ((*nifa = calloc(sizeof (**nifa), 1))
!= NULL) {
(*nifa)->family = AF_INET6;
(*nifa)->ipaddr_atype =
IPADM_ADDR_STATIC;
(*nifa)->ipaddr = ipaddr;
nifa = &((*nifa)->next);
} else {
nlog(LOG_ERR,
"populate_ip_ncu_properties: "
"couldn't allocate nwamd address "
"for %s: %s", addrvalue[i],
strerror(errno));
ipadm_destroy_addrobj(ipaddr);
}
}
*nifa = NULL;
nwam_value_free(ncu_prop);
}
}
if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
&numvalues, NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE)) == NWAM_SUCCESS) {
nif->nwamd_if_ipv6_default_route.sin6_family = AF_INET6;
(void) inet_pton(AF_INET6, addrvalue[0],
&(nif->nwamd_if_ipv6_default_route.sin6_addr));
nif->nwamd_if_ipv6_default_route_set = B_TRUE;
nwam_value_free(ncu_prop);
}
skip_ipv6:
;
}
static nwamd_ncu_t *
nwamd_ncu_init(nwam_ncu_type_t ncu_type, const char *name)
{
nwamd_ncu_t *rv;
nlog(LOG_DEBUG, "nwamd_ncu_init(%d, %s)", ncu_type, name);
if ((rv = calloc(1, sizeof (*rv))) == NULL)
return (NULL);
rv->ncu_type = ncu_type;
rv->ncu_name = strdup(name);
rv->ncu_enabled = B_FALSE;
if (rv->ncu_type == NWAM_NCU_TYPE_LINK) {
(void) bzero(&rv->ncu_link, sizeof (nwamd_link_t));
(void) dladm_name2info(dld_handle, name,
&rv->ncu_link.nwamd_link_id, NULL, NULL,
&rv->ncu_link.nwamd_link_media);
(void) pthread_mutex_init(
&rv->ncu_link.nwamd_link_wifi_mutex, NULL);
rv->ncu_link.nwamd_link_wifi_priority = MAXINT;
} else {
(void) bzero(&rv->ncu_if, sizeof (nwamd_if_t));
}
return (rv);
}
void
nwamd_ncu_free(nwamd_ncu_t *ncu)
{
if (ncu != NULL) {
assert(ncu->ncu_type == NWAM_NCU_TYPE_LINK ||
ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE);
if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
struct nwamd_link *l = &ncu->ncu_link;
int i;
free(l->nwamd_link_wifi_key);
free(l->nwamd_link_mac_addr);
for (i = 0; i < l->nwamd_link_num_autopush; i++)
free(l->nwamd_link_autopush[i]);
} else if (ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE) {
struct nwamd_if_address *nifa;
nifa = ncu->ncu_if.nwamd_if_list;
while (nifa != NULL) {
struct nwamd_if_address *n;
n = nifa;
nifa = nifa->next;
ipadm_destroy_addrobj(n->ipaddr);
free(n);
}
}
free(ncu->ncu_name);
free(ncu);
}
}
static int
nwamd_ncu_display(nwamd_object_t ncu_obj, void *data)
{
nwamd_ncu_t *ncu = (nwamd_ncu_t *)ncu_obj->nwamd_object_data;
data = data;
nlog(LOG_DEBUG, "NCU (%p) %s state %s, %s",
(void *)ncu, ncu_obj->nwamd_object_name,
nwam_state_to_string(ncu_obj->nwamd_object_state),
nwam_aux_state_to_string(ncu_obj->nwamd_object_aux_state));
return (0);
}
void
nwamd_log_ncus(void)
{
nlog(LOG_DEBUG, "NCP %s", active_ncp);
(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU, nwamd_ncu_display,
NULL);
}
int
nwamd_ncu_action(const char *ncu, const char *parent, nwam_action_t action)
{
nwamd_event_t ncu_event = nwamd_event_init_object_action
(NWAM_OBJECT_TYPE_NCU, ncu, parent, action);
if (ncu_event == NULL)
return (1);
nwamd_event_enqueue(ncu_event);
return (0);
}
static void
add_phys_ncu_to_ncp(nwam_ncp_handle_t ncph, const char *name)
{
dladm_status_t dlrtn;
uint32_t media;
boolean_t is_wireless;
nwam_error_t err;
nwam_ncu_handle_t ncuh;
uint64_t uintval;
if ((dlrtn = dladm_name2info(dld_handle, name, NULL, NULL, NULL,
&media)) != DLADM_STATUS_OK) {
char errmsg[DLADM_STRSIZE];
nlog(LOG_ERR, "failed to get media type for %s: %s", name,
dladm_status2str(dlrtn, errmsg));
return;
}
is_wireless = (media == DL_WIFI);
if ((err = nwam_ncu_create(ncph, name, NWAM_NCU_TYPE_LINK,
NWAM_NCU_CLASS_PHYS, &ncuh)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "failed to create link ncu for %s: %s", name,
nwam_strerror(err));
if (err == NWAM_ENTITY_READ_ONLY) {
nwamd_event_t retry_event;
nlog(LOG_DEBUG, "Retrying addition of phys ncu for %s",
name);
retry_event = nwamd_event_init_link_action(name,
NWAM_ACTION_ADD);
if (retry_event != NULL) {
nwamd_event_enqueue_timed(retry_event,
NWAMD_READONLY_RETRY_INTERVAL);
}
}
return;
}
uintval = NWAM_ACTIVATION_MODE_PRIORITIZED;
if ((err = nwamd_set_ncu_uint(ncuh, &uintval, 1,
NWAM_NCU_PROP_ACTIVATION_MODE)) != NWAM_SUCCESS) {
goto finish;
}
uintval = is_wireless ? 1 : 0;
if ((err = nwamd_set_ncu_uint(ncuh, &uintval, 1,
NWAM_NCU_PROP_PRIORITY_GROUP)) != NWAM_SUCCESS) {
goto finish;
}
uintval = is_wireless ? NWAM_PRIORITY_MODE_EXCLUSIVE :
NWAM_PRIORITY_MODE_SHARED;
if ((err = nwamd_set_ncu_uint(ncuh, &uintval, 1,
NWAM_NCU_PROP_PRIORITY_MODE)) != NWAM_SUCCESS) {
goto finish;
}
err = nwam_ncu_commit(ncuh, 0);
finish:
nwam_ncu_free(ncuh);
if (err != NWAM_SUCCESS) {
nlog(LOG_ERR,
"failed to create automatic link ncu for %s: %s",
name, nwam_strerror(err));
}
}
static void
add_ip_ncu_to_ncp(nwam_ncp_handle_t ncph, const char *name)
{
nwam_error_t err;
nwam_ncu_handle_t ncuh;
if ((err = nwam_ncu_create(ncph, name, NWAM_NCU_TYPE_INTERFACE,
NWAM_NCU_CLASS_IP, &ncuh)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "failed to create ip ncu for %s: %s", name,
nwam_strerror(err));
return;
}
err = nwam_ncu_commit(ncuh, 0);
nwam_ncu_free(ncuh);
if (err != NWAM_SUCCESS) {
nlog(LOG_ERR,
"failed to create ip ncu for %s: %s", name,
nwam_strerror(err));
}
}
static void
remove_ncu_from_ncp(nwam_ncp_handle_t ncph, const char *name,
nwam_ncu_type_t type)
{
nwam_error_t err;
nwam_ncu_handle_t ncuh;
if ((err = nwam_ncu_read(ncph, name, type, 0, &ncuh)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "failed to read automatic ncu %s: %s", name,
nwam_strerror(err));
return;
}
err = nwam_ncu_destroy(ncuh, 0);
if (err != NWAM_SUCCESS) {
nlog(LOG_ERR, "failed to delete automatic ncu %s: %s", name,
nwam_strerror(err));
}
}
void
ncu_action_change_state(nwam_action_t action, nwam_ncu_type_t type,
const char *name)
{
nwamd_object_t ncu_obj = NULL;
nwamd_ncu_t *ncu;
if ((ncu_obj = nwamd_ncu_object_find(type, name)) == NULL)
return;
ncu = ncu_obj->nwamd_object_data;
if (action == NWAM_ACTION_ADD) {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_OFFLINE, NWAM_AUX_STATE_CONDITIONS_NOT_MET);
} else {
if (ncu->ncu_enabled) {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_NOT_FOUND);
} else {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_UNINITIALIZED,
NWAM_AUX_STATE_NOT_FOUND);
}
}
nwamd_object_release(ncu_obj);
}
void
nwamd_ncu_handle_link_action_event(nwamd_event_t event)
{
nwam_ncp_handle_t ncph;
nwam_ncu_type_t type;
nwam_action_t action =
event->event_msg->nwe_data.nwe_link_action.nwe_action;
nwam_error_t err;
char *name;
boolean_t automatic_ncp_active = B_FALSE;
if (action != NWAM_ACTION_ADD && action != NWAM_ACTION_REMOVE) {
nlog(LOG_ERR, "nwamd_ncu_handle_link_action_event: "
"invalid link action %s", nwam_action_to_string(action));
nwamd_event_do_not_send(event);
return;
}
nlog(LOG_DEBUG, "nwamd_ncu_handle_link_action_event: "
"link action '%s' event on %s", nwam_action_to_string(action),
event->event_object[0] == 0 ? "n/a" : event->event_object);
if ((err = nwam_ncu_typed_name_to_name(event->event_object, &type,
&name)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "nwamd_ncu_handle_link_action_event: "
"translation from typedname error: %s", nwam_strerror(err));
nwamd_event_do_not_send(event);
return;
}
(void) pthread_mutex_lock(&active_ncp_mutex);
if (strcmp(active_ncp, NWAM_NCP_NAME_AUTOMATIC) == 0 &&
active_ncph != NULL) {
automatic_ncp_active = B_TRUE;
}
(void) pthread_mutex_unlock(&active_ncp_mutex);
if ((err = nwam_ncp_read(NWAM_NCP_NAME_AUTOMATIC, 0, &ncph))
== NWAM_ENTITY_NOT_FOUND) {
err = nwam_ncp_create(NWAM_NCP_NAME_AUTOMATIC, 0, &ncph);
}
if (err != NWAM_SUCCESS)
goto fail;
if (action == NWAM_ACTION_ADD) {
add_phys_ncu_to_ncp(ncph, name);
add_ip_ncu_to_ncp(ncph, name);
} else {
remove_ncu_from_ncp(ncph, name, NWAM_NCU_TYPE_INTERFACE);
remove_ncu_from_ncp(ncph, name, NWAM_NCU_TYPE_LINK);
}
nwam_ncp_free(ncph);
if (!automatic_ncp_active) {
ncu_action_change_state(action, NWAM_NCU_TYPE_INTERFACE, name);
ncu_action_change_state(action, NWAM_NCU_TYPE_LINK, name);
}
if (!nwamd_event_enqueued(NWAM_EVENT_TYPE_NCU_CHECK,
NWAM_OBJECT_TYPE_NCP, NULL)) {
nwamd_create_ncu_check_event(NEXT_FEW_SECONDS);
}
fail:
free(name);
if (err != NWAM_SUCCESS) {
nwamd_event_t retry_event = nwamd_event_init_link_action(name,
action);
if (retry_event == NULL) {
nlog(LOG_ERR, "nwamd_ncu_handle_link_action_event: "
"could not create retry event to read/create "
"%s NCP", NWAM_NCP_NAME_AUTOMATIC);
return;
}
nlog(LOG_ERR, "nwamd_ncu_handle_link_action_event: "
"could not read/create %s NCP, retrying in %d seconds",
NWAM_NCP_NAME_AUTOMATIC, NWAMD_READONLY_RETRY_INTERVAL);
nwamd_event_enqueue_timed(retry_event,
NWAMD_READONLY_RETRY_INTERVAL);
}
}
struct link_aggr_search_data {
datalink_id_t linkid;
boolean_t under;
};
static int
ncu_aggr_search(const char *name, void *data)
{
struct link_aggr_search_data *lasd = data;
dladm_aggr_grp_attr_t ginfo;
datalink_id_t linkid;
int i;
if (dladm_name2info(dld_handle, name, &linkid, NULL, NULL, NULL) !=
DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
if (dladm_aggr_info(dld_handle, linkid, &ginfo, DLADM_OPT_ACTIVE)
!= DLADM_STATUS_OK || ginfo.lg_nports == 0)
return (DLADM_WALK_CONTINUE);
for (i = 0; i < ginfo.lg_nports; i++) {
if (lasd->linkid == ginfo.lg_ports[i].lp_linkid) {
lasd->under = B_TRUE;
return (DLADM_WALK_TERMINATE);
}
}
free(ginfo.lg_ports);
return (DLADM_WALK_CONTINUE);
}
static boolean_t
nwamd_link_belongs_to_an_aggr(const char *name)
{
struct link_aggr_search_data lasd;
if (dladm_name2info(dld_handle, name, &lasd.linkid, NULL, NULL, NULL)
!= DLADM_STATUS_OK)
return (B_FALSE);
lasd.under = B_FALSE;
(void) dladm_walk(ncu_aggr_search, dld_handle, &lasd,
DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
return (lasd.under);
}
static int
ncu_create_link_action_event(const char *name, void *data)
{
nwam_ncp_handle_t ncph = data;
nwam_ncu_handle_t ncuh;
nwamd_event_t link_event;
if (strncmp(name, VBOX_IFACE_PREFIX, strlen(VBOX_IFACE_PREFIX)) == 0)
return (DLADM_WALK_CONTINUE);
if (!nwamd_link_belongs_to_this_zone(name))
return (DLADM_WALK_CONTINUE);
if (nwamd_link_belongs_to_an_aggr(name)) {
return (DLADM_WALK_CONTINUE);
}
if (ncph != NULL && nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_LINK, 0,
&ncuh) == NWAM_SUCCESS) {
nwam_ncu_free(ncuh);
return (DLADM_WALK_CONTINUE);
}
nlog(LOG_DEBUG, "ncu_create_link_action_event: adding ncus for %s",
name);
link_event = nwamd_event_init_link_action(name, NWAM_ACTION_ADD);
if (link_event != NULL)
nwamd_event_enqueue(link_event);
return (DLADM_WALK_CONTINUE);
}
static int
nwamd_destroy_ncu(nwam_ncu_handle_t ncuh, void *data)
{
char *name;
uint32_t flags;
nwamd_event_t link_event;
if (nwam_ncu_get_name(ncuh, &name) != NWAM_SUCCESS) {
nlog(LOG_ERR, "nwamd_destroy_ncu: could not get NCU name");
return (0);
}
if ((dladm_name2info(dld_handle, name, NULL, &flags, NULL, NULL)
== DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) &&
!nwamd_link_belongs_to_an_aggr(name)) {
free(name);
return (0);
}
nlog(LOG_DEBUG, "nwamd_destroy_ncu: destroying ncus for %s", name);
link_event = nwamd_event_init_link_action(name, NWAM_ACTION_REMOVE);
if (link_event != NULL)
nwamd_event_enqueue(link_event);
free(name);
return (0);
}
void
nwamd_walk_physical_configuration(void)
{
nwam_ncp_handle_t ncph;
datalink_class_t dlclass = DATALINK_CLASS_PHYS;
zoneid_t zoneid = getzoneid();
(void) pthread_mutex_lock(&active_ncp_mutex);
if (strcmp(active_ncp, NWAM_NCP_NAME_AUTOMATIC) == 0 &&
active_ncph != NULL) {
ncph = active_ncph;
} else {
if (nwam_ncp_read(NWAM_NCP_NAME_AUTOMATIC, 0, &ncph)
!= NWAM_SUCCESS) {
ncph = NULL;
}
}
if (ncph != NULL) {
(void) nwam_ncp_walk_ncus(ncph, nwamd_destroy_ncu, NULL,
NWAM_FLAG_NCU_TYPE_LINK, NULL);
}
if (zoneid != GLOBAL_ZONEID)
dlclass |= DATALINK_CLASS_VNIC;
(void) dladm_walk(ncu_create_link_action_event, dld_handle, ncph,
dlclass, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
if (strcmp(active_ncp, NWAM_NCP_NAME_AUTOMATIC) != 0 ||
active_ncph == NULL) {
nwam_ncp_free(ncph);
}
(void) pthread_mutex_unlock(&active_ncp_mutex);
}
void
nwamd_ncu_handle_init_event(nwamd_event_t event)
{
nwamd_object_t object = NULL;
nwam_ncu_handle_t ncuh;
nwamd_ncu_t *ncu = NULL;
nwam_error_t err;
nwam_ncu_type_t type;
char *name;
uint32_t flags;
boolean_t new = B_TRUE;
nlog(LOG_DEBUG, "nwamd_ncu_handle_init_event(%s)",
event->event_object);
err = nwam_ncu_typed_name_to_name(event->event_object,
&type, &name);
if (err != NWAM_SUCCESS) {
nlog(LOG_ERR, "nwamd_ncu_handle_init_event: "
"nwam_ncu_typed_name_to_name returned %s",
nwam_strerror(err));
nwamd_event_do_not_send(event);
return;
}
(void) pthread_mutex_lock(&active_ncp_mutex);
if (active_ncph == NULL) {
nlog(LOG_DEBUG,
"nwamd_ncu_handle_init_event: active NCP handle NULL");
nwamd_event_do_not_send(event);
free(name);
(void) pthread_mutex_unlock(&active_ncp_mutex);
return;
}
err = nwam_ncu_read(active_ncph, event->event_object,
type, 0, &ncuh);
(void) pthread_mutex_unlock(&active_ncp_mutex);
if (err != NWAM_SUCCESS) {
nlog(LOG_ERR, "nwamd_ncu_handle_init_event: "
"could not read object '%s': %s",
event->event_object, nwam_strerror(err));
free(name);
nwamd_event_do_not_send(event);
return;
}
if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
event->event_object)) != NULL)
new = B_FALSE;
switch (type) {
case NWAM_NCU_TYPE_LINK:
if (new) {
ncu = nwamd_ncu_init(type, name);
} else {
ncu = object->nwamd_object_data;
nwam_ncu_free(object->nwamd_object_handle);
}
populate_common_ncu_properties(ncuh, ncu);
populate_link_ncu_properties(ncuh, ncu);
break;
case NWAM_NCU_TYPE_INTERFACE:
if (!new) {
nwam_ncu_free(object->nwamd_object_handle);
nwamd_ncu_free(object->nwamd_object_data);
}
ncu = nwamd_ncu_init(type, name);
populate_common_ncu_properties(ncuh, ncu);
populate_ip_ncu_properties(ncuh, ncu);
break;
default:
nlog(LOG_ERR, "unknown ncu type %d", type);
free(name);
nwam_ncu_free(ncuh);
nwamd_event_do_not_send(event);
nwamd_object_release(object);
return;
}
if (new) {
nlog(LOG_DEBUG, "nwamd_ncu_handle_init_event: didn't find "
"ncu so create it %s", name);
object = nwamd_object_init(NWAM_OBJECT_TYPE_NCU,
event->event_object, ncuh, ncu);
} else {
nlog(LOG_DEBUG, "nwamd_ncu_handle_init_event: refreshing "
"ncu %s", name);
object->nwamd_object_data = ncu;
object->nwamd_object_handle = ncuh;
}
if (dladm_name2info(dld_handle, name, NULL, &flags, NULL, NULL)
!= DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE)) {
nlog(LOG_DEBUG, "nwam_ncu_handle_init_event: "
"interface for NCU %s doesn't exist",
event->event_object);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
object->nwamd_object_name, NWAM_STATE_UNINITIALIZED,
NWAM_AUX_STATE_NOT_FOUND);
free(name);
nwamd_object_release(object);
return;
}
if (object->nwamd_object_state == NWAM_STATE_INITIALIZED) {
if (!ncu->ncu_enabled) {
object->nwamd_object_state = NWAM_STATE_DISABLED;
object->nwamd_object_aux_state =
NWAM_AUX_STATE_MANUAL_DISABLE;
} else {
object->nwamd_object_state = NWAM_STATE_OFFLINE;
object->nwamd_object_aux_state =
NWAM_AUX_STATE_CONDITIONS_NOT_MET;
}
} else {
nwamd_link_t *link = &ncu->ncu_link;
if (!ncu->ncu_enabled) {
if (object->nwamd_object_state != NWAM_STATE_DISABLED) {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
object->nwamd_object_name,
NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_MANUAL_DISABLE);
}
goto done;
} else {
if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
int64_t c;
(void) pthread_mutex_lock(&active_ncp_mutex);
c = current_ncu_priority_group;
(void) pthread_mutex_unlock(&active_ncp_mutex);
if (link->nwamd_link_activation_mode ==
NWAM_ACTIVATION_MODE_MANUAL ||
(link->nwamd_link_activation_mode ==
NWAM_ACTIVATION_MODE_PRIORITIZED &&
link->nwamd_link_priority_mode <= c)) {
nwamd_object_set_state
(NWAM_OBJECT_TYPE_NCU,
object->nwamd_object_name,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_INITIALIZED);
} else {
nwamd_object_set_state
(NWAM_OBJECT_TYPE_NCU,
object->nwamd_object_name,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_INITIALIZED);
}
goto done;
}
}
switch (type) {
case NWAM_NCU_TYPE_LINK:
if (ncu->ncu_link.nwamd_link_media == DL_WIFI) {
(void) nwamd_wlan_scan(ncu->ncu_name);
}
break;
case NWAM_NCU_TYPE_INTERFACE:
if (object->nwamd_object_state != NWAM_STATE_OFFLINE) {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
object->nwamd_object_name,
NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_DOWN);
} else {
object->nwamd_object_state = NWAM_STATE_OFFLINE;
object->nwamd_object_aux_state =
NWAM_AUX_STATE_CONDITIONS_NOT_MET;
}
break;
}
}
done:
if (type == NWAM_NCU_TYPE_LINK &&
!nwamd_event_enqueued(NWAM_EVENT_TYPE_NCU_CHECK,
NWAM_OBJECT_TYPE_NCP, NULL)) {
nwamd_create_ncu_check_event(NEXT_FEW_SECONDS);
}
free(name);
nwamd_object_release(object);
}
void
nwamd_ncu_handle_fini_event(nwamd_event_t event)
{
nwamd_object_t object;
nwamd_event_t state_event;
nlog(LOG_DEBUG, "nwamd_ncu_handle_fini_event(%s)",
event->event_object);
state_event = nwamd_event_init_object_state(NWAM_OBJECT_TYPE_NCU,
event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_UNINITIALIZED);
if (state_event == NULL) {
nwamd_event_do_not_send(event);
return;
}
nwamd_ncu_handle_state_event(state_event);
nwamd_event_fini(state_event);
if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
event->event_object)) == NULL) {
nlog(LOG_INFO, "nwamd_ncu_handle_fini_event: "
"ncu %s not found", event->event_object);
nwamd_event_do_not_send(event);
return;
}
nwamd_object_release_and_destroy(object);
}
void
nwamd_ncu_handle_action_event(nwamd_event_t event)
{
nwamd_object_t object;
(void) pthread_mutex_lock(&active_ncp_mutex);
if (strcmp(event->event_msg->nwe_data.nwe_object_action.nwe_parent,
active_ncp) != 0) {
nlog(LOG_DEBUG, "nwamd_ncu_handle_action_event: action for "
"inactive NCP %s, nothing to do",
event->event_msg->nwe_data.nwe_object_action.nwe_parent);
(void) pthread_mutex_unlock(&active_ncp_mutex);
return;
}
(void) pthread_mutex_unlock(&active_ncp_mutex);
switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
case NWAM_ACTION_ENABLE:
object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
event->event_object);
if (object == NULL) {
nlog(LOG_ERR, "nwamd_ncu_handle_action_event: "
"could not find ncu %s", event->event_object);
nwamd_event_do_not_send(event);
return;
}
if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
nlog(LOG_DEBUG, "nwamd_ncu_handle_action_event: "
"ncu %s already online, nothing to do",
event->event_object);
nwamd_object_release(object);
return;
}
nwamd_object_release(object);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_INITIALIZED);
break;
case NWAM_ACTION_DISABLE:
object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
event->event_object);
if (object == NULL) {
nlog(LOG_ERR, "nwamd_ncu_handle_action_event: "
"could not find ncu %s", event->event_object);
nwamd_event_do_not_send(event);
return;
}
if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
nlog(LOG_DEBUG, "nwamd_ncu_handle_action_event: "
"ncu %s already disabled, nothing to do",
event->event_object);
nwamd_object_release(object);
return;
}
nwamd_object_release(object);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_MANUAL_DISABLE);
break;
case NWAM_ACTION_ADD:
case NWAM_ACTION_REFRESH:
nwamd_ncu_handle_init_event(event);
break;
case NWAM_ACTION_DESTROY:
nwamd_ncu_handle_fini_event(event);
break;
default:
nlog(LOG_INFO, "nwam_ncu_handle_action_event: "
"unexpected action");
nwamd_event_do_not_send(event);
break;
}
}
void
nwamd_ncu_handle_state_event(nwamd_event_t event)
{
nwamd_object_t object;
nwam_state_t old_state, new_state;
nwam_aux_state_t new_aux_state;
nwamd_ncu_t *ncu;
boolean_t is_link, enabled, prioritized = B_FALSE;
char linkname[NWAM_MAX_NAME_LEN];
nwam_event_t m = event->event_msg;
if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
event->event_object)) == NULL) {
nlog(LOG_INFO, "nwamd_ncu_handle_state_event %lld: "
"state event for nonexistent NCU %s", event->event_id,
event->event_object);
nwamd_event_do_not_send(event);
return;
}
ncu = object->nwamd_object_data;
old_state = object->nwamd_object_state;
new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
new_aux_state =
event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;
(void) strlcpy(m->nwe_data.nwe_object_state.nwe_parent, ncu->ncu_parent,
sizeof (m->nwe_data.nwe_object_state.nwe_parent));
if ((new_aux_state == NWAM_AUX_STATE_IF_DHCP_TIMED_OUT ||
new_aux_state == NWAM_AUX_STATE_UP) &&
object->nwamd_object_state == NWAM_STATE_ONLINE) {
nlog(LOG_INFO, "nwamd_ncu_handle_state_event: "
"NCU %s already online, not going to '%s' state",
object->nwamd_object_name,
nwam_aux_state_to_string(new_aux_state));
nwamd_event_do_not_send(event);
nwamd_object_release(object);
return;
}
if (new_state == object->nwamd_object_state &&
new_aux_state == object->nwamd_object_aux_state) {
nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: "
"NCU %s already in state (%s, %s)",
object->nwamd_object_name, nwam_state_to_string(new_state),
nwam_aux_state_to_string(new_aux_state));
nwamd_object_release(object);
return;
}
if (old_state == NWAM_STATE_MAINTENANCE &&
(new_state == NWAM_STATE_ONLINE ||
(new_state == NWAM_STATE_OFFLINE_TO_ONLINE &&
new_aux_state != NWAM_AUX_STATE_INITIALIZED))) {
nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: "
"NCU %s cannot transition from state %s to state (%s, %s)",
object->nwamd_object_name, nwam_state_to_string(old_state),
nwam_state_to_string(new_state),
nwam_aux_state_to_string(new_aux_state));
nwamd_event_do_not_send(event);
nwamd_object_release(object);
return;
}
object->nwamd_object_state = new_state;
object->nwamd_object_aux_state = new_aux_state;
nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: changing state for NCU "
"%s to (%s, %s)", object->nwamd_object_name,
nwam_state_to_string(object->nwamd_object_state),
nwam_aux_state_to_string(object->nwamd_object_aux_state));
is_link = (ncu->ncu_type == NWAM_NCU_TYPE_LINK);
if (is_link)
(void) strlcpy(linkname, ncu->ncu_name, sizeof (linkname));
prioritized = (ncu->ncu_type == NWAM_NCU_TYPE_LINK &&
ncu->ncu_link.nwamd_link_activation_mode ==
NWAM_ACTIVATION_MODE_PRIORITIZED);
enabled = ncu->ncu_enabled;
nwamd_object_release(object);
switch (new_state) {
case NWAM_STATE_OFFLINE_TO_ONLINE:
if (enabled) {
nwamd_ncu_state_machine(event->event_object);
} else {
nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: "
"cannot move disabled NCU %s online",
event->event_object);
nwamd_event_do_not_send(event);
}
break;
case NWAM_STATE_ONLINE_TO_OFFLINE:
nwamd_ncu_state_machine(event->event_object);
break;
case NWAM_STATE_ONLINE:
nwamd_ncu_state_machine(event->event_object);
break;
case NWAM_STATE_OFFLINE:
if (prioritized) {
nwamd_create_ncu_check_event(0);
}
break;
case NWAM_STATE_DISABLED:
case NWAM_STATE_UNINITIALIZED:
case NWAM_STATE_MAINTENANCE:
case NWAM_STATE_DEGRADED:
default:
break;
}
if (is_link) {
if ((new_state == NWAM_STATE_ONLINE_TO_OFFLINE &&
new_aux_state != NWAM_AUX_STATE_UNINITIALIZED &&
new_aux_state != NWAM_AUX_STATE_NOT_FOUND) ||
new_state == NWAM_STATE_DISABLED) {
nwamd_propogate_link_up_down_to_ip(linkname, B_FALSE);
}
if (new_state == NWAM_STATE_ONLINE) {
nwamd_propogate_link_up_down_to_ip(linkname, B_TRUE);
}
} else {
if (new_state == NWAM_STATE_ONLINE)
nwamd_create_ncu_check_event(0);
}
}