4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
33 #include <libdllink.h>
34 #include <libdlstat.h>
35 #include <libdlwlan.h>
45 #include <sys/types.h>
55 #include "known_wlans.h"
59 * ncu_phys.c - contains routines that are physical-link specific.
64 * Get link state from kstats. Used to determine initial link state for
65 * cases where drivers do not support DL_NOTE_LINK_UP/DOWN. If link
66 * state is LINK_STATE_UNKNOWN, we assume the link is up and the IP NCU
67 * timeout will cause us to move on to other links.
70 nwamd_get_link_state(const char *name
)
74 char module
[DLPI_LINKNAME_MAX
];
76 link_state_t link_state
= LINK_STATE_UNKNOWN
;
78 if ((kcp
= kstat_open()) == NULL
)
81 if (dlpi_parselink(name
, module
, &instance
) != DLPI_SUCCESS
)
84 if ((ksp
= kstat_lookup(kcp
, module
, instance
, "mac")) == NULL
) {
86 * The kstat query could fail if the underlying MAC
87 * driver was already detached.
92 if (kstat_read(kcp
, ksp
, NULL
) == -1)
95 (void) dladm_kstat_value(ksp
, "link_state", KSTAT_DATA_UINT32
,
99 (void) kstat_close(kcp
);
105 * Set/unset link propeties. At present, these are MAC address, link MTU and
106 * autopush modules. We set MAC address last as setting it may cause a chip
107 * reset which can prevent other device property setting succeeding.
110 nwamd_set_unset_link_properties(nwamd_ncu_t
*ncu
, boolean_t set
)
112 dlpi_handle_t dh
= ncu
->ncu_link
.nwamd_link_dhp
;
113 char *addr
= set
? ncu
->ncu_link
.nwamd_link_mac_addr
: NULL
;
114 uint64_t mtu
= set
? ncu
->ncu_link
.nwamd_link_mtu
: 0;
115 char **autopush
= set
? ncu
->ncu_link
.nwamd_link_autopush
: NULL
;
116 uint_t num_autopush
= set
? ncu
->ncu_link
.nwamd_link_num_autopush
: 0;
117 uchar_t
*hwaddr
= NULL
, curraddr
[DLPI_PHYSADDR_MAX
];
118 size_t hwaddrlen
= DLPI_PHYSADDR_MAX
;
120 dladm_status_t status
;
121 char mtustr
[DLADM_PROP_VAL_MAX
];
123 char errmsg
[DLADM_STRSIZE
];
127 * Set MTU here - either default value (if mtu == 0 indicating it has
128 * not been set) or specified value.
132 status
= dladm_get_linkprop(dld_handle
,
133 ncu
->ncu_link
.nwamd_link_id
, DLADM_PROP_VAL_DEFAULT
, "mtu",
135 if (status
!= DLADM_STATUS_OK
) {
136 nlog(LOG_ERR
, "nwamd_set_unset_link_properties: "
137 "dladm_get_linkprop failed: %s",
138 dladm_status2str(status
, errmsg
));
142 (void) snprintf(mtustr
, DLADM_PROP_VAL_MAX
, "%lld", mtu
);
147 nlog(LOG_DEBUG
, "nwamd_set_unset_link_properties: setting MTU of %s "
148 "for link %s", mtustr
, ncu
->ncu_name
);
149 status
= dladm_set_linkprop(dld_handle
, ncu
->ncu_link
.nwamd_link_id
,
150 "mtu", &cp
, 1, DLADM_OPT_ACTIVE
);
151 if (status
!= DLADM_STATUS_OK
) {
152 nlog(LOG_ERR
, "nwamd_set_unset_link_properties: "
153 "dladm_set_linkprop failed: %s",
154 dladm_status2str(status
, errmsg
));
157 nlog(LOG_DEBUG
, "nwamd_set_unset_link_properties: setting %d "
158 "autopush module for link %s", num_autopush
, ncu
->ncu_name
);
159 status
= dladm_set_linkprop(dld_handle
, ncu
->ncu_link
.nwamd_link_id
,
160 "autopush", autopush
, num_autopush
, DLADM_OPT_ACTIVE
);
161 if (status
!= DLADM_STATUS_OK
) {
162 nlog(LOG_ERR
, "nwamd_set_unset_link_properties: "
163 "dladm_set_linkprop failed for autopush property: %s",
164 dladm_status2str(status
, errmsg
));
168 * Set physical address - either factory (if link_mac_addr is NULL
169 * or we are unsetting properties) or specified MAC address string.
172 if ((hwaddr
= calloc(1, DLPI_PHYSADDR_MAX
)) == NULL
) {
174 "nwamd_set_unset_link_properties: malloc() failed");
177 if ((retval
= dlpi_get_physaddr(dh
, DL_FACT_PHYS_ADDR
,
178 hwaddr
, &hwaddrlen
)) != DLPI_SUCCESS
) {
179 nlog(LOG_ERR
, "nwamd_set_unset_link_properties: "
180 "could not get physical address for %s: %s",
181 ncu
->ncu_name
, dlpi_strerror(retval
));
186 int addrlen
= hwaddrlen
;
187 if ((hwaddr
= _link_aton(addr
, &addrlen
)) == NULL
) {
190 "nwamd_set_unset_link_properties: "
191 "%s: bad address for %s",
192 addr
, ncu
->ncu_name
);
195 nlog(LOG_ERR
, "nwamd_set_unset_link_properties:"
204 * Only set physical address if desired address differs from current -
205 * this avoids unnecessary chip resets for some drivers.
207 retval
= dlpi_get_physaddr(dh
, DL_CURR_PHYS_ADDR
, curraddr
,
209 if (retval
!= DLPI_SUCCESS
|| bcmp(curraddr
, hwaddr
, hwaddrlen
) != 0) {
210 retval
= dlpi_set_physaddr(dh
, DL_CURR_PHYS_ADDR
, hwaddr
,
212 if (retval
!= DLPI_SUCCESS
) {
213 nlog(LOG_ERR
, "nwamd_set_unset_link_properties:"
214 "failed setting mac address on %s: %s",
215 ncu
->ncu_name
, dlpi_strerror(retval
));
221 #define WLAN_ENC(sec) \
222 ((sec == DLADM_WLAN_SECMODE_WPA ? "WPA" : \
223 (sec == DLADM_WLAN_SECMODE_WEP ? "WEP" : "none")))
225 #define NEED_ENC(sec) \
226 (sec == DLADM_WLAN_SECMODE_WPA || sec == DLADM_WLAN_SECMODE_WEP)
228 #define WIRELESS_LAN_INIT_COUNT 8
231 * The variable wireless_scan_level specifies the signal level
232 * that we will initiate connections to previously-visited APs
233 * at when we are in the connected state.
235 dladm_wlan_strength_t wireless_scan_level
= DLADM_WLAN_STRENGTH_WEAK
;
238 * The variable wireless_scan_interval specifies how often the periodic
241 uint64_t wireless_scan_interval
= WIRELESS_SCAN_INTERVAL_DEFAULT
;
244 * The variable wireless_autoconf specifies if we use dladm_wlan_autoconf()
247 boolean_t wireless_autoconf
= B_FALSE
;
250 * The variable wireless_strict_bssid specifies if we only connect
251 * to WLANs with BSSIDs that we previously connected to.
253 boolean_t wireless_strict_bssid
= B_FALSE
;
256 * We need to ensure scan or connect threads do not run concurrently
257 * on any links - otherwise we get radio interference. Acquire this
258 * lock on entering scan/connect threads to prevent this.
260 pthread_mutex_t wireless_mutex
= PTHREAD_MUTEX_INITIALIZER
;
263 scanconnect_entry(void)
265 (void) pthread_mutex_lock(&wireless_mutex
);
269 scanconnect_exit(void)
271 (void) pthread_mutex_unlock(&wireless_mutex
);
275 * Below are functions used to handle storage/retrieval of keys
276 * for a given WLAN. The keys are stored/retrieved using dladm_set_secobj()
277 * and dladm_get_secobj().
281 * Convert key hexascii string to raw secobj value. This
282 * code is very similar to convert_secobj() in dladm.c, it would
283 * be good to have a libdladm function to convert values.
286 key_string_to_secobj_value(char *buf
, uint8_t *obj_val
, uint_t
*obj_lenp
,
287 dladm_secobj_class_t
class)
289 size_t buf_len
= strlen(buf
);
291 nlog(LOG_DEBUG
, "before: key_string_to_secobj_value: buf_len = %d",
294 /* length zero means "delete" */
298 if (buf
[buf_len
- 1] == '\n')
299 buf
[--buf_len
] = '\0';
301 nlog(LOG_DEBUG
, "after: key_string_to_secobj_value: buf_len = %d",
304 if (class == DLADM_SECOBJ_CLASS_WPA
) {
306 * Per IEEE802.11i spec, the Pre-shared key (PSK) length should
307 * be between 8 and 63.
309 if (buf_len
< 8 || buf_len
> 63) {
311 "key_string_to_secobj_value:"
312 " invalid WPA key length: buf_len = %d", buf_len
);
315 (void) memcpy(obj_val
, buf
, (uint_t
)buf_len
);
321 case 5: /* ASCII key sizes */
323 (void) memcpy(obj_val
, buf
, (uint_t
)buf_len
);
324 *obj_lenp
= (uint_t
)buf_len
;
327 case 26: /* Hex key sizes, not preceded by 0x */
328 if (hexascii_to_octet(buf
, (uint_t
)buf_len
, obj_val
, obj_lenp
)
331 "key_string_to_secobj_value: invalid WEP key");
336 case 28: /* Hex key sizes, preceded by 0x */
337 if (strncmp(buf
, "0x", 2) != 0 ||
338 hexascii_to_octet(buf
+ 2, (uint_t
)buf_len
- 2, obj_val
,
341 "key_string_to_secobj_value: invalid WEP key");
347 "key_string_to_secobj_value: invalid WEP key length");
354 * Callback used on each known WLAN:
355 * return 1 if a secobj, linked with an existing kwown wlan, has the same name
356 * of the secobj that is being created.
360 find_keyname_cb(nwam_known_wlan_handle_t kwh
, void *new_keyname
)
363 nwam_value_t old_key
;
366 uint_t num_old_keyname
, i
;
368 if ((err
= nwam_known_wlan_get_prop_value(kwh
,
369 NWAM_KNOWN_WLAN_PROP_KEYNAME
, &old_key
)) != NWAM_SUCCESS
) {
370 nlog(LOG_ERR
, "find_keyname_cb: nwam_known_wlan_get_prop: %s",
374 if ((err
= nwam_value_get_string_array(old_key
, &old_keyname
,
377 nlog(LOG_ERR
, "find_keyname_cb: nwam_value_get_string: %s",
379 nwam_value_free(old_key
);
382 nwam_value_free(old_key
);
383 for (i
= 0; i
< num_old_keyname
; i
++) {
384 if (strcmp(old_keyname
[i
], (const char *)new_keyname
) == 0)
385 /* Found matching keyname so terminate walk */
392 * Print the key name format into the appropriate field, then convert any ":"
393 * characters to ".", as ":[1-4]" is the slot indicator, which otherwise
394 * would trip us up. Invalid characters for secobj names are ignored.
395 * The fourth parameter is expected to be of size DLADM_SECOBJ_NAME_MAX.
397 * (Note that much of the system uses DLADM_WLAN_MAX_KEYNAME_LEN, which is 64
398 * rather than 32, but that dladm_get_secobj will fail if a length greater than
399 * DLD_SECOBJ_NAME_MAX is seen, and that's 32. This is all horribly broken.)
402 nwamd_set_key_name(const char *essid
, const char *bssid
, char *name
, size_t nsz
)
405 char secobj_name
[DLADM_WLAN_MAX_KEYNAME_LEN
];
407 /* create a concatenated string with essid and bssid */
408 if (bssid
== NULL
|| bssid
[0] == '\0') {
409 (void) snprintf(secobj_name
, sizeof (secobj_name
), "nwam-%s",
412 (void) snprintf(secobj_name
, sizeof (secobj_name
), "nwam-%s-%s",
416 /* copy only valid chars to the return string, terminating with \0 */
417 i
= 0; /* index into secobj_name */
418 j
= 0; /* index into name */
419 while (secobj_name
[i
] != '\0') {
423 if (secobj_name
[i
] == ':') {
426 } else if (isalnum(secobj_name
[i
]) ||
427 secobj_name
[i
] == '.' || secobj_name
[i
] == '-' ||
428 secobj_name
[i
] == '_') {
429 name
[j
] = secobj_name
[i
];
438 nwamd_wlan_set_key(const char *linkname
, const char *essid
, const char *bssid
,
439 uint32_t security_mode
, uint_t keyslot
, char *raw_key
)
441 nwamd_object_t ncu_obj
;
445 uint8_t obj_val
[DLADM_SECOBJ_VAL_MAX
];
446 uint_t obj_len
= sizeof (obj_val
);
447 char obj_name
[DLADM_SECOBJ_NAME_MAX
];
448 char obj_tempname
[DLADM_SECOBJ_NAME_MAX
];
449 dladm_status_t status
;
450 char errmsg
[DLADM_STRSIZE
];
451 dladm_secobj_class_t
class;
453 if ((ncu_obj
= nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK
, linkname
))
455 nlog(LOG_ERR
, "nwamd_wlan_set_key: could not find object "
456 "for link %s", linkname
);
457 return (NWAM_ENTITY_NOT_FOUND
);
459 ncu
= ncu_obj
->nwamd_object_data
;
460 link
= &ncu
->ncu_link
;
462 class = (security_mode
== DLADM_WLAN_SECMODE_WEP
?
463 DLADM_SECOBJ_CLASS_WEP
: DLADM_SECOBJ_CLASS_WPA
);
464 if (key_string_to_secobj_value(raw_key
, obj_val
, &obj_len
,
466 /* above function logs internally on failure */
467 nwamd_object_release(ncu_obj
);
468 return (NWAM_ERROR_INTERNAL
);
471 nlog(LOG_DEBUG
, "nwamd_wlan_set_key: running for link %s", linkname
);
473 * Name key object for this WLAN so it can be later retrieved.
474 * (bssid is appended if an object, with the same keyname,
475 * already exists and is associated to a known wlan)
477 nwamd_set_key_name(essid
, NULL
, obj_tempname
, sizeof (obj_tempname
));
478 (void) nwam_walk_known_wlans(find_keyname_cb
, obj_tempname
, 0, &ret
);
480 * We also check if the keyval is the same. The user might want
481 * to use the same key for more APs with the same ESSID.
482 * This can result in a known wlan with multiple BSSIDs
485 dladm_wlan_key_t
*old_secobj
= nwamd_wlan_get_key_named(
486 obj_tempname
, security_mode
);
487 nlog(LOG_DEBUG
, "found existing obj_name %s", obj_tempname
);
488 ret
= memcmp((*old_secobj
).wk_val
, obj_val
, obj_len
);
489 nwamd_set_key_name(essid
, ret
? bssid
: NULL
, obj_name
,
493 nwamd_set_key_name(essid
, NULL
, obj_name
,
496 nlog(LOG_DEBUG
, "store_key: obj_name is %s", obj_name
);
499 * We have validated the new key, so remove the old one.
500 * This will actually delete the keyobj only if the user had set
501 * a wrong key and is replacing it with a new one for the same AP.
503 status
= dladm_unset_secobj(dld_handle
, obj_name
,
504 DLADM_OPT_ACTIVE
| DLADM_OPT_PERSIST
);
505 if (status
!= DLADM_STATUS_OK
&& status
!= DLADM_STATUS_NOTFOUND
) {
506 nlog(LOG_ERR
, "store_key: could not remove old secure object "
507 "'%s' for key: %s", obj_name
,
508 dladm_status2str(status
, errmsg
));
509 nwamd_object_release(ncu_obj
);
510 return (NWAM_ERROR_INTERNAL
);
513 /* if we're just deleting the key, then we're done */
514 if (raw_key
[0] == '\0') {
515 nwamd_object_release(ncu_obj
);
516 return (NWAM_SUCCESS
);
519 status
= dladm_set_secobj(dld_handle
, obj_name
, class,
521 DLADM_OPT_CREATE
| DLADM_OPT_PERSIST
| DLADM_OPT_ACTIVE
);
522 if (status
!= DLADM_STATUS_OK
) {
523 nlog(LOG_ERR
, "store_key: could not create secure object "
524 "'%s' for key: %s", obj_name
,
525 dladm_status2str(status
, errmsg
));
526 nwamd_object_release(ncu_obj
);
527 return (NWAM_ERROR_INTERNAL
);
529 link
->nwamd_link_wifi_key
= nwamd_wlan_get_key_named(obj_name
,
531 (void) strlcpy(link
->nwamd_link_wifi_keyname
, obj_name
,
532 sizeof (link
->nwamd_link_wifi_keyname
));
533 link
->nwamd_link_wifi_security_mode
= security_mode
;
534 if (security_mode
== DLADM_WLAN_SECMODE_WEP
) {
535 link
->nwamd_link_wifi_key
->wk_idx
=
536 (keyslot
>= 1 && keyslot
<= 4) ? keyslot
: 1;
539 /* If link NCU is offline* or online, (re)connect. */
540 switch (ncu_obj
->nwamd_object_state
) {
541 case NWAM_STATE_ONLINE
:
542 /* if changing the key of the connected WLAN, reconnect */
543 if (strcmp(essid
, link
->nwamd_link_wifi_essid
) == 0)
544 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
545 ncu_obj
->nwamd_object_name
, NWAM_STATE_ONLINE
,
546 NWAM_AUX_STATE_LINK_WIFI_CONNECTING
);
548 case NWAM_STATE_OFFLINE_TO_ONLINE
:
549 /* if we are waiting for the key, connect */
550 if (ncu_obj
->nwamd_object_aux_state
==
551 NWAM_AUX_STATE_LINK_WIFI_NEED_KEY
)
552 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
553 ncu_obj
->nwamd_object_name
,
554 NWAM_STATE_OFFLINE_TO_ONLINE
,
555 NWAM_AUX_STATE_LINK_WIFI_CONNECTING
);
560 nwamd_object_release(ncu_obj
);
562 return (NWAM_SUCCESS
);
566 * returns NULL if no key was recovered from libdladm. Passing in
567 * security mode of 0 means we don't care what key type it is.
570 nwamd_wlan_get_key_named(const char *name
, uint32_t security_mode
)
572 dladm_status_t status
;
573 char errmsg
[DLADM_STRSIZE
];
574 dladm_wlan_key_t
*cooked_key
;
575 dladm_secobj_class_t
class;
577 if (security_mode
== DLADM_WLAN_SECMODE_NONE
)
581 * Newly-allocated key must be freed by caller, or by
582 * subsequent call to nwamd_wlan_get_key_named().
584 if ((cooked_key
= malloc(sizeof (dladm_wlan_key_t
))) == NULL
) {
585 nlog(LOG_ERR
, "nwamd_wlan_get_key_named: malloc failed");
590 * Set name appropriately to retrieve key for this WLAN. Note that we
591 * cannot use the actual wk_name buffer size, as it's two times too
592 * large for dladm_get_secobj.
594 (void) strlcpy(cooked_key
->wk_name
, name
, DLADM_SECOBJ_NAME_MAX
);
595 nlog(LOG_DEBUG
, "nwamd_wlan_get_key_named: len = %d, object = %s\n",
596 strlen(cooked_key
->wk_name
), cooked_key
->wk_name
);
597 cooked_key
->wk_len
= sizeof (cooked_key
->wk_val
);
598 cooked_key
->wk_idx
= 1;
600 /* Try the kernel first, then fall back to persistent storage. */
601 status
= dladm_get_secobj(dld_handle
, cooked_key
->wk_name
, &class,
602 cooked_key
->wk_val
, &cooked_key
->wk_len
,
604 if (status
!= DLADM_STATUS_OK
) {
605 nlog(LOG_DEBUG
, "nwamd_wlan_get_key_named: "
606 "dladm_get_secobj(TEMP) failed: %s",
607 dladm_status2str(status
, errmsg
));
608 status
= dladm_get_secobj(dld_handle
, cooked_key
->wk_name
,
609 &class, cooked_key
->wk_val
, &cooked_key
->wk_len
,
614 case DLADM_STATUS_OK
:
615 nlog(LOG_DEBUG
, "nwamd_wlan_get_key_named: "
616 "dladm_get_secobj succeeded: len %d", cooked_key
->wk_len
);
618 case DLADM_STATUS_NOTFOUND
:
620 * We do not want an error in the case that the secobj
621 * is not found, since we then prompt for it.
626 nlog(LOG_ERR
, "nwamd_wlan_get_key_named: could not get key "
627 "from secure object '%s': %s", cooked_key
->wk_name
,
628 dladm_status2str(status
, errmsg
));
633 if (security_mode
!= 0) {
635 case DLADM_SECOBJ_CLASS_WEP
:
636 if (security_mode
== DLADM_WLAN_SECMODE_WEP
)
639 case DLADM_SECOBJ_CLASS_WPA
:
640 if (security_mode
== DLADM_WLAN_SECMODE_WPA
)
644 /* shouldn't happen */
645 nlog(LOG_ERR
, "nwamd_wlan_get_key: invalid class %d",
649 /* key type mismatch */
650 nlog(LOG_ERR
, "nwamd_wlan_get_key: key type mismatch"
651 " from secure object '%s'", cooked_key
->wk_name
);
659 static dladm_wlan_key_t
*
660 nwamd_wlan_get_key(const char *essid
, const char *bssid
, uint32_t security_mode
)
662 char keyname
[DLADM_SECOBJ_NAME_MAX
];
664 nwamd_set_key_name(essid
, bssid
, keyname
, DLADM_SECOBJ_NAME_MAX
);
666 return (nwamd_wlan_get_key_named(keyname
, security_mode
));
670 * Checks if a wireless network can be selected or not. A wireless network
671 * CANNOT be selected if the NCU is DISABLED, or the NCU is OFFLINE or
672 * ONLINE* and has lower priority than the currently active priority-group.
673 * Called with object lock held.
676 wireless_selection_possible(nwamd_object_t object
)
678 nwamd_ncu_t
*ncu
= object
->nwamd_object_data
;
680 if (ncu
->ncu_link
.nwamd_link_media
!= DL_WIFI
)
683 (void) pthread_mutex_lock(&active_ncp_mutex
);
684 if (object
->nwamd_object_state
== NWAM_STATE_DISABLED
||
685 ((object
->nwamd_object_state
== NWAM_STATE_OFFLINE
||
686 object
->nwamd_object_state
== NWAM_STATE_ONLINE_TO_OFFLINE
) &&
687 ncu
->ncu_link
.nwamd_link_activation_mode
==
688 NWAM_ACTIVATION_MODE_PRIORITIZED
&&
689 (current_ncu_priority_group
== INVALID_PRIORITY_GROUP
||
690 ncu
->ncu_link
.nwamd_link_priority_group
>
691 current_ncu_priority_group
))) {
692 (void) pthread_mutex_unlock(&active_ncp_mutex
);
695 (void) pthread_mutex_unlock(&active_ncp_mutex
);
701 * Update the selected and/or connected values for the
702 * scan data. If these change, we need to trigger a scan
703 * event since the updated values need to be communicated
707 nwamd_set_selected_connected(nwamd_ncu_t
*ncu
, boolean_t selected
,
710 nwamd_link_t
*link
= &ncu
->ncu_link
;
711 nwamd_wifi_scan_t
*s
= &link
->nwamd_link_wifi_scan
;
713 boolean_t trigger_scan_event
= B_FALSE
;
715 for (i
= 0; i
< s
->nwamd_wifi_scan_curr_num
; i
++) {
716 if (strcmp(s
->nwamd_wifi_scan_curr
[i
].nww_essid
,
717 link
->nwamd_link_wifi_essid
) != 0 ||
718 (link
->nwamd_link_wifi_bssid
[0] != '\0' &&
719 strcmp(s
->nwamd_wifi_scan_curr
[i
].nww_bssid
,
720 link
->nwamd_link_wifi_bssid
) != 0))
723 if (!s
->nwamd_wifi_scan_curr
[i
].nww_selected
)
724 trigger_scan_event
= B_TRUE
;
725 s
->nwamd_wifi_scan_curr
[i
].nww_selected
= B_TRUE
;
727 if (s
->nwamd_wifi_scan_curr
[i
].nww_selected
)
728 trigger_scan_event
= B_TRUE
;
729 s
->nwamd_wifi_scan_curr
[i
].nww_selected
= B_FALSE
;
732 if (!s
->nwamd_wifi_scan_curr
[i
].nww_connected
)
733 trigger_scan_event
= B_TRUE
;
734 s
->nwamd_wifi_scan_curr
[i
].nww_connected
= B_TRUE
;
736 if (s
->nwamd_wifi_scan_curr
[i
].nww_connected
)
737 trigger_scan_event
= B_TRUE
;
738 s
->nwamd_wifi_scan_curr
[i
].nww_connected
= B_FALSE
;
742 if (trigger_scan_event
|| s
->nwamd_wifi_scan_changed
) {
743 nwamd_event_t scan_event
= nwamd_event_init_wlan
744 (ncu
->ncu_name
, NWAM_EVENT_TYPE_WLAN_SCAN_REPORT
, connected
,
745 s
->nwamd_wifi_scan_curr
, s
->nwamd_wifi_scan_curr_num
);
746 if (scan_event
!= NULL
) {
747 /* Avoid sending same scan data multiple times */
748 s
->nwamd_wifi_scan_changed
= B_FALSE
;
749 nwamd_event_enqueue(scan_event
);
755 * Callback used on each known WLAN - if the BSSID is matched, set
756 * the ESSID of the hidden WLAN to the known WLAN name.
759 find_bssid_cb(nwam_known_wlan_handle_t kwh
, void *data
)
761 nwamd_link_t
*link
= data
;
763 nwam_value_t bssidval
;
764 char **bssids
, *name
;
765 uint_t num_bssids
, i
;
767 if ((err
= nwam_known_wlan_get_prop_value(kwh
,
768 NWAM_KNOWN_WLAN_PROP_BSSIDS
, &bssidval
)) != NWAM_SUCCESS
) {
769 nlog(LOG_ERR
, "find_bssid_cb: nwam_known_wlan_get_prop: %s",
773 if ((err
= nwam_value_get_string_array(bssidval
, &bssids
, &num_bssids
))
775 nlog(LOG_ERR
, "find_bssid_cb: nwam_value_get_string_array: %s",
777 nwam_value_free(bssidval
);
780 for (i
= 0; i
< num_bssids
; i
++) {
781 if (strcmp(bssids
[i
], link
->nwamd_link_wifi_bssid
) == 0) {
782 if ((err
= nwam_known_wlan_get_name(kwh
, &name
))
784 nlog(LOG_ERR
, "find_bssid_cb: "
785 "nwam_known_wlan_get_name: %s",
789 (void) strlcpy(link
->nwamd_link_wifi_essid
, name
,
790 sizeof (link
->nwamd_link_wifi_essid
));
792 nwam_value_free(bssidval
);
793 /* Found ESSID for BSSID so terminate walk */
797 nwam_value_free(bssidval
);
803 * We may have encountered a BSSID for a hidden WLAN before and as a result
804 * may have a known WLAN entry with this BSSID. Walk known WLANs, searching
805 * for a BSSID match. Called with object lock held.
808 check_if_hidden_wlan_was_visited(nwamd_link_t
*link
)
810 (void) nwam_walk_known_wlans(find_bssid_cb
, link
,
811 NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER
, NULL
);
815 nwamd_wlan_select(const char *linkname
, const char *essid
, const char *bssid
,
816 uint32_t security_mode
, boolean_t add_to_known_wlans
)
818 nwamd_object_t ncu_obj
;
821 boolean_t found_key
= B_FALSE
;
823 if ((ncu_obj
= nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK
, linkname
))
825 nlog(LOG_ERR
, "nwamd_wlan_select: could not find object "
826 "for link %s", linkname
);
827 return (NWAM_ENTITY_NOT_FOUND
);
829 ncu
= ncu_obj
->nwamd_object_data
;
830 link
= &ncu
->ncu_link
;
833 * If wireless selection is not possible because of the current
834 * state or priority-group, then stop.
836 if (!wireless_selection_possible(ncu_obj
)) {
837 nwamd_object_release(ncu_obj
);
838 return (NWAM_ENTITY_INVALID_STATE
);
841 /* unset selected, connected flag for previously connected wlan */
842 nwamd_set_selected_connected(ncu
, B_FALSE
, B_FALSE
);
844 /* Disconnect to allow new selection to go ahead */
845 (void) dladm_wlan_disconnect(dld_handle
, link
->nwamd_link_id
);
847 (void) strlcpy(link
->nwamd_link_wifi_essid
, essid
,
848 sizeof (link
->nwamd_link_wifi_essid
));
849 (void) strlcpy(link
->nwamd_link_wifi_bssid
, bssid
,
850 sizeof (link
->nwamd_link_wifi_bssid
));
851 link
->nwamd_link_wifi_security_mode
= security_mode
;
852 link
->nwamd_link_wifi_add_to_known_wlans
= add_to_known_wlans
;
854 /* If this is a hidden wlan, then essid is empty */
855 if (link
->nwamd_link_wifi_essid
[0] == '\0')
856 check_if_hidden_wlan_was_visited(link
);
858 /* set selected flag for newly-selected WLAN */
859 nwamd_set_selected_connected(ncu
, B_TRUE
, B_FALSE
);
861 /* does this WLAN require a key? If so go to NEED_KEY */
862 if (NEED_ENC(link
->nwamd_link_wifi_security_mode
)) {
864 * nwam secobjs can have two formats: nwam-ESSID-BSSID and
865 * nwam-ESSID. There is no reason for searching through known
866 * wlan keynames since this is only the selection process.
868 if ((link
->nwamd_link_wifi_key
= nwamd_wlan_get_key
869 (link
->nwamd_link_wifi_essid
, link
->nwamd_link_wifi_bssid
,
870 link
->nwamd_link_wifi_security_mode
)) != NULL
) {
872 * Found old key format,
873 * known wlans with similar names might exist
875 nwamd_set_key_name(link
->nwamd_link_wifi_essid
,
876 link
->nwamd_link_wifi_bssid
,
877 link
->nwamd_link_wifi_keyname
,
878 DLADM_SECOBJ_NAME_MAX
);
879 nlog(LOG_DEBUG
, "nwamd_wlan_select: got old format "
881 link
->nwamd_link_wifi_keyname
);
883 } else if ((link
->nwamd_link_wifi_key
= nwamd_wlan_get_key
884 (link
->nwamd_link_wifi_essid
, NULL
,
885 link
->nwamd_link_wifi_security_mode
)) != NULL
) {
886 nwamd_set_key_name(link
->nwamd_link_wifi_essid
, NULL
,
887 link
->nwamd_link_wifi_keyname
,
888 DLADM_SECOBJ_NAME_MAX
);
889 nlog(LOG_DEBUG
, "nwamd_wlan_select: got WLAN key %s",
890 link
->nwamd_link_wifi_keyname
);
893 nlog(LOG_ERR
, "nwamd_wlan_select: could not "
894 "find key for WLAN '%s'",
895 link
->nwamd_link_wifi_essid
);
898 free(link
->nwamd_link_wifi_key
);
899 link
->nwamd_link_wifi_key
= NULL
;
900 link
->nwamd_link_wifi_keyname
[0] = '\0';
903 if (NEED_ENC(link
->nwamd_link_wifi_security_mode
) && !found_key
) {
904 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
905 ncu_obj
->nwamd_object_name
,
906 NWAM_STATE_OFFLINE_TO_ONLINE
,
907 NWAM_AUX_STATE_LINK_WIFI_NEED_KEY
);
909 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
910 ncu_obj
->nwamd_object_name
, NWAM_STATE_OFFLINE_TO_ONLINE
,
911 NWAM_AUX_STATE_LINK_WIFI_CONNECTING
);
913 nwamd_object_release(ncu_obj
);
915 return (NWAM_SUCCESS
);
919 * See if BSSID is in visited list of BSSIDs for known WLAN. Used for
920 * strict BSSID matching (depends on wireless_strict_bssid property value).
923 bssid_match(nwam_known_wlan_handle_t kwh
, void *bssid
)
925 nwam_value_t bssidsval
;
931 if ((err
= nwam_known_wlan_get_prop_value(kwh
,
932 NWAM_KNOWN_WLAN_PROP_BSSIDS
, &bssidsval
)) != NWAM_SUCCESS
) {
933 nlog(LOG_ERR
, "bssid_match: %s", nwam_strerror(err
));
936 if ((err
= nwam_value_get_string_array(bssidsval
, &bssids
, &nelem
))
938 nwam_value_free(bssidsval
);
941 for (i
= 0; i
< nelem
; i
++) {
942 if (strcmp((const char *)bssid
, bssids
[i
]) == 0) {
947 nwam_value_free(bssidsval
);
952 /* Find most prioritized AP with strongest signal in scan data. */
954 find_best_wlan_cb(nwam_known_wlan_handle_t kwh
, void *data
)
956 nwamd_ncu_t
*ncu
= data
;
957 nwamd_link_t
*link
= &ncu
->ncu_link
;
958 nwamd_wifi_scan_t
*s
= &link
->nwamd_link_wifi_scan
;
962 dladm_wlan_strength_t curr_strength
= 0;
963 dladm_wlan_strength_t max_strength
= 0;
964 boolean_t found
= B_FALSE
;
966 if ((err
= nwam_known_wlan_get_name(kwh
, &name
)) != NWAM_SUCCESS
) {
967 nlog(LOG_ERR
, "find_best_wlan_cb: could not look up name: %s",
972 if (link
->nwamd_link_wifi_connected
) {
973 (void) dladm_wlan_str2strength
974 (link
->nwamd_link_wifi_signal_strength
, &curr_strength
);
978 * If we're >= scan level, don't pick another Known WLAN if still
979 * connected (even if a Known WLAN with higher priority is available).
980 * If the user wants to connect to a different Known WLAN, it can be
981 * done from the GUI or select-wifi subcommand of nwamadm(1M).
983 if (curr_strength
>= wireless_scan_level
&&
984 link
->nwamd_link_wifi_connected
) {
989 for (i
= 0; i
< s
->nwamd_wifi_scan_curr_num
; i
++) {
990 nwam_wlan_t
*cur_wlan
= &(s
->nwamd_wifi_scan_curr
[i
]);
991 int b_match
= bssid_match(kwh
, cur_wlan
->nww_bssid
);
994 * We need to either match the scanned essid, or in the case
995 * where the essid was not broadcast, match the scanned bssid.
997 if (strcmp(cur_wlan
->nww_essid
, name
) != 0 &&
998 !(cur_wlan
->nww_essid
[0] == '\0' && b_match
))
1001 * If wireless_strict_bssid is specified, need to match
1004 if (wireless_strict_bssid
&& !b_match
)
1007 * Found a match. Since we walk known WLANs in
1008 * priority order, it's guaranteed to be the
1009 * most prioritized. It may not be the strongest though -
1010 * we continue the walk and record the strength along
1011 * with the ESSID and BSSID, so that if we encounter
1012 * another AP with the same ESSID but a higher signal strength,
1013 * we will choose it - but only if the currently-connected
1014 * WLAN is at or below wireless_scan_level.
1016 (void) dladm_wlan_str2strength
1017 (cur_wlan
->nww_signal_strength
, &curr_strength
);
1019 if (curr_strength
> max_strength
) {
1020 (void) strlcpy(link
->nwamd_link_wifi_essid
,
1021 cur_wlan
->nww_essid
,
1022 sizeof (link
->nwamd_link_wifi_essid
));
1024 * Set BSSID if wireless_strict_bssid is specified or
1025 * if this is a hidden WLAN. Store the BSSID here and
1026 * then later determine the hidden WLAN's name in the
1029 if (wireless_strict_bssid
||
1030 cur_wlan
->nww_essid
[0] == '\0') {
1031 (void) strlcpy(link
->nwamd_link_wifi_bssid
,
1032 cur_wlan
->nww_bssid
,
1033 sizeof (link
->nwamd_link_wifi_bssid
));
1035 (void) strlcpy(link
->nwamd_link_wifi_signal_strength
,
1036 cur_wlan
->nww_signal_strength
,
1037 sizeof (link
->nwamd_link_wifi_signal_strength
));
1038 link
->nwamd_link_wifi_security_mode
=
1039 cur_wlan
->nww_security_mode
;
1042 (void) dladm_wlan_str2strength
1043 (link
->nwamd_link_wifi_signal_strength
, &max_strength
);
1046 return (found
? 1 : 0);
1050 nwamd_find_known_wlan(nwamd_object_t ncu_obj
)
1052 nwamd_ncu_t
*ncu
= ncu_obj
->nwamd_object_data
;
1056 * Walk known WLANs, finding lowest priority (preferred) WLAN
1057 * in our scan results.
1059 (void) nwam_walk_known_wlans(find_best_wlan_cb
, ncu
,
1060 NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER
, &ret
);
1066 * WLAN scan code for WIFI link NCUs.
1069 /* Create periodic scan event for object. Called with object lock held. */
1071 nwamd_ncu_create_periodic_scan_event(nwamd_object_t ncu_obj
)
1073 nwamd_event_t scan_event
;
1075 if (wireless_scan_interval
== 0) {
1076 nlog(LOG_DEBUG
, "nwamd_ncu_create_periodic_scan_event: "
1077 "wireless_scan_interval set to 0 so no periodic scanning");
1080 scan_event
= nwamd_event_init(NWAM_EVENT_TYPE_PERIODIC_SCAN
,
1081 NWAM_OBJECT_TYPE_NCU
, 0, ncu_obj
->nwamd_object_name
);
1082 if (scan_event
!= NULL
) {
1083 nwamd_event_enqueue_timed(scan_event
,
1084 wireless_scan_interval
> WIRELESS_SCAN_INTERVAL_MIN
?
1085 wireless_scan_interval
: WIRELESS_SCAN_INTERVAL_MIN
);
1089 /* Handle periodic scan event (which puts link into WIFI_INIT state */
1091 nwamd_ncu_handle_periodic_scan_event(nwamd_event_t event
)
1093 nwamd_object_t ncu_obj
;
1096 ncu_obj
= nwamd_object_find(NWAM_OBJECT_TYPE_NCU
,
1097 event
->event_object
);
1098 if (ncu_obj
== NULL
) {
1099 nlog(LOG_ERR
, "nwamd_ncu_handle_periodic_scan_event: "
1100 "no object %s", event
->event_object
);
1103 ncu
= ncu_obj
->nwamd_object_data
;
1105 /* Only rescan if state is offline* or online */
1106 nlog(LOG_DEBUG
, "nwamd_ncu_handle_periodic_scan_event: doing rescan..");
1108 if (ncu_obj
->nwamd_object_state
== NWAM_STATE_OFFLINE_TO_ONLINE
||
1109 ncu_obj
->nwamd_object_state
== NWAM_STATE_ONLINE
) {
1110 /* rescan, then create periodic scan event */
1111 (void) nwamd_wlan_scan(ncu
->ncu_name
);
1112 nwamd_ncu_create_periodic_scan_event(ncu_obj
);
1114 nwamd_object_release(ncu_obj
);
1118 get_scan_results(void *arg
, dladm_wlan_attr_t
*attrp
)
1120 nwamd_wifi_scan_t
*s
= arg
;
1121 const char *linkname
= s
->nwamd_wifi_scan_link
;
1122 char essid_name
[DLADM_STRSIZE
];
1123 char bssid_name
[DLADM_STRSIZE
];
1124 char strength
[DLADM_STRSIZE
];
1125 uint_t i
, index
= 0;
1126 boolean_t found
= B_FALSE
;
1128 (void) dladm_wlan_essid2str(&attrp
->wa_essid
, essid_name
);
1129 (void) dladm_wlan_bssid2str(&attrp
->wa_bssid
, bssid_name
);
1130 (void) dladm_wlan_strength2str(&attrp
->wa_strength
, strength
);
1132 index
= s
->nwamd_wifi_scan_curr_num
;
1133 if (index
== NWAMD_MAX_NUM_WLANS
) {
1134 nlog(LOG_ERR
, "get_scan_results: truncating WLAN scan results "
1135 "for link %s: ommiting (%s, %s)", linkname
, essid_name
,
1140 (void) strlcpy(s
->nwamd_wifi_scan_curr
[index
].nww_essid
, essid_name
,
1141 sizeof (s
->nwamd_wifi_scan_curr
[index
].nww_essid
));
1142 (void) strlcpy(s
->nwamd_wifi_scan_curr
[index
].nww_bssid
, bssid_name
,
1143 sizeof (s
->nwamd_wifi_scan_curr
[index
].nww_bssid
));
1144 (void) strlcpy(s
->nwamd_wifi_scan_curr
[index
].nww_signal_strength
,
1146 sizeof (s
->nwamd_wifi_scan_curr
[index
].nww_signal_strength
));
1147 s
->nwamd_wifi_scan_curr
[index
].nww_security_mode
= attrp
->wa_secmode
;
1148 s
->nwamd_wifi_scan_curr
[index
].nww_speed
= attrp
->wa_speed
;
1149 s
->nwamd_wifi_scan_curr
[index
].nww_channel
= attrp
->wa_channel
;
1150 s
->nwamd_wifi_scan_curr
[index
].nww_bsstype
= attrp
->wa_bsstype
;
1153 * We fill in actual values for selected/connected/key later when we
1154 * reacquire the object lock.
1156 s
->nwamd_wifi_scan_curr
[index
].nww_selected
= B_FALSE
;
1157 s
->nwamd_wifi_scan_curr
[index
].nww_connected
= B_FALSE
;
1158 s
->nwamd_wifi_scan_curr
[index
].nww_have_key
= B_FALSE
;
1159 s
->nwamd_wifi_scan_curr
[index
].nww_keyindex
= 1;
1160 s
->nwamd_wifi_scan_curr_num
++;
1162 /* Check if this AP was in previous scan results */
1163 for (i
= 0; i
< s
->nwamd_wifi_scan_last_num
; i
++) {
1164 found
= (strcmp(s
->nwamd_wifi_scan_last
[i
].nww_essid
,
1166 strcmp(s
->nwamd_wifi_scan_last
[i
].nww_bssid
,
1172 s
->nwamd_wifi_scan_changed
= B_TRUE
;
1174 nlog(LOG_DEBUG
, "get_scan_results(%s, %d): ESSID %s, BSSID %s",
1175 linkname
, index
, essid_name
, bssid_name
);
1181 * Check if we're connected to the expected WLAN, or in the case of autoconf
1182 * record the WLAN we're connected to.
1185 nwamd_wlan_connected(nwamd_object_t ncu_obj
)
1187 nwamd_ncu_t
*ncu
= ncu_obj
->nwamd_object_data
;
1188 nwamd_link_t
*link
= &ncu
->ncu_link
;
1189 dladm_wlan_linkattr_t attr
;
1190 char essid
[DLADM_STRSIZE
];
1191 char bssid
[DLADM_STRSIZE
];
1192 boolean_t connected
= B_FALSE
;
1196 * This is awful, but some wireless drivers
1197 * (particularly 'ath') will erroneously report
1198 * "disconnected" if queried right after a scan. If we
1199 * see 'down' reported here, we retry a few times to
1200 * make sure it's really down.
1202 while (retries
++ < 4) {
1203 if (dladm_wlan_get_linkattr(dld_handle
, link
->nwamd_link_id
,
1204 &attr
) != DLADM_STATUS_OK
) {
1205 attr
.la_status
= DLADM_WLAN_LINK_DISCONNECTED
;
1206 } else if (attr
.la_status
== DLADM_WLAN_LINK_CONNECTED
) {
1211 if (attr
.la_status
== DLADM_WLAN_LINK_CONNECTED
) {
1212 (void) dladm_wlan_essid2str(&attr
.la_wlan_attr
.wa_essid
, essid
);
1213 (void) dladm_wlan_bssid2str(&attr
.la_wlan_attr
.wa_bssid
, bssid
);
1215 nlog(LOG_DEBUG
, "nwamd_wlan_connected: %s connected to %s %s",
1216 ncu
->ncu_name
, essid
, bssid
);
1221 * If we're using autoconf, we have no control over what we connect to,
1222 * so rather than verifying ESSSID, simply record ESSID/BSSID.
1224 if (link
->nwamd_link_wifi_autoconf
) {
1225 (void) strlcpy(link
->nwamd_link_wifi_essid
, essid
,
1226 sizeof (link
->nwamd_link_wifi_essid
));
1227 (void) strlcpy(link
->nwamd_link_wifi_bssid
, bssid
,
1228 sizeof (link
->nwamd_link_wifi_bssid
));
1231 * Are we connected to expected WLAN? Note:
1232 * we'd like to verify BSSID, but we cannot due to CR 6772510.
1234 if (strcmp(essid
, link
->nwamd_link_wifi_essid
) == 0) {
1235 /* Update connected signal strength */
1236 (void) dladm_wlan_strength2str(&attr
.la_wlan_attr
.wa_strength
,
1237 link
->nwamd_link_wifi_signal_strength
);
1239 /* Store current BSSID */
1240 (void) strlcpy(link
->nwamd_link_wifi_bssid
, bssid
,
1241 sizeof (link
->nwamd_link_wifi_bssid
));
1243 if (attr
.la_wlan_attr
.wa_strength
< wireless_scan_level
) {
1245 * We're connected, but we've dropped below
1246 * scan threshold. Initiate a scan.
1248 nlog(LOG_DEBUG
, "nwamd_wlan_connected: "
1249 "connected but signal under threshold...");
1250 (void) nwamd_wlan_scan(ncu
->ncu_name
);
1253 } else if (strlen(essid
) == 0) {
1255 * For hidden WLANs, no ESSID is specified, so we cannot verify
1259 "nwamd_wlan_connected: connected to hidden WLAN, cannot "
1260 "verify connection details");
1263 (void) nlog(LOG_ERR
,
1264 "nwamd_wlan_connected: wrong AP on %s; expected %s %s",
1265 ncu
->ncu_name
, link
->nwamd_link_wifi_essid
,
1266 link
->nwamd_link_wifi_bssid
);
1267 (void) dladm_wlan_disconnect(dld_handle
, link
->nwamd_link_id
);
1268 link
->nwamd_link_wifi_connected
= B_FALSE
;
1274 * WLAN scan thread. Called with the per-link WiFi mutex held.
1277 wlan_scan_thread(void *arg
)
1279 char *linkname
= arg
;
1280 nwamd_object_t ncu_obj
;
1283 dladm_status_t status
;
1284 char essid
[DLADM_STRSIZE
];
1285 char bssid
[DLADM_STRSIZE
];
1286 uint32_t now
, link_id
;
1287 nwamd_wifi_scan_t s
;
1290 if ((ncu_obj
= nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK
, linkname
))
1292 nlog(LOG_ERR
, "wlan_scan_thread: could not find object "
1293 "for link %s", linkname
);
1298 ncu
= ncu_obj
->nwamd_object_data
;
1299 link
= &ncu
->ncu_link
;
1302 * It is possible multiple scan threads have queued up waiting for the
1303 * object lock. We try to prevent excessive scanning by limiting the
1304 * interval between scans to WIRELESS_SCAN_REQUESTED_INTERVAL_MIN sec.
1306 now
= NSEC_TO_SEC(gethrtime());
1307 if ((now
- link
->nwamd_link_wifi_scan
.nwamd_wifi_scan_last_time
) <
1308 WIRELESS_SCAN_REQUESTED_INTERVAL_MIN
) {
1309 nlog(LOG_DEBUG
, "wlan_scan_thread: last scan for %s "
1310 "was < %d sec ago, ignoring scan request",
1311 linkname
, WIRELESS_SCAN_REQUESTED_INTERVAL_MIN
);
1312 nwamd_object_release(ncu_obj
);
1318 * Prepare scan data - copy link name and copy previous "current"
1319 * scan results from the nwamd_link_t to the last scan results for
1320 * the next scan so that we can compare results to find if things
1321 * have changed since last time.
1323 (void) bzero(&s
, sizeof (nwamd_wifi_scan_t
));
1324 (void) strlcpy(s
.nwamd_wifi_scan_link
, ncu
->ncu_name
,
1325 sizeof (s
.nwamd_wifi_scan_link
));
1326 s
.nwamd_wifi_scan_last_num
=
1327 link
->nwamd_link_wifi_scan
.nwamd_wifi_scan_curr_num
;
1328 if (s
.nwamd_wifi_scan_last_num
> 0) {
1329 (void) memcpy(s
.nwamd_wifi_scan_last
,
1330 link
->nwamd_link_wifi_scan
.nwamd_wifi_scan_curr
,
1331 s
.nwamd_wifi_scan_last_num
* sizeof (nwam_wlan_t
));
1333 link_id
= link
->nwamd_link_id
;
1334 nwamd_object_release(ncu_obj
);
1336 nlog(LOG_DEBUG
, "wlan_scan_thread: initiating scan on %s",
1337 s
.nwamd_wifi_scan_link
);
1339 scanconnect_entry();
1340 status
= dladm_wlan_scan(dld_handle
, link_id
, &s
, get_scan_results
);
1341 s
.nwamd_wifi_scan_last_time
= NSEC_TO_SEC(gethrtime());
1342 if (!s
.nwamd_wifi_scan_changed
) {
1343 /* Scan may have lost WLANs, if so this qualifies as change */
1344 s
.nwamd_wifi_scan_changed
= (s
.nwamd_wifi_scan_curr_num
!=
1345 s
.nwamd_wifi_scan_last_num
);
1349 if (status
!= DLADM_STATUS_OK
) {
1350 nlog(LOG_ERR
, "wlan_scan_thread: cannot scan link %s",
1351 s
.nwamd_wifi_scan_link
);
1356 if ((ncu_obj
= nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK
, linkname
))
1358 nlog(LOG_ERR
, "wlan_scan_thread: could not find object "
1359 "for link %s after doing scan", linkname
);
1363 ncu
= ncu_obj
->nwamd_object_data
;
1364 link
= &ncu
->ncu_link
;
1366 /* For new scan data, add key info from known WLANs */
1367 for (i
= 0; i
< s
.nwamd_wifi_scan_curr_num
; i
++) {
1368 if (NEED_ENC(s
.nwamd_wifi_scan_curr
[i
].nww_security_mode
)) {
1369 char keyname
[NWAM_MAX_VALUE_LEN
];
1370 dladm_wlan_key_t
*key
= NULL
;
1373 * If strict_bssid is true, we start checking for
1374 * known wlans with the same BSSID.
1375 * This would prevent the selection of secobjs
1376 * that actually are referenced by different kwl
1377 * with the same ESSID.
1379 if (wireless_strict_bssid
) {
1381 (void) nwam_walk_known_wlans(bssid_match
,
1382 s
.nwamd_wifi_scan_curr
[i
].nww_bssid
, 0,
1388 if (known_wlan_get_keyname
1389 (s
.nwamd_wifi_scan_curr
[i
].nww_essid
, keyname
)
1391 (key
= nwamd_wlan_get_key_named(keyname
,
1392 s
.nwamd_wifi_scan_curr
[i
].nww_security_mode
))
1394 s
.nwamd_wifi_scan_curr
[i
].nww_have_key
=
1396 s
.nwamd_wifi_scan_curr
[i
].nww_keyindex
=
1397 s
.nwamd_wifi_scan_curr
[i
].
1398 nww_security_mode
==
1399 DLADM_WLAN_SECMODE_WEP
?
1401 nlog(LOG_DEBUG
, "found matching keyname for \
1402 %s", s
.nwamd_wifi_scan_curr
[i
].nww_bssid
);
1407 /* Copy scan data into nwamd_link_t */
1408 link
->nwamd_link_wifi_scan
= s
;
1409 /* Set selected, connected and send scan event if we've got new data */
1410 nwamd_set_selected_connected(ncu
,
1411 link
->nwamd_link_wifi_essid
[0] != '\0',
1412 link
->nwamd_link_wifi_connected
);
1415 * If wireless selection is not possible because of the current
1416 * state or priority-group, then this was just a scan request.
1417 * Nothing else to do.
1419 if (!wireless_selection_possible(ncu_obj
)) {
1420 nwamd_object_release(ncu_obj
);
1426 * Check if WLAN is on our known WLAN list. If no
1427 * previously-visited WLANs are found in scan data, set
1428 * new state to NEED_SELECTION (provided we're not currently
1429 * connected, as can be the case during a periodic scan or
1430 * monitor-triggered scan where the signal strength recovers.
1432 if (!nwamd_find_known_wlan(ncu_obj
)) {
1433 if (!nwamd_wlan_connected(ncu_obj
)) {
1434 if (link
->nwamd_link_wifi_connected
) {
1435 nlog(LOG_DEBUG
, "wlan_scan_thread: "
1436 "unexpected disconnect after scan");
1437 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1438 ncu_obj
->nwamd_object_name
,
1439 NWAM_STATE_ONLINE_TO_OFFLINE
,
1440 NWAM_AUX_STATE_DOWN
);
1442 nlog(LOG_DEBUG
, "wlan_scan_thread: "
1443 "no known WLANs - ask user");
1444 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1445 ncu_obj
->nwamd_object_name
,
1446 NWAM_STATE_OFFLINE_TO_ONLINE
,
1447 NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION
);
1450 /* still connected. if not online, change to online */
1451 nlog(LOG_DEBUG
, "wlan_scan_thread: still connected to "
1452 "%s %s", link
->nwamd_link_wifi_essid
,
1453 link
->nwamd_link_wifi_bssid
);
1454 if (ncu_obj
->nwamd_object_state
!= NWAM_STATE_ONLINE
) {
1455 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1456 ncu_obj
->nwamd_object_name
,
1457 NWAM_STATE_OFFLINE_TO_ONLINE
,
1461 nwamd_object_release(ncu_obj
);
1464 nlog(LOG_DEBUG
, "wlan_scan_thread: found known WLAN %s %s",
1465 link
->nwamd_link_wifi_essid
, link
->nwamd_link_wifi_bssid
);
1467 if (!nwamd_wlan_connected(ncu_obj
)) {
1468 /* Copy selected ESSID/BSSID, unlock, call select */
1469 (void) strlcpy(essid
, link
->nwamd_link_wifi_essid
,
1471 (void) strlcpy(bssid
, link
->nwamd_link_wifi_bssid
,
1473 nwamd_object_release(ncu_obj
);
1474 (void) nwamd_wlan_select(linkname
, essid
, bssid
,
1475 link
->nwamd_link_wifi_security_mode
, B_TRUE
);
1477 /* still connected. if not online, change to online */
1478 nlog(LOG_DEBUG
, "wlan_scan_thread: still connected to "
1479 "known WLAN %s %s", link
->nwamd_link_wifi_essid
,
1480 link
->nwamd_link_wifi_bssid
);
1481 if (ncu_obj
->nwamd_object_state
!= NWAM_STATE_ONLINE
) {
1482 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1483 ncu_obj
->nwamd_object_name
,
1484 NWAM_STATE_OFFLINE_TO_ONLINE
,
1487 nwamd_object_release(ncu_obj
);
1495 nwamd_wlan_scan(const char *linkname
)
1497 pthread_t wifi_thread
;
1498 char *link
= strdup(linkname
);
1501 nlog(LOG_ERR
, "nwamd_wlan_scan: out of memory");
1502 return (NWAM_NO_MEMORY
);
1505 nlog(LOG_DEBUG
, "nwamd_wlan_scan: WLAN scan for %s",
1508 if (pthread_create(&wifi_thread
, NULL
, wlan_scan_thread
,
1510 nlog(LOG_ERR
, "nwamd_wlan_scan: could not start scan");
1512 return (NWAM_ERROR_INTERNAL
);
1514 /* detach thread so that it doesn't become a zombie */
1515 (void) pthread_detach(wifi_thread
);
1516 return (NWAM_SUCCESS
);
1520 * WLAN connection code.
1523 static dladm_status_t
1524 do_connect(uint32_t link_id
, dladm_wlan_attr_t
*attrp
, dladm_wlan_key_t
*key
,
1525 uint_t keycount
, uint_t flags
)
1527 dladm_status_t status
;
1528 char errmsg
[DLADM_STRSIZE
];
1530 scanconnect_entry();
1531 status
= dladm_wlan_connect(dld_handle
, link_id
, attrp
,
1532 DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT
, key
, keycount
, flags
);
1535 nlog(LOG_DEBUG
, "nwamd_do_connect: dladm_wlan_connect returned %s",
1536 dladm_status2str(status
, errmsg
));
1542 wlan_connect_thread(void *arg
)
1544 char *linkname
= arg
;
1545 nwamd_object_t ncu_obj
;
1551 dladm_wlan_key_t
*key
= NULL
;
1552 dladm_wlan_attr_t attr
;
1553 dladm_status_t status
;
1554 boolean_t autoconf
= B_FALSE
;
1556 if ((ncu_obj
= nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK
, linkname
))
1558 nlog(LOG_ERR
, "wlan_connect_thread: could not find object "
1559 "for link %s", linkname
);
1564 ncu
= ncu_obj
->nwamd_object_data
;
1565 link
= &ncu
->ncu_link
;
1567 if (!wireless_selection_possible(ncu_obj
)) {
1568 nlog(LOG_DEBUG
, "wlan_connect_thread: %s in invalid state or "
1569 "has lower priority", ncu
->ncu_name
);
1573 /* If it is already connected to the required AP, just return. */
1574 if (nwamd_wlan_connected(ncu_obj
)) {
1575 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1576 ncu_obj
->nwamd_object_name
,
1577 ncu_obj
->nwamd_object_state
, NWAM_AUX_STATE_UP
);
1581 (void) memset(&attr
, 0, sizeof (attr
));
1582 if (dladm_wlan_str2essid(link
->nwamd_link_wifi_essid
, &attr
.wa_essid
)
1583 != DLADM_STATUS_OK
) {
1584 nlog(LOG_ERR
, "wlan_connect_thread: invalid ESSID '%s' "
1585 "for '%s'", link
->nwamd_link_wifi_essid
, ncu
->ncu_name
);
1588 attr
.wa_valid
= DLADM_WLAN_ATTR_ESSID
;
1590 /* note: bssid logic here is non-functional */
1591 if (link
->nwamd_link_wifi_bssid
[0] != '\0') {
1592 if (dladm_wlan_str2bssid(link
->nwamd_link_wifi_bssid
,
1593 &attr
.wa_bssid
) != DLADM_STATUS_OK
) {
1594 nlog(LOG_ERR
, "wlan_connect_thread: invalid BSSID '%s'",
1595 "for '%s'", link
->nwamd_link_wifi_bssid
,
1598 attr
.wa_valid
|= DLADM_WLAN_ATTR_BSSID
;
1602 /* First check for the key */
1603 if (NEED_ENC(link
->nwamd_link_wifi_security_mode
)) {
1604 if (link
->nwamd_link_wifi_key
== NULL
) {
1605 nlog(LOG_ERR
, "wlan_connect_thread: could not find "
1606 "key for WLAN '%s'", link
->nwamd_link_wifi_essid
);
1607 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1608 ncu_obj
->nwamd_object_name
,
1609 NWAM_STATE_OFFLINE_TO_ONLINE
,
1610 NWAM_AUX_STATE_LINK_WIFI_NEED_KEY
);
1613 /* Make a copy of the key as we need to unlock the object */
1614 if ((key
= calloc(1, sizeof (dladm_wlan_key_t
))) == NULL
) {
1615 nlog(LOG_ERR
, "wlan_connect_thread: out of memory");
1618 (void) memcpy(key
, link
->nwamd_link_wifi_key
,
1619 sizeof (dladm_wlan_key_t
));
1621 attr
.wa_valid
|= DLADM_WLAN_ATTR_SECMODE
;
1622 attr
.wa_secmode
= link
->nwamd_link_wifi_security_mode
;
1624 nlog(LOG_DEBUG
, "wlan_connect_thread: retrieved key");
1631 * Connect; only scan if a bssid was not specified. If it times out,
1632 * try a second time using autoconf. Drop the object lock during the
1633 * connect attempt since connecting may take some time, and access to
1634 * the link object during that period would be impossible if we held the
1638 link
->nwamd_link_wifi_autoconf
= B_FALSE
;
1639 link_id
= link
->nwamd_link_id
;
1641 nwamd_object_release(ncu_obj
);
1643 status
= do_connect(link_id
, &attr
, key
, keycount
,
1644 DLADM_WLAN_CONNECT_NOSCAN
);
1645 if (status
!= DLADM_STATUS_OK
) {
1646 /* Connect failed, try autoconf */
1647 if (!wireless_autoconf
|| (status
= do_connect(link_id
, &attr
,
1648 NULL
, 0, 0)) != DLADM_STATUS_OK
) {
1649 nlog(LOG_ERR
, "wlan_connect_thread: connect failed for "
1653 if (status
== DLADM_STATUS_OK
)
1657 /* Connect succeeded, reacquire object */
1658 if ((ncu_obj
= nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK
, linkname
))
1660 nlog(LOG_ERR
, "wlan_connect_thread: could not find object "
1661 "for link %s", linkname
);
1665 ncu
= ncu_obj
->nwamd_object_data
;
1666 link
= &ncu
->ncu_link
;
1669 link
->nwamd_link_wifi_autoconf
= B_TRUE
;
1672 * If WLAN is WEP/WPA, we would like to test the connection as the key
1673 * may be wrong. It is difficult to find a reliable test that works
1674 * across APs however. Do nothing for now.
1676 link
->nwamd_link_wifi_connected
= nwamd_wlan_connected(ncu_obj
);
1678 if (link
->nwamd_link_wifi_connected
) {
1679 if (link
->nwamd_link_wifi_add_to_known_wlans
) {
1680 /* add to known WLANs */
1681 nlog(LOG_DEBUG
, "wlan_connect_thread: "
1682 "add '%s' to known WLANs",
1683 link
->nwamd_link_wifi_essid
);
1684 if ((err
= nwam_known_wlan_add_to_known_wlans
1685 (link
->nwamd_link_wifi_essid
,
1686 link
->nwamd_link_wifi_bssid
[0] != '\0' ?
1687 link
->nwamd_link_wifi_bssid
: NULL
,
1688 link
->nwamd_link_wifi_security_mode
,
1689 link
->nwamd_link_wifi_security_mode
==
1690 DLADM_WLAN_SECMODE_WEP
?
1691 (uint_t
)link
->nwamd_link_wifi_key
->wk_idx
: 1,
1692 NEED_ENC(link
->nwamd_link_wifi_security_mode
) ?
1693 link
->nwamd_link_wifi_keyname
: NULL
))
1695 nlog(LOG_ERR
, "wlan_connect_thread: "
1696 "could not add to known WLANs: %s",
1697 nwam_strerror(err
));
1700 nwamd_set_selected_connected(ncu
, B_TRUE
, B_TRUE
);
1701 nlog(LOG_DEBUG
, "wlan_connect_thread: connect "
1702 "succeeded, setting state online");
1703 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1704 ncu_obj
->nwamd_object_name
, NWAM_STATE_ONLINE
,
1709 nwamd_object_release(ncu_obj
);
1718 nwamd_wlan_connect(const char *linkname
)
1720 pthread_t wifi_thread
;
1721 char *link
= strdup(linkname
);
1724 nlog(LOG_ERR
, "nwamd_wlan_connect: out of memory");
1728 nlog(LOG_DEBUG
, "nwamd_wlan_connect: WLAN connect for %s",
1731 if (pthread_create(&wifi_thread
, NULL
, wlan_connect_thread
, link
) != 0)
1732 nlog(LOG_ERR
, "nwamd_wlan_connect: could not start connect");
1734 /* detach thread so that it doesn't become a zombie */
1735 (void) pthread_detach(wifi_thread
);
1739 * Launch signal strength-monitoring thread which periodically
1740 * checks connection and signal strength. If we become disconnected
1741 * or signal drops below threshold specified by wireless_scan_level,
1742 * initiate a scan. The scan initiation is taken care of by
1743 * the call to nwamd_wlan_connected().
1746 wlan_monitor_signal_thread(void *arg
)
1748 char *linkname
= arg
;
1749 nwamd_object_t ncu_obj
;
1752 boolean_t first_time
= B_TRUE
;
1755 if ((ncu_obj
= nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK
,
1756 linkname
)) == NULL
) {
1757 nlog(LOG_ERR
, "wlan_monitor_signal_thread: could "
1758 "not find object for link %s", linkname
);
1761 ncu
= ncu_obj
->nwamd_object_data
;
1762 link
= &ncu
->ncu_link
;
1764 /* If the NCU is DISABLED/OFFLINE, exit the monitoring thread */
1765 if (ncu_obj
->nwamd_object_state
== NWAM_STATE_OFFLINE
||
1766 ncu_obj
->nwamd_object_state
== NWAM_STATE_DISABLED
) {
1767 nlog(LOG_INFO
, "wlan_monitor_signal_thread: "
1768 "%s is %s, stopping thread", linkname
,
1769 nwam_state_to_string(ncu_obj
->nwamd_object_state
));
1770 link
->nwamd_link_wifi_monitor_thread
= 0;
1771 nwamd_object_release(ncu_obj
);
1776 * First time thru loop, we check if there is another
1777 * link monitoring thread in operation - if so exit this
1781 first_time
= B_FALSE
;
1783 if (link
->nwamd_link_wifi_monitor_thread
!= 0) {
1784 /* Already have a monitor thread for link? */
1785 nwamd_object_release(ncu_obj
);
1788 link
->nwamd_link_wifi_monitor_thread
=
1792 if (!nwamd_wlan_connected(ncu_obj
)) {
1793 nlog(LOG_ERR
, "wlan_monitor_signal_thread: "
1794 "disconnect occured for WLAN on link %s", linkname
);
1795 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1796 ncu_obj
->nwamd_object_name
,
1797 NWAM_STATE_ONLINE_TO_OFFLINE
,
1798 NWAM_AUX_STATE_DOWN
);
1799 link
->nwamd_link_wifi_monitor_thread
= 0;
1800 nwamd_object_release(ncu_obj
);
1803 nwamd_object_release(ncu_obj
);
1804 (void) sleep(WIRELESS_MONITOR_SIGNAL_INTERVAL
);
1812 nwamd_wlan_monitor_signal(const char *linkname
)
1814 pthread_t wifi_thread
;
1815 char *link
= strdup(linkname
);
1818 nlog(LOG_ERR
, "nwamd_wlan_monitor_signal: out of memory");
1822 nlog(LOG_DEBUG
, "nwamd_wlan_monitor_signal: WLAN monitor for %s",
1825 if (pthread_create(&wifi_thread
, NULL
, wlan_monitor_signal_thread
,
1827 nlog(LOG_ERR
, "nwamd_wlan_monitor_signal: could not monitor "
1833 /* detach thread so that it doesn't become a zombie */
1834 (void) pthread_detach(wifi_thread
);
1838 nwamd_ncu_handle_link_state_event(nwamd_event_t event
)
1841 nwamd_object_t ncu_obj
;
1845 ncu_obj
= nwamd_object_find(NWAM_OBJECT_TYPE_NCU
, event
->event_object
);
1846 if (ncu_obj
== NULL
) {
1847 nlog(LOG_INFO
, "nwamd_ncu_handle_link_state_event: no object "
1848 "%s", event
->event_object
);
1849 nwamd_event_do_not_send(event
);
1852 ncu
= ncu_obj
->nwamd_object_data
;
1853 link
= &ncu
->ncu_link
;
1854 evm
= event
->event_msg
;
1857 * We ignore link state events for WiFi because it is very flaky.
1858 * Instead we use the monitor thread and drive WiFi state changes from
1861 if (link
->nwamd_link_media
== DL_WIFI
) {
1862 nwamd_object_release(ncu_obj
);
1867 * If it's a link up event and we're not disabled, go online.
1869 if (evm
->nwe_data
.nwe_link_state
.nwe_link_up
&&
1870 ncu_obj
->nwamd_object_state
!= NWAM_STATE_DISABLED
) {
1872 if (link
->nwamd_link_activation_mode
==
1873 NWAM_ACTIVATION_MODE_PRIORITIZED
) {
1874 int64_t priority_group
;
1876 (void) pthread_mutex_lock(&active_ncp_mutex
);
1877 priority_group
= current_ncu_priority_group
;
1878 (void) pthread_mutex_unlock(&active_ncp_mutex
);
1880 /* compare priority groups */
1881 if (link
->nwamd_link_priority_group
> priority_group
) {
1883 "nwamd_ncu_handle_link_state_event: "
1884 "got LINK UP event for priority group "
1885 "%lld, less preferred than current %lld, "
1887 link
->nwamd_link_priority_group
,
1890 } else if (link
->nwamd_link_priority_group
==
1893 "nwamd_ncu_handle_link_state_event: "
1894 "got LINK UP event for priority group "
1895 "%lld, same as current %lld",
1896 link
->nwamd_link_priority_group
,
1899 * Change link state to UP. It will be
1900 * propagated to IP state machine. Only do
1901 * the NCU check if and when the interface
1904 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1905 event
->event_object
,
1906 NWAM_STATE_OFFLINE_TO_ONLINE
,
1910 "nwamd_ncu_handle_link_state_event: "
1911 "got LINK UP event for priority group "
1912 "%lld, more preferred than current %lld",
1913 link
->nwamd_link_priority_group
,
1917 * We need to mark the link as up so that when
1918 * it is activated we will bring the interface
1921 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1922 event
->event_object
,
1923 NWAM_STATE_OFFLINE_TO_ONLINE
,
1925 nwamd_object_release(ncu_obj
);
1926 nwamd_ncp_deactivate_priority_group
1928 nwamd_ncp_activate_priority_group
1929 (link
->nwamd_link_priority_group
);
1933 } else if (link
->nwamd_link_activation_mode
==
1934 NWAM_ACTIVATION_MODE_MANUAL
) {
1935 nlog(LOG_DEBUG
, "nwamd_ncu_handle_link_state_event: "
1936 "got LINK UP event for manual NCU %s",
1937 ncu_obj
->nwamd_object_name
);
1939 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1940 event
->event_object
, NWAM_STATE_OFFLINE_TO_ONLINE
,
1946 * If the link is down then start or continue transition down.
1948 if (!evm
->nwe_data
.nwe_link_state
.nwe_link_up
&&
1949 (ncu_obj
->nwamd_object_state
== NWAM_STATE_ONLINE
||
1950 ncu_obj
->nwamd_object_state
== NWAM_STATE_OFFLINE_TO_ONLINE
)) {
1952 if (link
->nwamd_link_activation_mode
==
1953 NWAM_ACTIVATION_MODE_PRIORITIZED
) {
1955 "nwamd_ncu_handle_link_state_event: "
1956 "got LINK DOWN for priority group %lld",
1957 link
->nwamd_link_priority_group
);
1958 /* Moving to offline checks priority group */
1960 nlog(LOG_DEBUG
, "nwamd_ncu_handle_link_state_event: "
1961 "got LINK DOWN event for manual NCU %s",
1962 ncu_obj
->nwamd_object_name
);
1964 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU
,
1965 event
->event_object
, NWAM_STATE_ONLINE_TO_OFFLINE
,
1966 NWAM_AUX_STATE_DOWN
);
1969 nwamd_object_release(ncu_obj
);