#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <execinfo.h>
#include <kstat.h>
#include <libdladm.h>
#include <libdllink.h>
#include <libdlstat.h>
#include <libdlwlan.h>
#include <libnwam.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <libdlpi.h>
#include <ucontext.h>
#include "events.h"
#include "llp.h"
#include "objects.h"
#include "ncp.h"
#include "ncu.h"
#include "known_wlans.h"
#include "util.h"
link_state_t
nwamd_get_link_state(const char *name)
{
kstat_ctl_t *kcp;
kstat_t *ksp;
char module[DLPI_LINKNAME_MAX];
uint_t instance;
link_state_t link_state = LINK_STATE_UNKNOWN;
if ((kcp = kstat_open()) == NULL)
return (link_state);
if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS)
goto out;
if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL) {
goto out;
}
if (kstat_read(kcp, ksp, NULL) == -1)
goto out;
(void) dladm_kstat_value(ksp, "link_state", KSTAT_DATA_UINT32,
&link_state);
out:
(void) kstat_close(kcp);
return (link_state);
}
void
nwamd_set_unset_link_properties(nwamd_ncu_t *ncu, boolean_t set)
{
dlpi_handle_t dh = ncu->ncu_link.nwamd_link_dhp;
char *addr = set ? ncu->ncu_link.nwamd_link_mac_addr : NULL;
uint64_t mtu = set ? ncu->ncu_link.nwamd_link_mtu : 0;
char **autopush = set ? ncu->ncu_link.nwamd_link_autopush : NULL;
uint_t num_autopush = set ? ncu->ncu_link.nwamd_link_num_autopush : 0;
uchar_t *hwaddr = NULL, curraddr[DLPI_PHYSADDR_MAX];
size_t hwaddrlen = DLPI_PHYSADDR_MAX;
int retval;
dladm_status_t status;
char mtustr[DLADM_PROP_VAL_MAX];
char *cp;
char errmsg[DLADM_STRSIZE];
uint_t cnt = 1;
if (mtu == 0) {
cp = mtustr;
status = dladm_get_linkprop(dld_handle,
ncu->ncu_link.nwamd_link_id, DLADM_PROP_VAL_DEFAULT, "mtu",
&cp, &cnt);
if (status != DLADM_STATUS_OK) {
nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
"dladm_get_linkprop failed: %s",
dladm_status2str(status, errmsg));
return;
}
} else {
(void) snprintf(mtustr, DLADM_PROP_VAL_MAX, "%lld", mtu);
}
cp = mtustr;
nlog(LOG_DEBUG, "nwamd_set_unset_link_properties: setting MTU of %s "
"for link %s", mtustr, ncu->ncu_name);
status = dladm_set_linkprop(dld_handle, ncu->ncu_link.nwamd_link_id,
"mtu", &cp, 1, DLADM_OPT_ACTIVE);
if (status != DLADM_STATUS_OK) {
nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
"dladm_set_linkprop failed: %s",
dladm_status2str(status, errmsg));
}
nlog(LOG_DEBUG, "nwamd_set_unset_link_properties: setting %d "
"autopush module for link %s", num_autopush, ncu->ncu_name);
status = dladm_set_linkprop(dld_handle, ncu->ncu_link.nwamd_link_id,
"autopush", autopush, num_autopush, DLADM_OPT_ACTIVE);
if (status != DLADM_STATUS_OK) {
nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
"dladm_set_linkprop failed for autopush property: %s",
dladm_status2str(status, errmsg));
}
if (addr == NULL) {
if ((hwaddr = calloc(1, DLPI_PHYSADDR_MAX)) == NULL) {
nlog(LOG_ERR,
"nwamd_set_unset_link_properties: malloc() failed");
return;
}
if ((retval = dlpi_get_physaddr(dh, DL_FACT_PHYS_ADDR,
hwaddr, &hwaddrlen)) != DLPI_SUCCESS) {
nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
"could not get physical address for %s: %s",
ncu->ncu_name, dlpi_strerror(retval));
free(hwaddr);
return;
}
} else {
int addrlen = hwaddrlen;
if ((hwaddr = _link_aton(addr, &addrlen)) == NULL) {
if (addrlen == -1) {
nlog(LOG_ERR,
"nwamd_set_unset_link_properties: "
"%s: bad address for %s",
addr, ncu->ncu_name);
return;
} else {
nlog(LOG_ERR, "nwamd_set_unset_link_properties:"
" malloc() failed");
return;
}
}
hwaddrlen = addrlen;
}
retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, curraddr,
&hwaddrlen);
if (retval != DLPI_SUCCESS || bcmp(curraddr, hwaddr, hwaddrlen) != 0) {
retval = dlpi_set_physaddr(dh, DL_CURR_PHYS_ADDR, hwaddr,
hwaddrlen);
if (retval != DLPI_SUCCESS) {
nlog(LOG_ERR, "nwamd_set_unset_link_properties:"
"failed setting mac address on %s: %s",
ncu->ncu_name, dlpi_strerror(retval));
}
}
free(hwaddr);
}
#define WLAN_ENC(sec) \
((sec == DLADM_WLAN_SECMODE_WPA ? "WPA" : \
(sec == DLADM_WLAN_SECMODE_WEP ? "WEP" : "none")))
#define NEED_ENC(sec) \
(sec == DLADM_WLAN_SECMODE_WPA || sec == DLADM_WLAN_SECMODE_WEP)
#define WIRELESS_LAN_INIT_COUNT 8
dladm_wlan_strength_t wireless_scan_level = DLADM_WLAN_STRENGTH_WEAK;
uint64_t wireless_scan_interval = WIRELESS_SCAN_INTERVAL_DEFAULT;
boolean_t wireless_autoconf = B_FALSE;
boolean_t wireless_strict_bssid = B_FALSE;
pthread_mutex_t wireless_mutex = PTHREAD_MUTEX_INITIALIZER;
static void
scanconnect_entry(void)
{
(void) pthread_mutex_lock(&wireless_mutex);
}
static void
scanconnect_exit(void)
{
(void) pthread_mutex_unlock(&wireless_mutex);
}
static int
key_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp,
dladm_secobj_class_t class)
{
size_t buf_len = strlen(buf);
nlog(LOG_DEBUG, "before: key_string_to_secobj_value: buf_len = %d",
buf_len);
if (buf_len == 0) {
return (0);
}
if (buf[buf_len - 1] == '\n')
buf[--buf_len] = '\0';
nlog(LOG_DEBUG, "after: key_string_to_secobj_value: buf_len = %d",
buf_len);
if (class == DLADM_SECOBJ_CLASS_WPA) {
if (buf_len < 8 || buf_len > 63) {
nlog(LOG_ERR,
"key_string_to_secobj_value:"
" invalid WPA key length: buf_len = %d", buf_len);
return (-1);
}
(void) memcpy(obj_val, buf, (uint_t)buf_len);
*obj_lenp = buf_len;
return (0);
}
switch (buf_len) {
case 5:
case 13:
(void) memcpy(obj_val, buf, (uint_t)buf_len);
*obj_lenp = (uint_t)buf_len;
break;
case 10:
case 26:
if (hexascii_to_octet(buf, (uint_t)buf_len, obj_val, obj_lenp)
!= 0) {
nlog(LOG_ERR,
"key_string_to_secobj_value: invalid WEP key");
return (-1);
}
break;
case 12:
case 28:
if (strncmp(buf, "0x", 2) != 0 ||
hexascii_to_octet(buf + 2, (uint_t)buf_len - 2, obj_val,
obj_lenp) != 0) {
nlog(LOG_ERR,
"key_string_to_secobj_value: invalid WEP key");
return (-1);
}
break;
default:
syslog(LOG_ERR,
"key_string_to_secobj_value: invalid WEP key length");
return (-1);
}
return (0);
}
static int
find_keyname_cb(nwam_known_wlan_handle_t kwh, void *new_keyname)
{
nwam_error_t err;
nwam_value_t old_key;
char **old_keyname;
uint_t num_old_keyname, i;
if ((err = nwam_known_wlan_get_prop_value(kwh,
NWAM_KNOWN_WLAN_PROP_KEYNAME, &old_key)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "find_keyname_cb: nwam_known_wlan_get_prop: %s",
nwam_strerror(err));
return (0);
}
if ((err = nwam_value_get_string_array(old_key, &old_keyname,
&num_old_keyname))
!= NWAM_SUCCESS) {
nlog(LOG_ERR, "find_keyname_cb: nwam_value_get_string: %s",
nwam_strerror(err));
nwam_value_free(old_key);
return (0);
}
nwam_value_free(old_key);
for (i = 0; i < num_old_keyname; i++) {
if (strcmp(old_keyname[i], (const char *)new_keyname) == 0)
return (1);
}
return (0);
}
void
nwamd_set_key_name(const char *essid, const char *bssid, char *name, size_t nsz)
{
int i, j;
char secobj_name[DLADM_WLAN_MAX_KEYNAME_LEN];
if (bssid == NULL || bssid[0] == '\0') {
(void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s",
essid);
} else {
(void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s-%s",
essid, bssid);
}
i = 0;
j = 0;
while (secobj_name[i] != '\0') {
if (j == nsz - 1)
break;
if (secobj_name[i] == ':') {
name[j] = '.';
j++;
} else if (isalnum(secobj_name[i]) ||
secobj_name[i] == '.' || secobj_name[i] == '-' ||
secobj_name[i] == '_') {
name[j] = secobj_name[i];
j++;
}
i++;
}
name[j] = '\0';
}
nwam_error_t
nwamd_wlan_set_key(const char *linkname, const char *essid, const char *bssid,
uint32_t security_mode, uint_t keyslot, char *raw_key)
{
nwamd_object_t ncu_obj;
nwamd_ncu_t *ncu;
nwamd_link_t *link;
int ret = 0;
uint8_t obj_val[DLADM_SECOBJ_VAL_MAX];
uint_t obj_len = sizeof (obj_val);
char obj_name[DLADM_SECOBJ_NAME_MAX];
char obj_tempname[DLADM_SECOBJ_NAME_MAX];
dladm_status_t status;
char errmsg[DLADM_STRSIZE];
dladm_secobj_class_t class;
if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
== NULL) {
nlog(LOG_ERR, "nwamd_wlan_set_key: could not find object "
"for link %s", linkname);
return (NWAM_ENTITY_NOT_FOUND);
}
ncu = ncu_obj->nwamd_object_data;
link = &ncu->ncu_link;
class = (security_mode == DLADM_WLAN_SECMODE_WEP ?
DLADM_SECOBJ_CLASS_WEP : DLADM_SECOBJ_CLASS_WPA);
if (key_string_to_secobj_value(raw_key, obj_val, &obj_len,
class) != 0) {
nwamd_object_release(ncu_obj);
return (NWAM_ERROR_INTERNAL);
}
nlog(LOG_DEBUG, "nwamd_wlan_set_key: running for link %s", linkname);
nwamd_set_key_name(essid, NULL, obj_tempname, sizeof (obj_tempname));
(void) nwam_walk_known_wlans(find_keyname_cb, obj_tempname, 0, &ret);
if (ret == 1) {
dladm_wlan_key_t *old_secobj = nwamd_wlan_get_key_named(
obj_tempname, security_mode);
nlog(LOG_DEBUG, "found existing obj_name %s", obj_tempname);
ret = memcmp((*old_secobj).wk_val, obj_val, obj_len);
nwamd_set_key_name(essid, ret ? bssid : NULL, obj_name,
sizeof (obj_name));
free(old_secobj);
} else {
nwamd_set_key_name(essid, NULL, obj_name,
sizeof (obj_name));
}
nlog(LOG_DEBUG, "store_key: obj_name is %s", obj_name);
status = dladm_unset_secobj(dld_handle, obj_name,
DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
if (status != DLADM_STATUS_OK && status != DLADM_STATUS_NOTFOUND) {
nlog(LOG_ERR, "store_key: could not remove old secure object "
"'%s' for key: %s", obj_name,
dladm_status2str(status, errmsg));
nwamd_object_release(ncu_obj);
return (NWAM_ERROR_INTERNAL);
}
if (raw_key[0] == '\0') {
nwamd_object_release(ncu_obj);
return (NWAM_SUCCESS);
}
status = dladm_set_secobj(dld_handle, obj_name, class,
obj_val, obj_len,
DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_ACTIVE);
if (status != DLADM_STATUS_OK) {
nlog(LOG_ERR, "store_key: could not create secure object "
"'%s' for key: %s", obj_name,
dladm_status2str(status, errmsg));
nwamd_object_release(ncu_obj);
return (NWAM_ERROR_INTERNAL);
}
link->nwamd_link_wifi_key = nwamd_wlan_get_key_named(obj_name,
security_mode);
(void) strlcpy(link->nwamd_link_wifi_keyname, obj_name,
sizeof (link->nwamd_link_wifi_keyname));
link->nwamd_link_wifi_security_mode = security_mode;
if (security_mode == DLADM_WLAN_SECMODE_WEP) {
link->nwamd_link_wifi_key->wk_idx =
(keyslot >= 1 && keyslot <= 4) ? keyslot : 1;
}
switch (ncu_obj->nwamd_object_state) {
case NWAM_STATE_ONLINE:
if (strcmp(essid, link->nwamd_link_wifi_essid) == 0)
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name, NWAM_STATE_ONLINE,
NWAM_AUX_STATE_LINK_WIFI_CONNECTING);
break;
case NWAM_STATE_OFFLINE_TO_ONLINE:
if (ncu_obj->nwamd_object_aux_state ==
NWAM_AUX_STATE_LINK_WIFI_NEED_KEY)
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_LINK_WIFI_CONNECTING);
break;
default:
break;
}
nwamd_object_release(ncu_obj);
return (NWAM_SUCCESS);
}
dladm_wlan_key_t *
nwamd_wlan_get_key_named(const char *name, uint32_t security_mode)
{
dladm_status_t status;
char errmsg[DLADM_STRSIZE];
dladm_wlan_key_t *cooked_key;
dladm_secobj_class_t class;
if (security_mode == DLADM_WLAN_SECMODE_NONE)
return (NULL);
if ((cooked_key = malloc(sizeof (dladm_wlan_key_t))) == NULL) {
nlog(LOG_ERR, "nwamd_wlan_get_key_named: malloc failed");
return (NULL);
}
(void) strlcpy(cooked_key->wk_name, name, DLADM_SECOBJ_NAME_MAX);
nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: len = %d, object = %s\n",
strlen(cooked_key->wk_name), cooked_key->wk_name);
cooked_key->wk_len = sizeof (cooked_key->wk_val);
cooked_key->wk_idx = 1;
status = dladm_get_secobj(dld_handle, cooked_key->wk_name, &class,
cooked_key->wk_val, &cooked_key->wk_len,
DLADM_OPT_ACTIVE);
if (status != DLADM_STATUS_OK) {
nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: "
"dladm_get_secobj(TEMP) failed: %s",
dladm_status2str(status, errmsg));
status = dladm_get_secobj(dld_handle, cooked_key->wk_name,
&class, cooked_key->wk_val, &cooked_key->wk_len,
DLADM_OPT_PERSIST);
}
switch (status) {
case DLADM_STATUS_OK:
nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: "
"dladm_get_secobj succeeded: len %d", cooked_key->wk_len);
break;
case DLADM_STATUS_NOTFOUND:
free(cooked_key);
return (NULL);
default:
nlog(LOG_ERR, "nwamd_wlan_get_key_named: could not get key "
"from secure object '%s': %s", cooked_key->wk_name,
dladm_status2str(status, errmsg));
free(cooked_key);
return (NULL);
}
if (security_mode != 0) {
switch (class) {
case DLADM_SECOBJ_CLASS_WEP:
if (security_mode == DLADM_WLAN_SECMODE_WEP)
return (cooked_key);
break;
case DLADM_SECOBJ_CLASS_WPA:
if (security_mode == DLADM_WLAN_SECMODE_WPA)
return (cooked_key);
break;
default:
nlog(LOG_ERR, "nwamd_wlan_get_key: invalid class %d",
class);
break;
}
nlog(LOG_ERR, "nwamd_wlan_get_key: key type mismatch"
" from secure object '%s'", cooked_key->wk_name);
free(cooked_key);
return (NULL);
}
return (cooked_key);
}
static dladm_wlan_key_t *
nwamd_wlan_get_key(const char *essid, const char *bssid, uint32_t security_mode)
{
char keyname[DLADM_SECOBJ_NAME_MAX];
nwamd_set_key_name(essid, bssid, keyname, DLADM_SECOBJ_NAME_MAX);
return (nwamd_wlan_get_key_named(keyname, security_mode));
}
static boolean_t
wireless_selection_possible(nwamd_object_t object)
{
nwamd_ncu_t *ncu = object->nwamd_object_data;
if (ncu->ncu_link.nwamd_link_media != DL_WIFI)
return (B_FALSE);
(void) pthread_mutex_lock(&active_ncp_mutex);
if (object->nwamd_object_state == NWAM_STATE_DISABLED ||
((object->nwamd_object_state == NWAM_STATE_OFFLINE ||
object->nwamd_object_state == NWAM_STATE_ONLINE_TO_OFFLINE) &&
ncu->ncu_link.nwamd_link_activation_mode ==
NWAM_ACTIVATION_MODE_PRIORITIZED &&
(current_ncu_priority_group == INVALID_PRIORITY_GROUP ||
ncu->ncu_link.nwamd_link_priority_group >
current_ncu_priority_group))) {
(void) pthread_mutex_unlock(&active_ncp_mutex);
return (B_FALSE);
}
(void) pthread_mutex_unlock(&active_ncp_mutex);
return (B_TRUE);
}
void
nwamd_set_selected_connected(nwamd_ncu_t *ncu, boolean_t selected,
boolean_t connected)
{
nwamd_link_t *link = &ncu->ncu_link;
nwamd_wifi_scan_t *s = &link->nwamd_link_wifi_scan;
int i;
boolean_t trigger_scan_event = B_FALSE;
for (i = 0; i < s->nwamd_wifi_scan_curr_num; i++) {
if (strcmp(s->nwamd_wifi_scan_curr[i].nww_essid,
link->nwamd_link_wifi_essid) != 0 ||
(link->nwamd_link_wifi_bssid[0] != '\0' &&
strcmp(s->nwamd_wifi_scan_curr[i].nww_bssid,
link->nwamd_link_wifi_bssid) != 0))
continue;
if (selected) {
if (!s->nwamd_wifi_scan_curr[i].nww_selected)
trigger_scan_event = B_TRUE;
s->nwamd_wifi_scan_curr[i].nww_selected = B_TRUE;
} else {
if (s->nwamd_wifi_scan_curr[i].nww_selected)
trigger_scan_event = B_TRUE;
s->nwamd_wifi_scan_curr[i].nww_selected = B_FALSE;
}
if (connected) {
if (!s->nwamd_wifi_scan_curr[i].nww_connected)
trigger_scan_event = B_TRUE;
s->nwamd_wifi_scan_curr[i].nww_connected = B_TRUE;
} else {
if (s->nwamd_wifi_scan_curr[i].nww_connected)
trigger_scan_event = B_TRUE;
s->nwamd_wifi_scan_curr[i].nww_connected = B_FALSE;
}
}
if (trigger_scan_event || s->nwamd_wifi_scan_changed) {
nwamd_event_t scan_event = nwamd_event_init_wlan
(ncu->ncu_name, NWAM_EVENT_TYPE_WLAN_SCAN_REPORT, connected,
s->nwamd_wifi_scan_curr, s->nwamd_wifi_scan_curr_num);
if (scan_event != NULL) {
s->nwamd_wifi_scan_changed = B_FALSE;
nwamd_event_enqueue(scan_event);
}
}
}
static int
find_bssid_cb(nwam_known_wlan_handle_t kwh, void *data)
{
nwamd_link_t *link = data;
nwam_error_t err;
nwam_value_t bssidval;
char **bssids, *name;
uint_t num_bssids, i;
if ((err = nwam_known_wlan_get_prop_value(kwh,
NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidval)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "find_bssid_cb: nwam_known_wlan_get_prop: %s",
nwam_strerror(err));
return (0);
}
if ((err = nwam_value_get_string_array(bssidval, &bssids, &num_bssids))
!= NWAM_SUCCESS) {
nlog(LOG_ERR, "find_bssid_cb: nwam_value_get_string_array: %s",
nwam_strerror(err));
nwam_value_free(bssidval);
return (0);
}
for (i = 0; i < num_bssids; i++) {
if (strcmp(bssids[i], link->nwamd_link_wifi_bssid) == 0) {
if ((err = nwam_known_wlan_get_name(kwh, &name))
!= NWAM_SUCCESS) {
nlog(LOG_ERR, "find_bssid_cb: "
"nwam_known_wlan_get_name: %s",
nwam_strerror(err));
continue;
}
(void) strlcpy(link->nwamd_link_wifi_essid, name,
sizeof (link->nwamd_link_wifi_essid));
free(name);
nwam_value_free(bssidval);
return (1);
}
}
nwam_value_free(bssidval);
return (0);
}
static void
check_if_hidden_wlan_was_visited(nwamd_link_t *link)
{
(void) nwam_walk_known_wlans(find_bssid_cb, link,
NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, NULL);
}
nwam_error_t
nwamd_wlan_select(const char *linkname, const char *essid, const char *bssid,
uint32_t security_mode, boolean_t add_to_known_wlans)
{
nwamd_object_t ncu_obj;
nwamd_ncu_t *ncu;
nwamd_link_t *link;
boolean_t found_key = B_FALSE;
if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
== NULL) {
nlog(LOG_ERR, "nwamd_wlan_select: could not find object "
"for link %s", linkname);
return (NWAM_ENTITY_NOT_FOUND);
}
ncu = ncu_obj->nwamd_object_data;
link = &ncu->ncu_link;
if (!wireless_selection_possible(ncu_obj)) {
nwamd_object_release(ncu_obj);
return (NWAM_ENTITY_INVALID_STATE);
}
nwamd_set_selected_connected(ncu, B_FALSE, B_FALSE);
(void) dladm_wlan_disconnect(dld_handle, link->nwamd_link_id);
(void) strlcpy(link->nwamd_link_wifi_essid, essid,
sizeof (link->nwamd_link_wifi_essid));
(void) strlcpy(link->nwamd_link_wifi_bssid, bssid,
sizeof (link->nwamd_link_wifi_bssid));
link->nwamd_link_wifi_security_mode = security_mode;
link->nwamd_link_wifi_add_to_known_wlans = add_to_known_wlans;
if (link->nwamd_link_wifi_essid[0] == '\0')
check_if_hidden_wlan_was_visited(link);
nwamd_set_selected_connected(ncu, B_TRUE, B_FALSE);
if (NEED_ENC(link->nwamd_link_wifi_security_mode)) {
if ((link->nwamd_link_wifi_key = nwamd_wlan_get_key
(link->nwamd_link_wifi_essid, link->nwamd_link_wifi_bssid,
link->nwamd_link_wifi_security_mode)) != NULL) {
nwamd_set_key_name(link->nwamd_link_wifi_essid,
link->nwamd_link_wifi_bssid,
link->nwamd_link_wifi_keyname,
DLADM_SECOBJ_NAME_MAX);
nlog(LOG_DEBUG, "nwamd_wlan_select: got old format "
"WLAN key %s",
link->nwamd_link_wifi_keyname);
found_key = B_TRUE;
} else if ((link->nwamd_link_wifi_key = nwamd_wlan_get_key
(link->nwamd_link_wifi_essid, NULL,
link->nwamd_link_wifi_security_mode)) != NULL) {
nwamd_set_key_name(link->nwamd_link_wifi_essid, NULL,
link->nwamd_link_wifi_keyname,
DLADM_SECOBJ_NAME_MAX);
nlog(LOG_DEBUG, "nwamd_wlan_select: got WLAN key %s",
link->nwamd_link_wifi_keyname);
found_key = B_TRUE;
} else {
nlog(LOG_ERR, "nwamd_wlan_select: could not "
"find key for WLAN '%s'",
link->nwamd_link_wifi_essid);
}
} else {
free(link->nwamd_link_wifi_key);
link->nwamd_link_wifi_key = NULL;
link->nwamd_link_wifi_keyname[0] = '\0';
}
if (NEED_ENC(link->nwamd_link_wifi_security_mode) && !found_key) {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_LINK_WIFI_NEED_KEY);
} else {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name, NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_LINK_WIFI_CONNECTING);
}
nwamd_object_release(ncu_obj);
return (NWAM_SUCCESS);
}
static int
bssid_match(nwam_known_wlan_handle_t kwh, void *bssid)
{
nwam_value_t bssidsval;
nwam_error_t err;
char **bssids;
uint_t nelem, i;
int found = 0;
if ((err = nwam_known_wlan_get_prop_value(kwh,
NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidsval)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "bssid_match: %s", nwam_strerror(err));
return (0);
}
if ((err = nwam_value_get_string_array(bssidsval, &bssids, &nelem))
!= NWAM_SUCCESS) {
nwam_value_free(bssidsval);
return (0);
}
for (i = 0; i < nelem; i++) {
if (strcmp((const char *)bssid, bssids[i]) == 0) {
found = 1;
break;
}
}
nwam_value_free(bssidsval);
return (found);
}
static int
find_best_wlan_cb(nwam_known_wlan_handle_t kwh, void *data)
{
nwamd_ncu_t *ncu = data;
nwamd_link_t *link = &ncu->ncu_link;
nwamd_wifi_scan_t *s = &link->nwamd_link_wifi_scan;
nwam_error_t err;
char *name = NULL;
int i;
dladm_wlan_strength_t curr_strength = 0;
dladm_wlan_strength_t max_strength = 0;
boolean_t found = B_FALSE;
if ((err = nwam_known_wlan_get_name(kwh, &name)) != NWAM_SUCCESS) {
nlog(LOG_ERR, "find_best_wlan_cb: could not look up name: %s",
nwam_strerror(err));
return (0);
}
if (link->nwamd_link_wifi_connected) {
(void) dladm_wlan_str2strength
(link->nwamd_link_wifi_signal_strength, &curr_strength);
}
if (curr_strength >= wireless_scan_level &&
link->nwamd_link_wifi_connected) {
free(name);
return (1);
}
for (i = 0; i < s->nwamd_wifi_scan_curr_num; i++) {
nwam_wlan_t *cur_wlan = &(s->nwamd_wifi_scan_curr[i]);
int b_match = bssid_match(kwh, cur_wlan->nww_bssid);
if (strcmp(cur_wlan->nww_essid, name) != 0 &&
!(cur_wlan->nww_essid[0] == '\0' && b_match))
continue;
if (wireless_strict_bssid && !b_match)
continue;
(void) dladm_wlan_str2strength
(cur_wlan->nww_signal_strength, &curr_strength);
if (curr_strength > max_strength) {
(void) strlcpy(link->nwamd_link_wifi_essid,
cur_wlan->nww_essid,
sizeof (link->nwamd_link_wifi_essid));
if (wireless_strict_bssid ||
cur_wlan->nww_essid[0] == '\0') {
(void) strlcpy(link->nwamd_link_wifi_bssid,
cur_wlan->nww_bssid,
sizeof (link->nwamd_link_wifi_bssid));
}
(void) strlcpy(link->nwamd_link_wifi_signal_strength,
cur_wlan->nww_signal_strength,
sizeof (link->nwamd_link_wifi_signal_strength));
link->nwamd_link_wifi_security_mode =
cur_wlan->nww_security_mode;
found = B_TRUE;
}
(void) dladm_wlan_str2strength
(link->nwamd_link_wifi_signal_strength, &max_strength);
}
free(name);
return (found ? 1 : 0);
}
static boolean_t
nwamd_find_known_wlan(nwamd_object_t ncu_obj)
{
nwamd_ncu_t *ncu = ncu_obj->nwamd_object_data;
int ret;
(void) nwam_walk_known_wlans(find_best_wlan_cb, ncu,
NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, &ret);
return (ret == 1);
}
void
nwamd_ncu_create_periodic_scan_event(nwamd_object_t ncu_obj)
{
nwamd_event_t scan_event;
if (wireless_scan_interval == 0) {
nlog(LOG_DEBUG, "nwamd_ncu_create_periodic_scan_event: "
"wireless_scan_interval set to 0 so no periodic scanning");
return;
}
scan_event = nwamd_event_init(NWAM_EVENT_TYPE_PERIODIC_SCAN,
NWAM_OBJECT_TYPE_NCU, 0, ncu_obj->nwamd_object_name);
if (scan_event != NULL) {
nwamd_event_enqueue_timed(scan_event,
wireless_scan_interval > WIRELESS_SCAN_INTERVAL_MIN ?
wireless_scan_interval : WIRELESS_SCAN_INTERVAL_MIN);
}
}
void
nwamd_ncu_handle_periodic_scan_event(nwamd_event_t event)
{
nwamd_object_t ncu_obj;
nwamd_ncu_t *ncu;
ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
event->event_object);
if (ncu_obj == NULL) {
nlog(LOG_ERR, "nwamd_ncu_handle_periodic_scan_event: "
"no object %s", event->event_object);
return;
}
ncu = ncu_obj->nwamd_object_data;
nlog(LOG_DEBUG, "nwamd_ncu_handle_periodic_scan_event: doing rescan..");
if (ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE ||
ncu_obj->nwamd_object_state == NWAM_STATE_ONLINE) {
(void) nwamd_wlan_scan(ncu->ncu_name);
nwamd_ncu_create_periodic_scan_event(ncu_obj);
}
nwamd_object_release(ncu_obj);
}
static boolean_t
get_scan_results(void *arg, dladm_wlan_attr_t *attrp)
{
nwamd_wifi_scan_t *s = arg;
const char *linkname = s->nwamd_wifi_scan_link;
char essid_name[DLADM_STRSIZE];
char bssid_name[DLADM_STRSIZE];
char strength[DLADM_STRSIZE];
uint_t i, index = 0;
boolean_t found = B_FALSE;
(void) dladm_wlan_essid2str(&attrp->wa_essid, essid_name);
(void) dladm_wlan_bssid2str(&attrp->wa_bssid, bssid_name);
(void) dladm_wlan_strength2str(&attrp->wa_strength, strength);
index = s->nwamd_wifi_scan_curr_num;
if (index == NWAMD_MAX_NUM_WLANS) {
nlog(LOG_ERR, "get_scan_results: truncating WLAN scan results "
"for link %s: ommiting (%s, %s)", linkname, essid_name,
bssid_name);
return (B_TRUE);
}
(void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_essid, essid_name,
sizeof (s->nwamd_wifi_scan_curr[index].nww_essid));
(void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_bssid, bssid_name,
sizeof (s->nwamd_wifi_scan_curr[index].nww_bssid));
(void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_signal_strength,
strength,
sizeof (s->nwamd_wifi_scan_curr[index].nww_signal_strength));
s->nwamd_wifi_scan_curr[index].nww_security_mode = attrp->wa_secmode;
s->nwamd_wifi_scan_curr[index].nww_speed = attrp->wa_speed;
s->nwamd_wifi_scan_curr[index].nww_channel = attrp->wa_channel;
s->nwamd_wifi_scan_curr[index].nww_bsstype = attrp->wa_bsstype;
s->nwamd_wifi_scan_curr[index].nww_selected = B_FALSE;
s->nwamd_wifi_scan_curr[index].nww_connected = B_FALSE;
s->nwamd_wifi_scan_curr[index].nww_have_key = B_FALSE;
s->nwamd_wifi_scan_curr[index].nww_keyindex = 1;
s->nwamd_wifi_scan_curr_num++;
for (i = 0; i < s->nwamd_wifi_scan_last_num; i++) {
found = (strcmp(s->nwamd_wifi_scan_last[i].nww_essid,
essid_name) == 0 &&
strcmp(s->nwamd_wifi_scan_last[i].nww_bssid,
bssid_name) == 0);
if (found)
break;
}
if (!found)
s->nwamd_wifi_scan_changed = B_TRUE;
nlog(LOG_DEBUG, "get_scan_results(%s, %d): ESSID %s, BSSID %s",
linkname, index, essid_name, bssid_name);
return (B_TRUE);
}
boolean_t
nwamd_wlan_connected(nwamd_object_t ncu_obj)
{
nwamd_ncu_t *ncu = ncu_obj->nwamd_object_data;
nwamd_link_t *link = &ncu->ncu_link;
dladm_wlan_linkattr_t attr;
char essid[DLADM_STRSIZE];
char bssid[DLADM_STRSIZE];
boolean_t connected = B_FALSE;
int retries = 0;
while (retries++ < 4) {
if (dladm_wlan_get_linkattr(dld_handle, link->nwamd_link_id,
&attr) != DLADM_STATUS_OK) {
attr.la_status = DLADM_WLAN_LINK_DISCONNECTED;
} else if (attr.la_status == DLADM_WLAN_LINK_CONNECTED) {
break;
}
}
if (attr.la_status == DLADM_WLAN_LINK_CONNECTED) {
(void) dladm_wlan_essid2str(&attr.la_wlan_attr.wa_essid, essid);
(void) dladm_wlan_bssid2str(&attr.la_wlan_attr.wa_bssid, bssid);
connected = B_TRUE;
nlog(LOG_DEBUG, "nwamd_wlan_connected: %s connected to %s %s",
ncu->ncu_name, essid, bssid);
} else {
return (B_FALSE);
}
if (link->nwamd_link_wifi_autoconf) {
(void) strlcpy(link->nwamd_link_wifi_essid, essid,
sizeof (link->nwamd_link_wifi_essid));
(void) strlcpy(link->nwamd_link_wifi_bssid, bssid,
sizeof (link->nwamd_link_wifi_bssid));
}
if (strcmp(essid, link->nwamd_link_wifi_essid) == 0) {
(void) dladm_wlan_strength2str(&attr.la_wlan_attr.wa_strength,
link->nwamd_link_wifi_signal_strength);
(void) strlcpy(link->nwamd_link_wifi_bssid, bssid,
sizeof (link->nwamd_link_wifi_bssid));
if (attr.la_wlan_attr.wa_strength < wireless_scan_level) {
nlog(LOG_DEBUG, "nwamd_wlan_connected: "
"connected but signal under threshold...");
(void) nwamd_wlan_scan(ncu->ncu_name);
}
return (connected);
} else if (strlen(essid) == 0) {
nlog(LOG_DEBUG,
"nwamd_wlan_connected: connected to hidden WLAN, cannot "
"verify connection details");
return (connected);
} else {
(void) nlog(LOG_ERR,
"nwamd_wlan_connected: wrong AP on %s; expected %s %s",
ncu->ncu_name, link->nwamd_link_wifi_essid,
link->nwamd_link_wifi_bssid);
(void) dladm_wlan_disconnect(dld_handle, link->nwamd_link_id);
link->nwamd_link_wifi_connected = B_FALSE;
return (B_FALSE);
}
}
static void *
wlan_scan_thread(void *arg)
{
char *linkname = arg;
nwamd_object_t ncu_obj;
nwamd_ncu_t *ncu;
nwamd_link_t *link;
dladm_status_t status;
char essid[DLADM_STRSIZE];
char bssid[DLADM_STRSIZE];
uint32_t now, link_id;
nwamd_wifi_scan_t s;
int i;
if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
== NULL) {
nlog(LOG_ERR, "wlan_scan_thread: could not find object "
"for link %s", linkname);
free(linkname);
return (NULL);
}
ncu = ncu_obj->nwamd_object_data;
link = &ncu->ncu_link;
now = NSEC_TO_SEC(gethrtime());
if ((now - link->nwamd_link_wifi_scan.nwamd_wifi_scan_last_time) <
WIRELESS_SCAN_REQUESTED_INTERVAL_MIN) {
nlog(LOG_DEBUG, "wlan_scan_thread: last scan for %s "
"was < %d sec ago, ignoring scan request",
linkname, WIRELESS_SCAN_REQUESTED_INTERVAL_MIN);
nwamd_object_release(ncu_obj);
free(linkname);
return (NULL);
}
(void) bzero(&s, sizeof (nwamd_wifi_scan_t));
(void) strlcpy(s.nwamd_wifi_scan_link, ncu->ncu_name,
sizeof (s.nwamd_wifi_scan_link));
s.nwamd_wifi_scan_last_num =
link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr_num;
if (s.nwamd_wifi_scan_last_num > 0) {
(void) memcpy(s.nwamd_wifi_scan_last,
link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr,
s.nwamd_wifi_scan_last_num * sizeof (nwam_wlan_t));
}
link_id = link->nwamd_link_id;
nwamd_object_release(ncu_obj);
nlog(LOG_DEBUG, "wlan_scan_thread: initiating scan on %s",
s.nwamd_wifi_scan_link);
scanconnect_entry();
status = dladm_wlan_scan(dld_handle, link_id, &s, get_scan_results);
s.nwamd_wifi_scan_last_time = NSEC_TO_SEC(gethrtime());
if (!s.nwamd_wifi_scan_changed) {
s.nwamd_wifi_scan_changed = (s.nwamd_wifi_scan_curr_num !=
s.nwamd_wifi_scan_last_num);
}
scanconnect_exit();
if (status != DLADM_STATUS_OK) {
nlog(LOG_ERR, "wlan_scan_thread: cannot scan link %s",
s.nwamd_wifi_scan_link);
free(linkname);
return (NULL);
}
if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
== NULL) {
nlog(LOG_ERR, "wlan_scan_thread: could not find object "
"for link %s after doing scan", linkname);
free(linkname);
return (NULL);
}
ncu = ncu_obj->nwamd_object_data;
link = &ncu->ncu_link;
for (i = 0; i < s.nwamd_wifi_scan_curr_num; i++) {
if (NEED_ENC(s.nwamd_wifi_scan_curr[i].nww_security_mode)) {
char keyname[NWAM_MAX_VALUE_LEN];
dladm_wlan_key_t *key = NULL;
if (wireless_strict_bssid) {
int b_match = 0;
(void) nwam_walk_known_wlans(bssid_match,
s.nwamd_wifi_scan_curr[i].nww_bssid, 0,
&b_match);
if (b_match == 0)
continue;
}
if (known_wlan_get_keyname
(s.nwamd_wifi_scan_curr[i].nww_essid, keyname)
== NWAM_SUCCESS &&
(key = nwamd_wlan_get_key_named(keyname,
s.nwamd_wifi_scan_curr[i].nww_security_mode))
!= NULL) {
s.nwamd_wifi_scan_curr[i].nww_have_key =
B_TRUE;
s.nwamd_wifi_scan_curr[i].nww_keyindex =
s.nwamd_wifi_scan_curr[i].
nww_security_mode ==
DLADM_WLAN_SECMODE_WEP ?
key->wk_idx : 1;
nlog(LOG_DEBUG, "found matching keyname for \
%s", s.nwamd_wifi_scan_curr[i].nww_bssid);
free(key);
}
}
}
link->nwamd_link_wifi_scan = s;
nwamd_set_selected_connected(ncu,
link->nwamd_link_wifi_essid[0] != '\0',
link->nwamd_link_wifi_connected);
if (!wireless_selection_possible(ncu_obj)) {
nwamd_object_release(ncu_obj);
free(linkname);
return (NULL);
}
if (!nwamd_find_known_wlan(ncu_obj)) {
if (!nwamd_wlan_connected(ncu_obj)) {
if (link->nwamd_link_wifi_connected) {
nlog(LOG_DEBUG, "wlan_scan_thread: "
"unexpected disconnect after scan");
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_DOWN);
} else {
nlog(LOG_DEBUG, "wlan_scan_thread: "
"no known WLANs - ask user");
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION);
}
} else {
nlog(LOG_DEBUG, "wlan_scan_thread: still connected to "
"%s %s", link->nwamd_link_wifi_essid,
link->nwamd_link_wifi_bssid);
if (ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_UP);
}
}
nwamd_object_release(ncu_obj);
} else {
nlog(LOG_DEBUG, "wlan_scan_thread: found known WLAN %s %s",
link->nwamd_link_wifi_essid, link->nwamd_link_wifi_bssid);
if (!nwamd_wlan_connected(ncu_obj)) {
(void) strlcpy(essid, link->nwamd_link_wifi_essid,
sizeof (essid));
(void) strlcpy(bssid, link->nwamd_link_wifi_bssid,
sizeof (bssid));
nwamd_object_release(ncu_obj);
(void) nwamd_wlan_select(linkname, essid, bssid,
link->nwamd_link_wifi_security_mode, B_TRUE);
} else {
nlog(LOG_DEBUG, "wlan_scan_thread: still connected to "
"known WLAN %s %s", link->nwamd_link_wifi_essid,
link->nwamd_link_wifi_bssid);
if (ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_UP);
}
nwamd_object_release(ncu_obj);
}
}
free(linkname);
return (NULL);
}
nwam_error_t
nwamd_wlan_scan(const char *linkname)
{
pthread_t wifi_thread;
char *link = strdup(linkname);
if (link == NULL) {
nlog(LOG_ERR, "nwamd_wlan_scan: out of memory");
return (NWAM_NO_MEMORY);
}
nlog(LOG_DEBUG, "nwamd_wlan_scan: WLAN scan for %s",
link);
if (pthread_create(&wifi_thread, NULL, wlan_scan_thread,
link) != 0) {
nlog(LOG_ERR, "nwamd_wlan_scan: could not start scan");
free(link);
return (NWAM_ERROR_INTERNAL);
}
(void) pthread_detach(wifi_thread);
return (NWAM_SUCCESS);
}
static dladm_status_t
do_connect(uint32_t link_id, dladm_wlan_attr_t *attrp, dladm_wlan_key_t *key,
uint_t keycount, uint_t flags)
{
dladm_status_t status;
char errmsg[DLADM_STRSIZE];
scanconnect_entry();
status = dladm_wlan_connect(dld_handle, link_id, attrp,
DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT, key, keycount, flags);
scanconnect_exit();
nlog(LOG_DEBUG, "nwamd_do_connect: dladm_wlan_connect returned %s",
dladm_status2str(status, errmsg));
return (status);
}
static void *
wlan_connect_thread(void *arg)
{
char *linkname = arg;
nwamd_object_t ncu_obj;
nwamd_ncu_t *ncu;
nwamd_link_t *link;
nwam_error_t err;
uint_t keycount;
uint32_t link_id;
dladm_wlan_key_t *key = NULL;
dladm_wlan_attr_t attr;
dladm_status_t status;
boolean_t autoconf = B_FALSE;
if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
== NULL) {
nlog(LOG_ERR, "wlan_connect_thread: could not find object "
"for link %s", linkname);
free(linkname);
return (NULL);
}
ncu = ncu_obj->nwamd_object_data;
link = &ncu->ncu_link;
if (!wireless_selection_possible(ncu_obj)) {
nlog(LOG_DEBUG, "wlan_connect_thread: %s in invalid state or "
"has lower priority", ncu->ncu_name);
goto done;
}
if (nwamd_wlan_connected(ncu_obj)) {
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
ncu_obj->nwamd_object_state, NWAM_AUX_STATE_UP);
goto done;
}
(void) memset(&attr, 0, sizeof (attr));
if (dladm_wlan_str2essid(link->nwamd_link_wifi_essid, &attr.wa_essid)
!= DLADM_STATUS_OK) {
nlog(LOG_ERR, "wlan_connect_thread: invalid ESSID '%s' "
"for '%s'", link->nwamd_link_wifi_essid, ncu->ncu_name);
goto done;
}
attr.wa_valid = DLADM_WLAN_ATTR_ESSID;
if (link->nwamd_link_wifi_bssid[0] != '\0') {
if (dladm_wlan_str2bssid(link->nwamd_link_wifi_bssid,
&attr.wa_bssid) != DLADM_STATUS_OK) {
nlog(LOG_ERR, "wlan_connect_thread: invalid BSSID '%s'",
"for '%s'", link->nwamd_link_wifi_bssid,
ncu->ncu_name);
} else {
attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
}
}
if (NEED_ENC(link->nwamd_link_wifi_security_mode)) {
if (link->nwamd_link_wifi_key == NULL) {
nlog(LOG_ERR, "wlan_connect_thread: could not find "
"key for WLAN '%s'", link->nwamd_link_wifi_essid);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_LINK_WIFI_NEED_KEY);
goto done;
}
if ((key = calloc(1, sizeof (dladm_wlan_key_t))) == NULL) {
nlog(LOG_ERR, "wlan_connect_thread: out of memory");
goto done;
}
(void) memcpy(key, link->nwamd_link_wifi_key,
sizeof (dladm_wlan_key_t));
attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
attr.wa_secmode = link->nwamd_link_wifi_security_mode;
keycount = 1;
nlog(LOG_DEBUG, "wlan_connect_thread: retrieved key");
} else {
key = NULL;
keycount = 0;
}
link->nwamd_link_wifi_autoconf = B_FALSE;
link_id = link->nwamd_link_id;
nwamd_object_release(ncu_obj);
status = do_connect(link_id, &attr, key, keycount,
DLADM_WLAN_CONNECT_NOSCAN);
if (status != DLADM_STATUS_OK) {
if (!wireless_autoconf || (status = do_connect(link_id, &attr,
NULL, 0, 0)) != DLADM_STATUS_OK) {
nlog(LOG_ERR, "wlan_connect_thread: connect failed for "
"%s", linkname);
goto done_unlocked;
}
if (status == DLADM_STATUS_OK)
autoconf = B_TRUE;
}
if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
== NULL) {
nlog(LOG_ERR, "wlan_connect_thread: could not find object "
"for link %s", linkname);
goto done_unlocked;
}
ncu = ncu_obj->nwamd_object_data;
link = &ncu->ncu_link;
if (autoconf)
link->nwamd_link_wifi_autoconf = B_TRUE;
link->nwamd_link_wifi_connected = nwamd_wlan_connected(ncu_obj);
if (link->nwamd_link_wifi_connected) {
if (link->nwamd_link_wifi_add_to_known_wlans) {
nlog(LOG_DEBUG, "wlan_connect_thread: "
"add '%s' to known WLANs",
link->nwamd_link_wifi_essid);
if ((err = nwam_known_wlan_add_to_known_wlans
(link->nwamd_link_wifi_essid,
link->nwamd_link_wifi_bssid[0] != '\0' ?
link->nwamd_link_wifi_bssid : NULL,
link->nwamd_link_wifi_security_mode,
link->nwamd_link_wifi_security_mode ==
DLADM_WLAN_SECMODE_WEP ?
(uint_t)link->nwamd_link_wifi_key->wk_idx : 1,
NEED_ENC(link->nwamd_link_wifi_security_mode) ?
link->nwamd_link_wifi_keyname : NULL))
!= NWAM_SUCCESS) {
nlog(LOG_ERR, "wlan_connect_thread: "
"could not add to known WLANs: %s",
nwam_strerror(err));
}
}
nwamd_set_selected_connected(ncu, B_TRUE, B_TRUE);
nlog(LOG_DEBUG, "wlan_connect_thread: connect "
"succeeded, setting state online");
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name, NWAM_STATE_ONLINE,
NWAM_AUX_STATE_UP);
}
done:
nwamd_object_release(ncu_obj);
done_unlocked:
free(linkname);
free(key);
return (NULL);
}
void
nwamd_wlan_connect(const char *linkname)
{
pthread_t wifi_thread;
char *link = strdup(linkname);
if (link == NULL) {
nlog(LOG_ERR, "nwamd_wlan_connect: out of memory");
return;
}
nlog(LOG_DEBUG, "nwamd_wlan_connect: WLAN connect for %s",
link);
if (pthread_create(&wifi_thread, NULL, wlan_connect_thread, link) != 0)
nlog(LOG_ERR, "nwamd_wlan_connect: could not start connect");
(void) pthread_detach(wifi_thread);
}
static void *
wlan_monitor_signal_thread(void *arg)
{
char *linkname = arg;
nwamd_object_t ncu_obj;
nwamd_ncu_t *ncu;
nwamd_link_t *link;
boolean_t first_time = B_TRUE;
for (;;) {
if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK,
linkname)) == NULL) {
nlog(LOG_ERR, "wlan_monitor_signal_thread: could "
"not find object for link %s", linkname);
break;
}
ncu = ncu_obj->nwamd_object_data;
link = &ncu->ncu_link;
if (ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE ||
ncu_obj->nwamd_object_state == NWAM_STATE_DISABLED) {
nlog(LOG_INFO, "wlan_monitor_signal_thread: "
"%s is %s, stopping thread", linkname,
nwam_state_to_string(ncu_obj->nwamd_object_state));
link->nwamd_link_wifi_monitor_thread = 0;
nwamd_object_release(ncu_obj);
break;
}
if (first_time) {
first_time = B_FALSE;
if (link->nwamd_link_wifi_monitor_thread != 0) {
nwamd_object_release(ncu_obj);
break;
} else {
link->nwamd_link_wifi_monitor_thread =
pthread_self();
}
}
if (!nwamd_wlan_connected(ncu_obj)) {
nlog(LOG_ERR, "wlan_monitor_signal_thread: "
"disconnect occured for WLAN on link %s", linkname);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
ncu_obj->nwamd_object_name,
NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_DOWN);
link->nwamd_link_wifi_monitor_thread = 0;
nwamd_object_release(ncu_obj);
break;
}
nwamd_object_release(ncu_obj);
(void) sleep(WIRELESS_MONITOR_SIGNAL_INTERVAL);
}
free(linkname);
return (NULL);
}
void
nwamd_wlan_monitor_signal(const char *linkname)
{
pthread_t wifi_thread;
char *link = strdup(linkname);
if (link == NULL) {
nlog(LOG_ERR, "nwamd_wlan_monitor_signal: out of memory");
return;
}
nlog(LOG_DEBUG, "nwamd_wlan_monitor_signal: WLAN monitor for %s",
link);
if (pthread_create(&wifi_thread, NULL, wlan_monitor_signal_thread,
link) != 0) {
nlog(LOG_ERR, "nwamd_wlan_monitor_signal: could not monitor "
"link %s", link);
free(link);
return;
}
(void) pthread_detach(wifi_thread);
}
void
nwamd_ncu_handle_link_state_event(nwamd_event_t event)
{
nwam_event_t evm;
nwamd_object_t ncu_obj;
nwamd_ncu_t *ncu;
nwamd_link_t *link;
ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, event->event_object);
if (ncu_obj == NULL) {
nlog(LOG_INFO, "nwamd_ncu_handle_link_state_event: no object "
"%s", event->event_object);
nwamd_event_do_not_send(event);
return;
}
ncu = ncu_obj->nwamd_object_data;
link = &ncu->ncu_link;
evm = event->event_msg;
if (link->nwamd_link_media == DL_WIFI) {
nwamd_object_release(ncu_obj);
return;
}
if (evm->nwe_data.nwe_link_state.nwe_link_up &&
ncu_obj->nwamd_object_state != NWAM_STATE_DISABLED) {
if (link->nwamd_link_activation_mode ==
NWAM_ACTIVATION_MODE_PRIORITIZED) {
int64_t priority_group;
(void) pthread_mutex_lock(&active_ncp_mutex);
priority_group = current_ncu_priority_group;
(void) pthread_mutex_unlock(&active_ncp_mutex);
if (link->nwamd_link_priority_group > priority_group) {
nlog(LOG_DEBUG,
"nwamd_ncu_handle_link_state_event: "
"got LINK UP event for priority group "
"%lld, less preferred than current %lld, "
"ignoring",
link->nwamd_link_priority_group,
priority_group);
} else if (link->nwamd_link_priority_group ==
priority_group) {
nlog(LOG_DEBUG,
"nwamd_ncu_handle_link_state_event: "
"got LINK UP event for priority group "
"%lld, same as current %lld",
link->nwamd_link_priority_group,
priority_group);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
event->event_object,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_UP);
} else {
nlog(LOG_DEBUG,
"nwamd_ncu_handle_link_state_event: "
"got LINK UP event for priority group "
"%lld, more preferred than current %lld",
link->nwamd_link_priority_group,
priority_group);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
event->event_object,
NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_UP);
nwamd_object_release(ncu_obj);
nwamd_ncp_deactivate_priority_group
(priority_group);
nwamd_ncp_activate_priority_group
(link->nwamd_link_priority_group);
return;
}
} else if (link->nwamd_link_activation_mode ==
NWAM_ACTIVATION_MODE_MANUAL) {
nlog(LOG_DEBUG, "nwamd_ncu_handle_link_state_event: "
"got LINK UP event for manual NCU %s",
ncu_obj->nwamd_object_name);
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
NWAM_AUX_STATE_UP);
}
}
if (!evm->nwe_data.nwe_link_state.nwe_link_up &&
(ncu_obj->nwamd_object_state == NWAM_STATE_ONLINE ||
ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE)) {
if (link->nwamd_link_activation_mode ==
NWAM_ACTIVATION_MODE_PRIORITIZED) {
nlog(LOG_DEBUG,
"nwamd_ncu_handle_link_state_event: "
"got LINK DOWN for priority group %lld",
link->nwamd_link_priority_group);
} else {
nlog(LOG_DEBUG, "nwamd_ncu_handle_link_state_event: "
"got LINK DOWN event for manual NCU %s",
ncu_obj->nwamd_object_name);
}
nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
NWAM_AUX_STATE_DOWN);
}
nwamd_object_release(ncu_obj);
}