1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <assert.h> 27 #include <ctype.h> 28 #include <err.h> 29 #include <errno.h> 30 #include <execinfo.h> 31 #include <kstat.h> 32 #include <libdladm.h> 33 #include <libdllink.h> 34 #include <libdlstat.h> 35 #include <libdlwlan.h> 36 #include <libnwam.h> 37 #include <limits.h> 38 #include <pthread.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <strings.h> 43 #include <sys/stat.h> 44 #include <sys/time.h> 45 #include <sys/types.h> 46 #include <unistd.h> 47 #include <libdlpi.h> 48 #include <ucontext.h> 49 50 #include "events.h" 51 #include "llp.h" 52 #include "objects.h" 53 #include "ncp.h" 54 #include "ncu.h" 55 #include "known_wlans.h" 56 #include "util.h" 57 58 /* 59 * ncu_phys.c - contains routines that are physical-link specific. 60 * Mostly WiFi code. 61 */ 62 63 /* 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. 68 */ 69 link_state_t 70 nwamd_get_link_state(const char *name) 71 { 72 kstat_ctl_t *kcp; 73 kstat_t *ksp; 74 char module[DLPI_LINKNAME_MAX]; 75 uint_t instance; 76 link_state_t link_state = LINK_STATE_UNKNOWN; 77 78 if ((kcp = kstat_open()) == NULL) 79 return (link_state); 80 81 if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS) 82 goto out; 83 84 if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL) { 85 /* 86 * The kstat query could fail if the underlying MAC 87 * driver was already detached. 88 */ 89 goto out; 90 } 91 92 if (kstat_read(kcp, ksp, NULL) == -1) 93 goto out; 94 95 (void) dladm_kstat_value(ksp, "link_state", KSTAT_DATA_UINT32, 96 &link_state); 97 98 out: 99 (void) kstat_close(kcp); 100 101 return (link_state); 102 } 103 104 /* 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. 108 */ 109 void 110 nwamd_set_unset_link_properties(nwamd_ncu_t *ncu, boolean_t set) 111 { 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; 119 int retval; 120 dladm_status_t status; 121 char mtustr[DLADM_PROP_VAL_MAX]; 122 char *cp; 123 char errmsg[DLADM_STRSIZE]; 124 uint_t cnt = 1; 125 126 /* 127 * Set MTU here - either default value (if mtu == 0 indicating it has 128 * not been set) or specified value. 129 */ 130 if (mtu == 0) { 131 cp = mtustr; 132 status = dladm_get_linkprop(dld_handle, 133 ncu->ncu_link.nwamd_link_id, DLADM_PROP_VAL_DEFAULT, "mtu", 134 &cp, &cnt); 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)); 139 return; 140 } 141 } else { 142 (void) snprintf(mtustr, DLADM_PROP_VAL_MAX, "%lld", mtu); 143 } 144 145 cp = mtustr; 146 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)); 155 } 156 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)); 165 } 166 167 /* 168 * Set physical address - either factory (if link_mac_addr is NULL 169 * or we are unsetting properties) or specified MAC address string. 170 */ 171 if (addr == NULL) { 172 if ((hwaddr = calloc(1, DLPI_PHYSADDR_MAX)) == NULL) { 173 nlog(LOG_ERR, 174 "nwamd_set_unset_link_properties: malloc() failed"); 175 return; 176 } 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)); 182 free(hwaddr); 183 return; 184 } 185 } else { 186 int addrlen = hwaddrlen; 187 if ((hwaddr = _link_aton(addr, &addrlen)) == NULL) { 188 if (addrlen == -1) { 189 nlog(LOG_ERR, 190 "nwamd_set_unset_link_properties: " 191 "%s: bad address for %s", 192 addr, ncu->ncu_name); 193 return; 194 } else { 195 nlog(LOG_ERR, "nwamd_set_unset_link_properties:" 196 " malloc() failed"); 197 return; 198 } 199 } 200 hwaddrlen = addrlen; 201 } 202 203 /* 204 * Only set physical address if desired address differs from current - 205 * this avoids unnecessary chip resets for some drivers. 206 */ 207 retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, curraddr, 208 &hwaddrlen); 209 if (retval != DLPI_SUCCESS || bcmp(curraddr, hwaddr, hwaddrlen) != 0) { 210 retval = dlpi_set_physaddr(dh, DL_CURR_PHYS_ADDR, hwaddr, 211 hwaddrlen); 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)); 216 } 217 } 218 free(hwaddr); 219 } 220 221 #define WLAN_ENC(sec) \ 222 ((sec == DLADM_WLAN_SECMODE_WPA ? "WPA" : \ 223 (sec == DLADM_WLAN_SECMODE_WEP ? "WEP" : "none"))) 224 225 #define NEED_ENC(sec) \ 226 (sec == DLADM_WLAN_SECMODE_WPA || sec == DLADM_WLAN_SECMODE_WEP) 227 228 #define WIRELESS_LAN_INIT_COUNT 8 229 230 /* 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. 234 */ 235 dladm_wlan_strength_t wireless_scan_level = DLADM_WLAN_STRENGTH_WEAK; 236 237 /* 238 * The variable wireless_scan_interval specifies how often the periodic 239 * scan occurs. 240 */ 241 uint64_t wireless_scan_interval = WIRELESS_SCAN_INTERVAL_DEFAULT; 242 243 /* 244 * The variable wireless_autoconf specifies if we use dladm_wlan_autoconf() 245 * to connect. 246 */ 247 boolean_t wireless_autoconf = B_FALSE; 248 249 /* 250 * The variable wireless_strict_bssid specifies if we only connect 251 * to WLANs with BSSIDs that we previously connected to. 252 */ 253 boolean_t wireless_strict_bssid = B_FALSE; 254 255 /* 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. 259 */ 260 pthread_mutex_t wireless_mutex = PTHREAD_MUTEX_INITIALIZER; 261 262 static void 263 scanconnect_entry(void) 264 { 265 (void) pthread_mutex_lock(&wireless_mutex); 266 } 267 268 static void 269 scanconnect_exit(void) 270 { 271 (void) pthread_mutex_unlock(&wireless_mutex); 272 } 273 274 /* 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(). 278 */ 279 280 /* 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. 284 */ 285 static int 286 key_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp, 287 dladm_secobj_class_t class) 288 { 289 size_t buf_len = strlen(buf); 290 291 nlog(LOG_DEBUG, "before: key_string_to_secobj_value: buf_len = %d", 292 buf_len); 293 if (buf_len == 0) { 294 /* length zero means "delete" */ 295 return (0); 296 } 297 298 if (buf[buf_len - 1] == '\n') 299 buf[--buf_len] = '\0'; 300 301 nlog(LOG_DEBUG, "after: key_string_to_secobj_value: buf_len = %d", 302 buf_len); 303 304 if (class == DLADM_SECOBJ_CLASS_WPA) { 305 /* 306 * Per IEEE802.11i spec, the Pre-shared key (PSK) length should 307 * be between 8 and 63. 308 */ 309 if (buf_len < 8 || buf_len > 63) { 310 nlog(LOG_ERR, 311 "key_string_to_secobj_value:" 312 " invalid WPA key length: buf_len = %d", buf_len); 313 return (-1); 314 } 315 (void) memcpy(obj_val, buf, (uint_t)buf_len); 316 *obj_lenp = buf_len; 317 return (0); 318 } 319 320 switch (buf_len) { 321 case 5: /* ASCII key sizes */ 322 case 13: 323 (void) memcpy(obj_val, buf, (uint_t)buf_len); 324 *obj_lenp = (uint_t)buf_len; 325 break; 326 case 10: 327 case 26: /* Hex key sizes, not preceded by 0x */ 328 if (hexascii_to_octet(buf, (uint_t)buf_len, obj_val, obj_lenp) 329 != 0) { 330 nlog(LOG_ERR, 331 "key_string_to_secobj_value: invalid WEP key"); 332 return (-1); 333 } 334 break; 335 case 12: 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, 339 obj_lenp) != 0) { 340 nlog(LOG_ERR, 341 "key_string_to_secobj_value: invalid WEP key"); 342 return (-1); 343 } 344 break; 345 default: 346 syslog(LOG_ERR, 347 "key_string_to_secobj_value: invalid WEP key length"); 348 return (-1); 349 } 350 return (0); 351 } 352 353 /* 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. 357 */ 358 359 static int 360 find_keyname_cb(nwam_known_wlan_handle_t kwh, void *new_keyname) 361 { 362 nwam_error_t err; 363 nwam_value_t old_key; 364 365 char **old_keyname; 366 uint_t num_old_keyname, i; 367 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", 371 nwam_strerror(err)); 372 return (0); 373 } 374 if ((err = nwam_value_get_string_array(old_key, &old_keyname, 375 &num_old_keyname)) 376 != NWAM_SUCCESS) { 377 nlog(LOG_ERR, "find_keyname_cb: nwam_value_get_string: %s", 378 nwam_strerror(err)); 379 nwam_value_free(old_key); 380 return (0); 381 } 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 */ 386 return (1); 387 } 388 return (0); 389 } 390 391 /* 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. 396 * 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.) 400 */ 401 void 402 nwamd_set_key_name(const char *essid, const char *bssid, char *name, size_t nsz) 403 { 404 int i, j; 405 char secobj_name[DLADM_WLAN_MAX_KEYNAME_LEN]; 406 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", 410 essid); 411 } else { 412 (void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s-%s", 413 essid, bssid); 414 } 415 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') { 420 if (j == nsz - 1) 421 break; 422 423 if (secobj_name[i] == ':') { 424 name[j] = '.'; 425 j++; 426 } else if (isalnum(secobj_name[i]) || 427 secobj_name[i] == '.' || secobj_name[i] == '-' || 428 secobj_name[i] == '_') { 429 name[j] = secobj_name[i]; 430 j++; 431 } 432 i++; 433 } 434 name[j] = '\0'; 435 } 436 437 nwam_error_t 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) 440 { 441 nwamd_object_t ncu_obj; 442 nwamd_ncu_t *ncu; 443 nwamd_link_t *link; 444 int ret = 0; 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; 452 453 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 454 == NULL) { 455 nlog(LOG_ERR, "nwamd_wlan_set_key: could not find object " 456 "for link %s", linkname); 457 return (NWAM_ENTITY_NOT_FOUND); 458 } 459 ncu = ncu_obj->nwamd_object_data; 460 link = &ncu->ncu_link; 461 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, 465 class) != 0) { 466 /* above function logs internally on failure */ 467 nwamd_object_release(ncu_obj); 468 return (NWAM_ERROR_INTERNAL); 469 } 470 471 nlog(LOG_DEBUG, "nwamd_wlan_set_key: running for link %s", linkname); 472 /* 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) 476 */ 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); 479 /* 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 483 */ 484 if (ret == 1) { 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, 490 sizeof (obj_name)); 491 free(old_secobj); 492 } else { 493 nwamd_set_key_name(essid, NULL, obj_name, 494 sizeof (obj_name)); 495 } 496 nlog(LOG_DEBUG, "store_key: obj_name is %s", obj_name); 497 498 /* 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. 502 */ 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); 511 } 512 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); 517 } 518 519 status = dladm_set_secobj(dld_handle, obj_name, class, 520 obj_val, obj_len, 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); 528 } 529 link->nwamd_link_wifi_key = nwamd_wlan_get_key_named(obj_name, 530 security_mode); 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; 537 } 538 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); 547 break; 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); 556 break; 557 default: 558 break; 559 } 560 nwamd_object_release(ncu_obj); 561 562 return (NWAM_SUCCESS); 563 } 564 565 /* 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. 568 */ 569 dladm_wlan_key_t * 570 nwamd_wlan_get_key_named(const char *name, uint32_t security_mode) 571 { 572 dladm_status_t status; 573 char errmsg[DLADM_STRSIZE]; 574 dladm_wlan_key_t *cooked_key; 575 dladm_secobj_class_t class; 576 577 if (security_mode == DLADM_WLAN_SECMODE_NONE) 578 return (NULL); 579 580 /* 581 * Newly-allocated key must be freed by caller, or by 582 * subsequent call to nwamd_wlan_get_key_named(). 583 */ 584 if ((cooked_key = malloc(sizeof (dladm_wlan_key_t))) == NULL) { 585 nlog(LOG_ERR, "nwamd_wlan_get_key_named: malloc failed"); 586 return (NULL); 587 } 588 589 /* 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. 593 */ 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; 599 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, 603 DLADM_OPT_ACTIVE); 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, 610 DLADM_OPT_PERSIST); 611 } 612 613 switch (status) { 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); 617 break; 618 case DLADM_STATUS_NOTFOUND: 619 /* 620 * We do not want an error in the case that the secobj 621 * is not found, since we then prompt for it. 622 */ 623 free(cooked_key); 624 return (NULL); 625 default: 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)); 629 free(cooked_key); 630 return (NULL); 631 } 632 633 if (security_mode != 0) { 634 switch (class) { 635 case DLADM_SECOBJ_CLASS_WEP: 636 if (security_mode == DLADM_WLAN_SECMODE_WEP) 637 return (cooked_key); 638 break; 639 case DLADM_SECOBJ_CLASS_WPA: 640 if (security_mode == DLADM_WLAN_SECMODE_WPA) 641 return (cooked_key); 642 break; 643 default: 644 /* shouldn't happen */ 645 nlog(LOG_ERR, "nwamd_wlan_get_key: invalid class %d", 646 class); 647 break; 648 } 649 /* key type mismatch */ 650 nlog(LOG_ERR, "nwamd_wlan_get_key: key type mismatch" 651 " from secure object '%s'", cooked_key->wk_name); 652 free(cooked_key); 653 return (NULL); 654 } 655 656 return (cooked_key); 657 } 658 659 static dladm_wlan_key_t * 660 nwamd_wlan_get_key(const char *essid, const char *bssid, uint32_t security_mode) 661 { 662 char keyname[DLADM_SECOBJ_NAME_MAX]; 663 664 nwamd_set_key_name(essid, bssid, keyname, DLADM_SECOBJ_NAME_MAX); 665 666 return (nwamd_wlan_get_key_named(keyname, security_mode)); 667 } 668 669 /* 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. 674 */ 675 static boolean_t 676 wireless_selection_possible(nwamd_object_t object) 677 { 678 nwamd_ncu_t *ncu = object->nwamd_object_data; 679 680 if (ncu->ncu_link.nwamd_link_media != DL_WIFI) 681 return (B_FALSE); 682 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); 693 return (B_FALSE); 694 } 695 (void) pthread_mutex_unlock(&active_ncp_mutex); 696 697 return (B_TRUE); 698 } 699 700 /* 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 704 * to the GUI. 705 */ 706 void 707 nwamd_set_selected_connected(nwamd_ncu_t *ncu, boolean_t selected, 708 boolean_t connected) 709 { 710 nwamd_link_t *link = &ncu->ncu_link; 711 nwamd_wifi_scan_t *s = &link->nwamd_link_wifi_scan; 712 int i; 713 boolean_t trigger_scan_event = B_FALSE; 714 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)) 721 continue; 722 if (selected) { 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; 726 } else { 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; 730 } 731 if (connected) { 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; 735 } else { 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; 739 } 740 } 741 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); 750 } 751 } 752 } 753 754 /* 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. 757 */ 758 static int 759 find_bssid_cb(nwam_known_wlan_handle_t kwh, void *data) 760 { 761 nwamd_link_t *link = data; 762 nwam_error_t err; 763 nwam_value_t bssidval; 764 char **bssids, *name; 765 uint_t num_bssids, i; 766 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", 770 nwam_strerror(err)); 771 return (0); 772 } 773 if ((err = nwam_value_get_string_array(bssidval, &bssids, &num_bssids)) 774 != NWAM_SUCCESS) { 775 nlog(LOG_ERR, "find_bssid_cb: nwam_value_get_string_array: %s", 776 nwam_strerror(err)); 777 nwam_value_free(bssidval); 778 return (0); 779 } 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)) 783 != NWAM_SUCCESS) { 784 nlog(LOG_ERR, "find_bssid_cb: " 785 "nwam_known_wlan_get_name: %s", 786 nwam_strerror(err)); 787 continue; 788 } 789 (void) strlcpy(link->nwamd_link_wifi_essid, name, 790 sizeof (link->nwamd_link_wifi_essid)); 791 free(name); 792 nwam_value_free(bssidval); 793 /* Found ESSID for BSSID so terminate walk */ 794 return (1); 795 } 796 } 797 nwam_value_free(bssidval); 798 799 return (0); 800 } 801 802 /* 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. 806 */ 807 static void 808 check_if_hidden_wlan_was_visited(nwamd_link_t *link) 809 { 810 (void) nwam_walk_known_wlans(find_bssid_cb, link, 811 NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, NULL); 812 } 813 814 nwam_error_t 815 nwamd_wlan_select(const char *linkname, const char *essid, const char *bssid, 816 uint32_t security_mode, boolean_t add_to_known_wlans) 817 { 818 nwamd_object_t ncu_obj; 819 nwamd_ncu_t *ncu; 820 nwamd_link_t *link; 821 boolean_t found_key = B_FALSE; 822 823 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 824 == NULL) { 825 nlog(LOG_ERR, "nwamd_wlan_select: could not find object " 826 "for link %s", linkname); 827 return (NWAM_ENTITY_NOT_FOUND); 828 } 829 ncu = ncu_obj->nwamd_object_data; 830 link = &ncu->ncu_link; 831 832 /* 833 * If wireless selection is not possible because of the current 834 * state or priority-group, then stop. 835 */ 836 if (!wireless_selection_possible(ncu_obj)) { 837 nwamd_object_release(ncu_obj); 838 return (NWAM_ENTITY_INVALID_STATE); 839 } 840 841 /* unset selected, connected flag for previously connected wlan */ 842 nwamd_set_selected_connected(ncu, B_FALSE, B_FALSE); 843 844 /* Disconnect to allow new selection to go ahead */ 845 (void) dladm_wlan_disconnect(dld_handle, link->nwamd_link_id); 846 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; 853 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); 857 858 /* set selected flag for newly-selected WLAN */ 859 nwamd_set_selected_connected(ncu, B_TRUE, B_FALSE); 860 861 /* does this WLAN require a key? If so go to NEED_KEY */ 862 if (NEED_ENC(link->nwamd_link_wifi_security_mode)) { 863 /* 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. 867 */ 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) { 871 /* 872 * Found old key format, 873 * known wlans with similar names might exist 874 */ 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 " 880 "WLAN key %s", 881 link->nwamd_link_wifi_keyname); 882 found_key = B_TRUE; 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); 891 found_key = B_TRUE; 892 } else { 893 nlog(LOG_ERR, "nwamd_wlan_select: could not " 894 "find key for WLAN '%s'", 895 link->nwamd_link_wifi_essid); 896 } 897 } else { 898 free(link->nwamd_link_wifi_key); 899 link->nwamd_link_wifi_key = NULL; 900 link->nwamd_link_wifi_keyname[0] = '\0'; 901 } 902 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); 908 } else { 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); 912 } 913 nwamd_object_release(ncu_obj); 914 915 return (NWAM_SUCCESS); 916 } 917 918 /* 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). 921 */ 922 static int 923 bssid_match(nwam_known_wlan_handle_t kwh, void *bssid) 924 { 925 nwam_value_t bssidsval; 926 nwam_error_t err; 927 char **bssids; 928 uint_t nelem, i; 929 int found = 0; 930 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)); 934 return (0); 935 } 936 if ((err = nwam_value_get_string_array(bssidsval, &bssids, &nelem)) 937 != NWAM_SUCCESS) { 938 nwam_value_free(bssidsval); 939 return (0); 940 } 941 for (i = 0; i < nelem; i++) { 942 if (strcmp((const char *)bssid, bssids[i]) == 0) { 943 found = 1; 944 break; 945 } 946 } 947 nwam_value_free(bssidsval); 948 949 return (found); 950 } 951 952 /* Find most prioritized AP with strongest signal in scan data. */ 953 static int 954 find_best_wlan_cb(nwam_known_wlan_handle_t kwh, void *data) 955 { 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; 959 nwam_error_t err; 960 char *name = NULL; 961 int i; 962 dladm_wlan_strength_t curr_strength = 0; 963 dladm_wlan_strength_t max_strength = 0; 964 boolean_t found = B_FALSE; 965 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", 968 nwam_strerror(err)); 969 return (0); 970 } 971 972 if (link->nwamd_link_wifi_connected) { 973 (void) dladm_wlan_str2strength 974 (link->nwamd_link_wifi_signal_strength, &curr_strength); 975 } 976 977 /* 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(8). 982 */ 983 if (curr_strength >= wireless_scan_level && 984 link->nwamd_link_wifi_connected) { 985 free(name); 986 return (1); 987 } 988 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); 992 993 /* 994 * We need to either match the scanned essid, or in the case 995 * where the essid was not broadcast, match the scanned bssid. 996 */ 997 if (strcmp(cur_wlan->nww_essid, name) != 0 && 998 !(cur_wlan->nww_essid[0] == '\0' && b_match)) 999 continue; 1000 /* 1001 * If wireless_strict_bssid is specified, need to match 1002 * BSSID too. 1003 */ 1004 if (wireless_strict_bssid && !b_match) 1005 continue; 1006 /* 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. 1015 */ 1016 (void) dladm_wlan_str2strength 1017 (cur_wlan->nww_signal_strength, &curr_strength); 1018 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)); 1023 /* 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 1027 * connect thread. 1028 */ 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)); 1034 } 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; 1040 found = B_TRUE; 1041 } 1042 (void) dladm_wlan_str2strength 1043 (link->nwamd_link_wifi_signal_strength, &max_strength); 1044 } 1045 free(name); 1046 return (found ? 1 : 0); 1047 } 1048 1049 static boolean_t 1050 nwamd_find_known_wlan(nwamd_object_t ncu_obj) 1051 { 1052 nwamd_ncu_t *ncu = ncu_obj->nwamd_object_data; 1053 int ret; 1054 1055 /* 1056 * Walk known WLANs, finding lowest priority (preferred) WLAN 1057 * in our scan results. 1058 */ 1059 (void) nwam_walk_known_wlans(find_best_wlan_cb, ncu, 1060 NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, &ret); 1061 1062 return (ret == 1); 1063 } 1064 1065 /* 1066 * WLAN scan code for WIFI link NCUs. 1067 */ 1068 1069 /* Create periodic scan event for object. Called with object lock held. */ 1070 void 1071 nwamd_ncu_create_periodic_scan_event(nwamd_object_t ncu_obj) 1072 { 1073 nwamd_event_t scan_event; 1074 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"); 1078 return; 1079 } 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); 1086 } 1087 } 1088 1089 /* Handle periodic scan event (which puts link into WIFI_INIT state */ 1090 void 1091 nwamd_ncu_handle_periodic_scan_event(nwamd_event_t event) 1092 { 1093 nwamd_object_t ncu_obj; 1094 nwamd_ncu_t *ncu; 1095 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); 1101 return; 1102 } 1103 ncu = ncu_obj->nwamd_object_data; 1104 1105 /* Only rescan if state is offline* or online */ 1106 nlog(LOG_DEBUG, "nwamd_ncu_handle_periodic_scan_event: doing rescan.."); 1107 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); 1113 } 1114 nwamd_object_release(ncu_obj); 1115 } 1116 1117 static boolean_t 1118 get_scan_results(void *arg, dladm_wlan_attr_t *attrp) 1119 { 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; 1127 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); 1131 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, 1136 bssid_name); 1137 return (B_TRUE); 1138 } 1139 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, 1145 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; 1151 1152 /* 1153 * We fill in actual values for selected/connected/key later when we 1154 * reacquire the object lock. 1155 */ 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++; 1161 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, 1165 essid_name) == 0 && 1166 strcmp(s->nwamd_wifi_scan_last[i].nww_bssid, 1167 bssid_name) == 0); 1168 if (found) 1169 break; 1170 } 1171 if (!found) 1172 s->nwamd_wifi_scan_changed = B_TRUE; 1173 1174 nlog(LOG_DEBUG, "get_scan_results(%s, %d): ESSID %s, BSSID %s", 1175 linkname, index, essid_name, bssid_name); 1176 1177 return (B_TRUE); 1178 } 1179 1180 /* 1181 * Check if we're connected to the expected WLAN, or in the case of autoconf 1182 * record the WLAN we're connected to. 1183 */ 1184 boolean_t 1185 nwamd_wlan_connected(nwamd_object_t ncu_obj) 1186 { 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; 1193 int retries = 0; 1194 1195 /* 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. 1201 */ 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) { 1207 break; 1208 } 1209 } 1210 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); 1214 connected = B_TRUE; 1215 nlog(LOG_DEBUG, "nwamd_wlan_connected: %s connected to %s %s", 1216 ncu->ncu_name, essid, bssid); 1217 } else { 1218 return (B_FALSE); 1219 } 1220 /* 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. 1223 */ 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)); 1229 } 1230 /* 1231 * Are we connected to expected WLAN? Note: 1232 * we'd like to verify BSSID, but we cannot due to CR 6772510. 1233 */ 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); 1238 1239 /* Store current BSSID */ 1240 (void) strlcpy(link->nwamd_link_wifi_bssid, bssid, 1241 sizeof (link->nwamd_link_wifi_bssid)); 1242 1243 if (attr.la_wlan_attr.wa_strength < wireless_scan_level) { 1244 /* 1245 * We're connected, but we've dropped below 1246 * scan threshold. Initiate a scan. 1247 */ 1248 nlog(LOG_DEBUG, "nwamd_wlan_connected: " 1249 "connected but signal under threshold..."); 1250 (void) nwamd_wlan_scan(ncu->ncu_name); 1251 } 1252 return (connected); 1253 } else if (strlen(essid) == 0) { 1254 /* 1255 * For hidden WLANs, no ESSID is specified, so we cannot verify 1256 * WLAN name. 1257 */ 1258 nlog(LOG_DEBUG, 1259 "nwamd_wlan_connected: connected to hidden WLAN, cannot " 1260 "verify connection details"); 1261 return (connected); 1262 } else { 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; 1269 return (B_FALSE); 1270 } 1271 } 1272 1273 /* 1274 * WLAN scan thread. Called with the per-link WiFi mutex held. 1275 */ 1276 static void * 1277 wlan_scan_thread(void *arg) 1278 { 1279 char *linkname = arg; 1280 nwamd_object_t ncu_obj; 1281 nwamd_ncu_t *ncu; 1282 nwamd_link_t *link; 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; 1288 int i; 1289 1290 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 1291 == NULL) { 1292 nlog(LOG_ERR, "wlan_scan_thread: could not find object " 1293 "for link %s", linkname); 1294 free(linkname); 1295 return (NULL); 1296 } 1297 1298 ncu = ncu_obj->nwamd_object_data; 1299 link = &ncu->ncu_link; 1300 1301 /* 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. 1305 */ 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); 1313 free(linkname); 1314 return (NULL); 1315 } 1316 1317 /* 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. 1322 */ 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)); 1332 } 1333 link_id = link->nwamd_link_id; 1334 nwamd_object_release(ncu_obj); 1335 1336 nlog(LOG_DEBUG, "wlan_scan_thread: initiating scan on %s", 1337 s.nwamd_wifi_scan_link); 1338 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); 1346 } 1347 scanconnect_exit(); 1348 1349 if (status != DLADM_STATUS_OK) { 1350 nlog(LOG_ERR, "wlan_scan_thread: cannot scan link %s", 1351 s.nwamd_wifi_scan_link); 1352 free(linkname); 1353 return (NULL); 1354 } 1355 1356 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 1357 == NULL) { 1358 nlog(LOG_ERR, "wlan_scan_thread: could not find object " 1359 "for link %s after doing scan", linkname); 1360 free(linkname); 1361 return (NULL); 1362 } 1363 ncu = ncu_obj->nwamd_object_data; 1364 link = &ncu->ncu_link; 1365 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; 1371 1372 /* 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. 1378 */ 1379 if (wireless_strict_bssid) { 1380 int b_match = 0; 1381 (void) nwam_walk_known_wlans(bssid_match, 1382 s.nwamd_wifi_scan_curr[i].nww_bssid, 0, 1383 &b_match); 1384 if (b_match == 0) 1385 continue; 1386 } 1387 1388 if (known_wlan_get_keyname 1389 (s.nwamd_wifi_scan_curr[i].nww_essid, keyname) 1390 == NWAM_SUCCESS && 1391 (key = nwamd_wlan_get_key_named(keyname, 1392 s.nwamd_wifi_scan_curr[i].nww_security_mode)) 1393 != NULL) { 1394 s.nwamd_wifi_scan_curr[i].nww_have_key = 1395 B_TRUE; 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 ? 1400 key->wk_idx : 1; 1401 nlog(LOG_DEBUG, "found matching keyname for \ 1402 %s", s.nwamd_wifi_scan_curr[i].nww_bssid); 1403 free(key); 1404 } 1405 } 1406 } 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); 1413 1414 /* 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. 1418 */ 1419 if (!wireless_selection_possible(ncu_obj)) { 1420 nwamd_object_release(ncu_obj); 1421 free(linkname); 1422 return (NULL); 1423 } 1424 1425 /* 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. 1431 */ 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); 1441 } else { 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); 1448 } 1449 } else { 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, 1458 NWAM_AUX_STATE_UP); 1459 } 1460 } 1461 nwamd_object_release(ncu_obj); 1462 1463 } else { 1464 nlog(LOG_DEBUG, "wlan_scan_thread: found known WLAN %s %s", 1465 link->nwamd_link_wifi_essid, link->nwamd_link_wifi_bssid); 1466 1467 if (!nwamd_wlan_connected(ncu_obj)) { 1468 /* Copy selected ESSID/BSSID, unlock, call select */ 1469 (void) strlcpy(essid, link->nwamd_link_wifi_essid, 1470 sizeof (essid)); 1471 (void) strlcpy(bssid, link->nwamd_link_wifi_bssid, 1472 sizeof (bssid)); 1473 nwamd_object_release(ncu_obj); 1474 (void) nwamd_wlan_select(linkname, essid, bssid, 1475 link->nwamd_link_wifi_security_mode, B_TRUE); 1476 } else { 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, 1485 NWAM_AUX_STATE_UP); 1486 } 1487 nwamd_object_release(ncu_obj); 1488 } 1489 } 1490 free(linkname); 1491 return (NULL); 1492 } 1493 1494 nwam_error_t 1495 nwamd_wlan_scan(const char *linkname) 1496 { 1497 pthread_t wifi_thread; 1498 char *link = strdup(linkname); 1499 1500 if (link == NULL) { 1501 nlog(LOG_ERR, "nwamd_wlan_scan: out of memory"); 1502 return (NWAM_NO_MEMORY); 1503 } 1504 1505 nlog(LOG_DEBUG, "nwamd_wlan_scan: WLAN scan for %s", 1506 link); 1507 1508 if (pthread_create(&wifi_thread, NULL, wlan_scan_thread, 1509 link) != 0) { 1510 nlog(LOG_ERR, "nwamd_wlan_scan: could not start scan"); 1511 free(link); 1512 return (NWAM_ERROR_INTERNAL); 1513 } 1514 /* detach thread so that it doesn't become a zombie */ 1515 (void) pthread_detach(wifi_thread); 1516 return (NWAM_SUCCESS); 1517 } 1518 1519 /* 1520 * WLAN connection code. 1521 */ 1522 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) 1526 { 1527 dladm_status_t status; 1528 char errmsg[DLADM_STRSIZE]; 1529 1530 scanconnect_entry(); 1531 status = dladm_wlan_connect(dld_handle, link_id, attrp, 1532 DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT, key, keycount, flags); 1533 scanconnect_exit(); 1534 1535 nlog(LOG_DEBUG, "nwamd_do_connect: dladm_wlan_connect returned %s", 1536 dladm_status2str(status, errmsg)); 1537 1538 return (status); 1539 } 1540 1541 static void * 1542 wlan_connect_thread(void *arg) 1543 { 1544 char *linkname = arg; 1545 nwamd_object_t ncu_obj; 1546 nwamd_ncu_t *ncu; 1547 nwamd_link_t *link; 1548 nwam_error_t err; 1549 uint_t keycount; 1550 uint32_t link_id; 1551 dladm_wlan_key_t *key = NULL; 1552 dladm_wlan_attr_t attr; 1553 dladm_status_t status; 1554 boolean_t autoconf = B_FALSE; 1555 1556 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 1557 == NULL) { 1558 nlog(LOG_ERR, "wlan_connect_thread: could not find object " 1559 "for link %s", linkname); 1560 free(linkname); 1561 return (NULL); 1562 } 1563 1564 ncu = ncu_obj->nwamd_object_data; 1565 link = &ncu->ncu_link; 1566 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); 1570 goto done; 1571 } 1572 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); 1578 goto done; 1579 } 1580 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); 1586 goto done; 1587 } 1588 attr.wa_valid = DLADM_WLAN_ATTR_ESSID; 1589 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, 1596 ncu->ncu_name); 1597 } else { 1598 attr.wa_valid |= DLADM_WLAN_ATTR_BSSID; 1599 } 1600 } 1601 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); 1611 goto done; 1612 } 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"); 1616 goto done; 1617 } 1618 (void) memcpy(key, link->nwamd_link_wifi_key, 1619 sizeof (dladm_wlan_key_t)); 1620 1621 attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE; 1622 attr.wa_secmode = link->nwamd_link_wifi_security_mode; 1623 keycount = 1; 1624 nlog(LOG_DEBUG, "wlan_connect_thread: retrieved key"); 1625 } else { 1626 key = NULL; 1627 keycount = 0; 1628 } 1629 1630 /* 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 1635 * lock. 1636 */ 1637 1638 link->nwamd_link_wifi_autoconf = B_FALSE; 1639 link_id = link->nwamd_link_id; 1640 1641 nwamd_object_release(ncu_obj); 1642 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 " 1650 "%s", linkname); 1651 goto done_unlocked; 1652 } 1653 if (status == DLADM_STATUS_OK) 1654 autoconf = B_TRUE; 1655 } 1656 1657 /* Connect succeeded, reacquire object */ 1658 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 1659 == NULL) { 1660 nlog(LOG_ERR, "wlan_connect_thread: could not find object " 1661 "for link %s", linkname); 1662 goto done_unlocked; 1663 } 1664 1665 ncu = ncu_obj->nwamd_object_data; 1666 link = &ncu->ncu_link; 1667 1668 if (autoconf) 1669 link->nwamd_link_wifi_autoconf = B_TRUE; 1670 1671 /* 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. 1675 */ 1676 link->nwamd_link_wifi_connected = nwamd_wlan_connected(ncu_obj); 1677 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)) 1694 != NWAM_SUCCESS) { 1695 nlog(LOG_ERR, "wlan_connect_thread: " 1696 "could not add to known WLANs: %s", 1697 nwam_strerror(err)); 1698 } 1699 } 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, 1705 NWAM_AUX_STATE_UP); 1706 } 1707 1708 done: 1709 nwamd_object_release(ncu_obj); 1710 done_unlocked: 1711 free(linkname); 1712 free(key); 1713 1714 return (NULL); 1715 } 1716 1717 void 1718 nwamd_wlan_connect(const char *linkname) 1719 { 1720 pthread_t wifi_thread; 1721 char *link = strdup(linkname); 1722 1723 if (link == NULL) { 1724 nlog(LOG_ERR, "nwamd_wlan_connect: out of memory"); 1725 return; 1726 } 1727 1728 nlog(LOG_DEBUG, "nwamd_wlan_connect: WLAN connect for %s", 1729 link); 1730 1731 if (pthread_create(&wifi_thread, NULL, wlan_connect_thread, link) != 0) 1732 nlog(LOG_ERR, "nwamd_wlan_connect: could not start connect"); 1733 1734 /* detach thread so that it doesn't become a zombie */ 1735 (void) pthread_detach(wifi_thread); 1736 } 1737 1738 /* 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(). 1744 */ 1745 static void * 1746 wlan_monitor_signal_thread(void *arg) 1747 { 1748 char *linkname = arg; 1749 nwamd_object_t ncu_obj; 1750 nwamd_ncu_t *ncu; 1751 nwamd_link_t *link; 1752 boolean_t first_time = B_TRUE; 1753 1754 for (;;) { 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); 1759 break; 1760 } 1761 ncu = ncu_obj->nwamd_object_data; 1762 link = &ncu->ncu_link; 1763 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); 1772 break; 1773 } 1774 1775 /* 1776 * First time thru loop, we check if there is another 1777 * link monitoring thread in operation - if so exit this 1778 * thread. 1779 */ 1780 if (first_time) { 1781 first_time = B_FALSE; 1782 1783 if (link->nwamd_link_wifi_monitor_thread != 0) { 1784 /* Already have a monitor thread for link? */ 1785 nwamd_object_release(ncu_obj); 1786 break; 1787 } else { 1788 link->nwamd_link_wifi_monitor_thread = 1789 pthread_self(); 1790 } 1791 } 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); 1801 break; 1802 } 1803 nwamd_object_release(ncu_obj); 1804 (void) sleep(WIRELESS_MONITOR_SIGNAL_INTERVAL); 1805 } 1806 free(linkname); 1807 1808 return (NULL); 1809 } 1810 1811 void 1812 nwamd_wlan_monitor_signal(const char *linkname) 1813 { 1814 pthread_t wifi_thread; 1815 char *link = strdup(linkname); 1816 1817 if (link == NULL) { 1818 nlog(LOG_ERR, "nwamd_wlan_monitor_signal: out of memory"); 1819 return; 1820 } 1821 1822 nlog(LOG_DEBUG, "nwamd_wlan_monitor_signal: WLAN monitor for %s", 1823 link); 1824 1825 if (pthread_create(&wifi_thread, NULL, wlan_monitor_signal_thread, 1826 link) != 0) { 1827 nlog(LOG_ERR, "nwamd_wlan_monitor_signal: could not monitor " 1828 "link %s", link); 1829 free(link); 1830 return; 1831 } 1832 1833 /* detach thread so that it doesn't become a zombie */ 1834 (void) pthread_detach(wifi_thread); 1835 } 1836 1837 void 1838 nwamd_ncu_handle_link_state_event(nwamd_event_t event) 1839 { 1840 nwam_event_t evm; 1841 nwamd_object_t ncu_obj; 1842 nwamd_ncu_t *ncu; 1843 nwamd_link_t *link; 1844 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); 1850 return; 1851 } 1852 ncu = ncu_obj->nwamd_object_data; 1853 link = &ncu->ncu_link; 1854 evm = event->event_msg; 1855 1856 /* 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 1859 * there. 1860 */ 1861 if (link->nwamd_link_media == DL_WIFI) { 1862 nwamd_object_release(ncu_obj); 1863 return; 1864 } 1865 1866 /* 1867 * If it's a link up event and we're not disabled, go online. 1868 */ 1869 if (evm->nwe_data.nwe_link_state.nwe_link_up && 1870 ncu_obj->nwamd_object_state != NWAM_STATE_DISABLED) { 1871 1872 if (link->nwamd_link_activation_mode == 1873 NWAM_ACTIVATION_MODE_PRIORITIZED) { 1874 int64_t priority_group; 1875 1876 (void) pthread_mutex_lock(&active_ncp_mutex); 1877 priority_group = current_ncu_priority_group; 1878 (void) pthread_mutex_unlock(&active_ncp_mutex); 1879 1880 /* compare priority groups */ 1881 if (link->nwamd_link_priority_group > priority_group) { 1882 nlog(LOG_DEBUG, 1883 "nwamd_ncu_handle_link_state_event: " 1884 "got LINK UP event for priority group " 1885 "%lld, less preferred than current %lld, " 1886 "ignoring", 1887 link->nwamd_link_priority_group, 1888 priority_group); 1889 1890 } else if (link->nwamd_link_priority_group == 1891 priority_group) { 1892 nlog(LOG_DEBUG, 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, 1897 priority_group); 1898 /* 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 1902 * NCU is online. 1903 */ 1904 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1905 event->event_object, 1906 NWAM_STATE_OFFLINE_TO_ONLINE, 1907 NWAM_AUX_STATE_UP); 1908 } else { 1909 nlog(LOG_DEBUG, 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, 1914 priority_group); 1915 1916 /* 1917 * We need to mark the link as up so that when 1918 * it is activated we will bring the interface 1919 * up. 1920 */ 1921 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1922 event->event_object, 1923 NWAM_STATE_OFFLINE_TO_ONLINE, 1924 NWAM_AUX_STATE_UP); 1925 nwamd_object_release(ncu_obj); 1926 nwamd_ncp_deactivate_priority_group 1927 (priority_group); 1928 nwamd_ncp_activate_priority_group 1929 (link->nwamd_link_priority_group); 1930 return; 1931 } 1932 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); 1938 1939 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1940 event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE, 1941 NWAM_AUX_STATE_UP); 1942 } 1943 } 1944 1945 /* 1946 * If the link is down then start or continue transition down. 1947 */ 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)) { 1951 1952 if (link->nwamd_link_activation_mode == 1953 NWAM_ACTIVATION_MODE_PRIORITIZED) { 1954 nlog(LOG_DEBUG, 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 */ 1959 } else { 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); 1963 } 1964 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1965 event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE, 1966 NWAM_AUX_STATE_DOWN); 1967 } 1968 1969 nwamd_object_release(ncu_obj); 1970 } 1971