#include <arpa/inet.h>
#include <assert.h>
#include <libdllink.h>
#include <libdlstat.h>
#include <libnwam.h>
#include <libscf.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <values.h>
#include "conditions.h"
#include "events.h"
#include "objects.h"
#include "ncp.h"
#include "ncu.h"
#include "util.h"
char active_ncp[NWAM_MAX_NAME_LEN];
nwam_ncp_handle_t active_ncph = NULL;
int64_t current_ncu_priority_group = INVALID_PRIORITY_GROUP;
pthread_mutex_t active_ncp_mutex = PTHREAD_MUTEX_INITIALIZER;
uint64_t ncu_wait_time = NCU_WAIT_TIME_DEFAULT;
boolean_t initial_ncp_enable = B_TRUE;
void
nwamd_ncp_handle_enable_event(nwamd_event_t event)
{
char *new_ncp = event->event_object;
nwam_ncp_handle_t new_ncph;
nwam_error_t err;
if (new_ncp[0] == '\0')
return;
(void) pthread_mutex_lock(&active_ncp_mutex);
if (strcmp(active_ncp, new_ncp) == 0 && !initial_ncp_enable) {
nlog(LOG_DEBUG, "nwamd_ncp_handle_enable_event: "
"%s is already active", new_ncp);
(void) pthread_mutex_unlock(&active_ncp_mutex);
return;
}
(void) pthread_mutex_unlock(&active_ncp_mutex);
nlog(LOG_DEBUG, "nwamd_ncp_handle_enable_event: activating NCP %s",
new_ncp);
nwamd_fini_ncus();
if ((err = nwam_ncp_read(new_ncp, 0, &new_ncph))
== NWAM_ENTITY_NOT_FOUND) {
err = nwam_ncp_create(new_ncp, 0, &new_ncph);
}
if (err == NWAM_SUCCESS) {
nwam_ncp_free(new_ncph);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCP, new_ncp,
NWAM_STATE_ONLINE, NWAM_AUX_STATE_ACTIVE);
} else if (initial_ncp_enable) {
nwamd_event_t retry_event = nwamd_event_init_object_action
(NWAM_OBJECT_TYPE_NCP, new_ncp, NULL, NWAM_ACTION_ENABLE);
if (retry_event == NULL) {
nlog(LOG_ERR, "nwamd_ncp_handle_enable_event: "
"could not create retry event to enable %s NCP",
new_ncp);
return;
}
nlog(LOG_ERR, "nwamd_ncp_handle_enable_event: "
"failed to enable %s NCP, retrying in %d seconds",
new_ncp, NWAMD_READONLY_RETRY_INTERVAL);
nwamd_event_enqueue_timed(retry_event,
NWAMD_READONLY_RETRY_INTERVAL);
} else {
nlog(LOG_ERR, "nwamd_ncp_handle_enable_event: error %s",
nwam_strerror(err));
return;
}
}
void
nwamd_ncp_handle_action_event(nwamd_event_t event)
{
switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
case NWAM_ACTION_ENABLE:
nwamd_ncp_handle_enable_event(event);
break;
case NWAM_ACTION_ADD:
case NWAM_ACTION_DESTROY:
break;
default:
nlog(LOG_INFO, "nwam_ncp_handle_action_event: "
"unexpected action");
nwamd_event_do_not_send(event);
break;
}
}
void
nwamd_ncp_handle_state_event(nwamd_event_t event)
{
char *new_ncp = event->event_object;
nwam_ncp_handle_t new_ncph, old_ncph;
nwam_error_t err;
if ((err = nwam_ncp_read(new_ncp, 0, &new_ncph)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "nwamd_ncp_handle_state_event: "
"cannot read NCP %s: : %s", new_ncp, nwam_strerror(err));
nwamd_event_do_not_send(event);
return;
}
(void) pthread_mutex_lock(&active_ncp_mutex);
old_ncph = active_ncph;
active_ncph = new_ncph;
nwam_ncp_free(old_ncph);
current_ncu_priority_group = INVALID_PRIORITY_GROUP;
(void) strlcpy(active_ncp, event->event_object,
sizeof (active_ncp));
(void) pthread_mutex_unlock(&active_ncp_mutex);
(void) nwamd_set_string_property(OUR_FMRI, OUR_PG,
OUR_ACTIVE_NCP_PROP_NAME, new_ncp);
(void) smf_refresh_instance(OUR_FMRI);
initial_ncp_enable = B_FALSE;
}
int
nwamd_ncp_action(const char *ncp, nwam_action_t action)
{
nwamd_event_t event = nwamd_event_init_object_action
(NWAM_OBJECT_TYPE_NCP, ncp, NULL, action);
if (event == NULL)
return (1);
nwamd_event_enqueue(event);
return (0);
}
struct priority_group_cbarg {
uint64_t minpriority;
uint64_t currpriority;
boolean_t found;
};
static int
find_next_priority_group_cb(nwamd_object_t object, void *data)
{
struct priority_group_cbarg *cbarg = data;
uint64_t priority;
nwamd_ncu_t *ncu = object->nwamd_object_data;
if (ncu->ncu_link.nwamd_link_activation_mode !=
NWAM_ACTIVATION_MODE_PRIORITIZED)
return (0);
priority = ncu->ncu_link.nwamd_link_priority_group;
if (priority >= cbarg->minpriority && priority < cbarg->currpriority) {
cbarg->found = B_TRUE;
cbarg->currpriority = priority;
}
return (0);
}
boolean_t
nwamd_ncp_find_next_priority_group(int64_t minpriority,
int64_t *nextpriorityp)
{
struct priority_group_cbarg cbarg;
cbarg.minpriority = minpriority;
cbarg.currpriority = MAXINT;
cbarg.found = B_FALSE;
(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
find_next_priority_group_cb, &cbarg);
if (cbarg.found) {
nlog(LOG_DEBUG, "nwamd_ncp_find_next_priority_group: "
"next priority group >= %lld is %lld",
minpriority, cbarg.currpriority);
*nextpriorityp = cbarg.currpriority;
return (B_TRUE);
} else {
nlog(LOG_DEBUG, "nwamd_ncp_find_next_priority_group: "
"no priority groups >= %lld exist", minpriority);
return (B_FALSE);
}
}
struct nwamd_ncu_check_walk_arg {
boolean_t manual;
int64_t priority_group;
uint64_t exclusive_ncus;
uint64_t exclusive_online_ncus;
uint64_t shared_ncus;
uint64_t shared_online_ncus;
uint64_t all_ncus;
uint64_t all_online_ncus;
boolean_t activate_or_deactivate;
boolean_t activate;
};
static int
nwamd_ncu_check_or_activate(nwamd_object_t object, void *data)
{
struct nwamd_ncu_check_walk_arg *wa = data;
nwamd_ncu_t *ncu;
uint64_t priority_group, priority_mode;
nwamd_object_t if_obj;
nwam_state_t state, if_state;
nwam_aux_state_t aux_state, if_aux_state;
char *name;
state = object->nwamd_object_state;
aux_state = object->nwamd_object_aux_state;
name = object->nwamd_object_name;
ncu = object->nwamd_object_data;
if (state == NWAM_STATE_UNINITIALIZED) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"skipping uninitialized ncu %s", name);
return (0);
}
if (!wa->manual && wa->priority_group == INVALID_PRIORITY_GROUP)
return (0);
if (ncu->ncu_type != NWAM_NCU_TYPE_LINK) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"skipping interface NCU %s", name);
return (0);
}
if (!wa->manual && ncu->ncu_link.nwamd_link_activation_mode !=
NWAM_ACTIVATION_MODE_PRIORITIZED) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"skipping non-prioritized NCU %s", name);
return (0);
}
if (wa->manual && ncu->ncu_link.nwamd_link_activation_mode !=
NWAM_ACTIVATION_MODE_MANUAL) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"skipping non-manual NCU %s", name);
return (0);
}
priority_group = ncu->ncu_link.nwamd_link_priority_group;
priority_mode = ncu->ncu_link.nwamd_link_priority_mode;
if (!wa->manual && priority_group != wa->priority_group) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"skipping NCU %s in different priority-group", name);
return (0);
}
if ((if_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE,
ncu->ncu_name)) == NULL) {
nlog(LOG_ERR, "nwamd_ncu_check_or_activate: "
"interface NCU of %s not found, skipping", name);
return (0);
}
if_state = if_obj->nwamd_object_state;
if_aux_state = if_obj->nwamd_object_aux_state;
nwamd_object_release(if_obj);
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: %s ncu %s",
wa->activate_or_deactivate ?
(wa->activate ? "activating" : "deactivating") :
"checking", name);
if (wa->manual) {
if (wa->activate_or_deactivate && wa->activate) {
if (state == NWAM_STATE_OFFLINE && ncu->ncu_enabled) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"moving NCU %s to offline* from offline",
name);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
name, NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_INITIALIZED);
}
if (state != NWAM_STATE_DISABLED &&
!ncu->ncu_enabled) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"moving NCU %s to online* (disabling)",
name);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
name, NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_MANUAL_DISABLE);
}
}
return (0);
}
switch (priority_mode) {
case NWAM_PRIORITY_MODE_EXCLUSIVE:
wa->exclusive_ncus++;
if (state == NWAM_STATE_ONLINE &&
(if_state == NWAM_STATE_ONLINE ||
if_aux_state == NWAM_AUX_STATE_IF_WAITING_FOR_ADDR))
wa->exclusive_online_ncus++;
if (wa->activate_or_deactivate && wa->activate) {
if (state == NWAM_STATE_OFFLINE &&
wa->exclusive_online_ncus == 0) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"moving NCU %s to offline* from offline",
name);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
name, NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_INITIALIZED);
}
}
if (wa->activate_or_deactivate && !wa->activate) {
if (aux_state != NWAM_AUX_STATE_CONDITIONS_NOT_MET) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"deactivating NCU %s", name);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
name, NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_CONDITIONS_NOT_MET);
}
}
if ((wa->activate_or_deactivate && wa->activate) ||
!wa->activate_or_deactivate) {
if (state == NWAM_STATE_ONLINE &&
if_state == NWAM_STATE_ONLINE &&
wa->exclusive_online_ncus > 1) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"moving NCU %s to online* since another "
"NCU is already active",
name);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
name, NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_CONDITIONS_NOT_MET);
}
}
break;
case NWAM_PRIORITY_MODE_SHARED:
wa->shared_ncus++;
if (state == NWAM_STATE_ONLINE &&
(if_state == NWAM_STATE_ONLINE ||
if_aux_state == NWAM_AUX_STATE_IF_WAITING_FOR_ADDR))
wa->shared_online_ncus++;
if (wa->activate_or_deactivate && wa->activate) {
if (state == NWAM_STATE_OFFLINE) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"activating NCU %s", name);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
name, NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_INITIALIZED);
}
}
if (wa->activate_or_deactivate && !wa->activate) {
if (aux_state != NWAM_AUX_STATE_CONDITIONS_NOT_MET) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"deactivating NCU %s", name);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
name, NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_CONDITIONS_NOT_MET);
}
}
break;
case NWAM_PRIORITY_MODE_ALL:
wa->all_ncus++;
if (state == NWAM_STATE_ONLINE &&
(if_state == NWAM_STATE_ONLINE ||
if_aux_state == NWAM_AUX_STATE_IF_WAITING_FOR_ADDR))
wa->all_online_ncus++;
if (wa->activate_or_deactivate && wa->activate) {
if (state == NWAM_STATE_OFFLINE) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"activating NCU %s", name);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
name, NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_INITIALIZED);
}
}
if (wa->activate_or_deactivate && !wa->activate) {
if (aux_state != NWAM_AUX_STATE_CONDITIONS_NOT_MET) {
nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
"deactivating NCU %s", name);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
name, NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_CONDITIONS_NOT_MET);
}
}
break;
default:
nlog(LOG_ERR, "nwamd_ncu_check_or_activate: "
"invalid priority-mode");
break;
}
return (0);
}
void
nwamd_ncp_activate_priority_group(int64_t priority)
{
struct nwamd_ncu_check_walk_arg wa;
nwamd_event_t check_event, priority_event;
if (priority == INVALID_PRIORITY_GROUP)
return;
(void) pthread_mutex_lock(&active_ncp_mutex);
if (priority == current_ncu_priority_group) {
(void) pthread_mutex_unlock(&active_ncp_mutex);
return;
}
(void) pthread_mutex_unlock(&active_ncp_mutex);
nlog(LOG_DEBUG, "nwamd_ncp_activate_priority_group: "
"activating priority group %lld", priority);
wa.manual = B_FALSE;
wa.priority_group = priority;
wa.exclusive_ncus = 0;
wa.exclusive_online_ncus = 0;
wa.shared_ncus = 0;
wa.shared_online_ncus = 0;
wa.all_ncus = 0;
wa.all_online_ncus = 0;
wa.activate_or_deactivate = B_TRUE;
wa.activate = B_TRUE;
if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
nwamd_ncu_check_or_activate, &wa) != 0) {
nlog(LOG_ERR, "nwamd_ncp_activate_priority_group: "
"nwamd_walk_objects() failed");
return;
}
priority_event = nwamd_event_init_priority_group_change(priority);
if (priority_event == NULL)
return;
nwamd_event_enqueue(priority_event);
check_event = nwamd_event_init_ncu_check();
if (check_event == NULL)
return;
nwamd_event_enqueue_timed(check_event, ncu_wait_time);
}
void
nwamd_ncp_deactivate_priority_group(int64_t priority)
{
struct nwamd_ncu_check_walk_arg wa;
if (priority == INVALID_PRIORITY_GROUP)
return;
nlog(LOG_DEBUG, "nwamd_ncp_deactivate_priority_group: "
"deactivating priority group %lld", priority);
wa.manual = B_FALSE;
wa.priority_group = priority;
wa.exclusive_ncus = 0;
wa.exclusive_online_ncus = 0;
wa.shared_ncus = 0;
wa.shared_online_ncus = 0;
wa.all_ncus = 0;
wa.all_online_ncus = 0;
wa.activate_or_deactivate = B_TRUE;
wa.activate = B_FALSE;
if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
nwamd_ncu_check_or_activate, &wa) != 0) {
nlog(LOG_ERR, "nwamd_ncp_deactivate_priority_group: "
"nwamd_walk_objects() failed");
return;
}
}
void
nwamd_ncp_deactivate_priority_group_all(int64_t priority)
{
if (priority == INVALID_PRIORITY_GROUP)
return;
nlog(LOG_DEBUG, "nwamd_ncp_deactivate_priority_group_all: "
"deactivating priority group less than or equal to %lld", priority);
do {
nwamd_ncp_deactivate_priority_group(priority);
} while (nwamd_ncp_find_next_priority_group(priority + 1, &priority));
}
boolean_t
nwamd_ncp_check_priority_group(int64_t *priority)
{
struct nwamd_ncu_check_walk_arg wa;
boolean_t conditions_met = B_FALSE;
nlog(LOG_DEBUG, "nwamd_ncp_check_priority_group: "
"checking priority group %lld", *priority);
if (*priority == INVALID_PRIORITY_GROUP) {
if (!nwamd_ncp_find_next_priority_group(0, priority))
return (B_FALSE);
}
while (!conditions_met) {
(void) memset(&wa, 0, sizeof (wa));
wa.manual = B_FALSE;
wa.priority_group = *priority;
wa.activate_or_deactivate = B_FALSE;
if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
nwamd_ncu_check_or_activate, &wa) != 0) {
nlog(LOG_ERR, "nwamd_ncp_check_priority_group: "
"nwamd_walk_objects() failed");
return (B_FALSE);
}
conditions_met = B_TRUE;
if (wa.exclusive_ncus > 0 && wa.exclusive_online_ncus != 1)
conditions_met = B_FALSE;
if (wa.shared_ncus > 0 && wa.shared_online_ncus == 0)
conditions_met = B_FALSE;
if (wa.all_ncus > 0 && wa.all_ncus != wa.all_online_ncus)
conditions_met = B_FALSE;
if (wa.exclusive_online_ncus == 0 &&
wa.shared_online_ncus == 0 && wa.all_online_ncus == 0)
conditions_met = B_FALSE;
if (conditions_met) {
return (B_TRUE);
} else {
if (!nwamd_ncp_find_next_priority_group
(wa.priority_group + 1, priority)) {
nlog(LOG_DEBUG, "ran out of prio groups");
return (B_FALSE);
}
}
}
return (B_FALSE);
}
void
nwamd_ncp_activate_manual_ncus(void)
{
struct nwamd_ncu_check_walk_arg wa;
nlog(LOG_DEBUG, "nwamd_ncp_activate_manual_ncus: activating NCUs");
wa.manual = B_TRUE;
wa.activate_or_deactivate = B_TRUE;
wa.activate = B_TRUE;
if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
nwamd_ncu_check_or_activate, &wa) != 0) {
nlog(LOG_ERR, "nwamd_ncp_activate_manual_ncus: "
"nwamd_walk_objects() failed");
return;
}
}
void
nwamd_create_ncu_check_event(uint64_t when)
{
nwamd_event_t check_event = nwamd_event_init_ncu_check();
if (check_event != NULL)
nwamd_event_enqueue_timed(check_event, when);
}