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 * Print the key name format into the appropriate field, then convert any ":" 355 * characters to ".", as ":[1-4]" is the slot indicator, which otherwise 356 * would trip us up. Invalid characters for secobj names are ignored. 357 * The fourth parameter is expected to be of size DLADM_SECOBJ_NAME_MAX. 358 * 359 * (Note that much of the system uses DLADM_WLAN_MAX_KEYNAME_LEN, which is 64 360 * rather than 32, but that dladm_get_secobj will fail if a length greater than 361 * DLD_SECOBJ_NAME_MAX is seen, and that's 32. This is all horribly broken.) 362 */ 363 void 364 nwamd_set_key_name(const char *essid, const char *bssid, char *name, size_t nsz) 365 { 366 int i, j; 367 char secobj_name[DLADM_WLAN_MAX_KEYNAME_LEN]; 368 369 /* create a concatenated string with essid and bssid */ 370 if (bssid == NULL || bssid[0] == '\0') { 371 (void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s", 372 essid); 373 } else { 374 (void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s-%s", 375 essid, bssid); 376 } 377 378 /* copy only valid chars to the return string, terminating with \0 */ 379 i = 0; /* index into secobj_name */ 380 j = 0; /* index into name */ 381 while (secobj_name[i] != '\0') { 382 if (j == nsz - 1) 383 break; 384 385 if (secobj_name[i] == ':') { 386 name[j] = '.'; 387 j++; 388 } else if (isalnum(secobj_name[i]) || 389 secobj_name[i] == '.' || secobj_name[i] == '-' || 390 secobj_name[i] == '_') { 391 name[j] = secobj_name[i]; 392 j++; 393 } 394 i++; 395 } 396 name[j] = '\0'; 397 } 398 399 nwam_error_t 400 nwamd_wlan_set_key(const char *linkname, const char *essid, const char *bssid, 401 uint32_t security_mode, uint_t keyslot, char *raw_key) 402 { 403 nwamd_object_t ncu_obj; 404 nwamd_ncu_t *ncu; 405 nwamd_link_t *link; 406 uint8_t obj_val[DLADM_SECOBJ_VAL_MAX]; 407 uint_t obj_len = sizeof (obj_val); 408 char obj_name[DLADM_SECOBJ_NAME_MAX]; 409 dladm_status_t status; 410 char errmsg[DLADM_STRSIZE]; 411 dladm_secobj_class_t class; 412 413 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 414 == NULL) { 415 nlog(LOG_ERR, "nwamd_wlan_set_key: could not find object " 416 "for link %s", linkname); 417 return (NWAM_ENTITY_NOT_FOUND); 418 } 419 ncu = ncu_obj->nwamd_object_data; 420 link = &ncu->ncu_link; 421 422 nlog(LOG_DEBUG, "nwamd_wlan_set_key: running for link %s", linkname); 423 /* 424 * Name key object for this WLAN so it can be later retrieved 425 * (name is unique for each ESSID/BSSID combination). 426 */ 427 nwamd_set_key_name(essid, bssid, obj_name, sizeof (obj_name)); 428 nlog(LOG_DEBUG, "store_key: obj_name is %s", obj_name); 429 430 class = (security_mode == DLADM_WLAN_SECMODE_WEP ? 431 DLADM_SECOBJ_CLASS_WEP : DLADM_SECOBJ_CLASS_WPA); 432 if (key_string_to_secobj_value(raw_key, obj_val, &obj_len, 433 class) != 0) { 434 /* above function logs internally on failure */ 435 nwamd_object_release(ncu_obj); 436 return (NWAM_ERROR_INTERNAL); 437 } 438 439 /* we've validated the new key, so remove the old one */ 440 status = dladm_unset_secobj(dld_handle, obj_name, 441 DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); 442 if (status != DLADM_STATUS_OK && status != DLADM_STATUS_NOTFOUND) { 443 nlog(LOG_ERR, "store_key: could not remove old secure object " 444 "'%s' for key: %s", obj_name, 445 dladm_status2str(status, errmsg)); 446 nwamd_object_release(ncu_obj); 447 return (NWAM_ERROR_INTERNAL); 448 } 449 450 /* if we're just deleting the key, then we're done */ 451 if (raw_key[0] == '\0') { 452 nwamd_object_release(ncu_obj); 453 return (NWAM_SUCCESS); 454 } 455 456 status = dladm_set_secobj(dld_handle, obj_name, class, 457 obj_val, obj_len, 458 DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_ACTIVE); 459 if (status != DLADM_STATUS_OK) { 460 nlog(LOG_ERR, "store_key: could not create secure object " 461 "'%s' for key: %s", obj_name, 462 dladm_status2str(status, errmsg)); 463 nwamd_object_release(ncu_obj); 464 return (NWAM_ERROR_INTERNAL); 465 } 466 link->nwamd_link_wifi_key = nwamd_wlan_get_key_named(obj_name, 467 security_mode); 468 (void) strlcpy(link->nwamd_link_wifi_keyname, obj_name, 469 sizeof (link->nwamd_link_wifi_keyname)); 470 link->nwamd_link_wifi_security_mode = security_mode; 471 if (security_mode == DLADM_WLAN_SECMODE_WEP) { 472 link->nwamd_link_wifi_key->wk_idx = 473 (keyslot >= 1 && keyslot <= 4) ? keyslot : 1; 474 } 475 476 /* If link NCU is offline* or online, (re)connect. */ 477 switch (ncu_obj->nwamd_object_state) { 478 case NWAM_STATE_ONLINE: 479 /* if changing the key of the connected WLAN, reconnect */ 480 if (strcmp(essid, link->nwamd_link_wifi_essid) == 0) 481 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 482 ncu_obj->nwamd_object_name, NWAM_STATE_ONLINE, 483 NWAM_AUX_STATE_LINK_WIFI_CONNECTING); 484 break; 485 case NWAM_STATE_OFFLINE_TO_ONLINE: 486 /* if we are waiting for the key, connect */ 487 if (ncu_obj->nwamd_object_aux_state == 488 NWAM_AUX_STATE_LINK_WIFI_NEED_KEY) 489 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 490 ncu_obj->nwamd_object_name, 491 NWAM_STATE_OFFLINE_TO_ONLINE, 492 NWAM_AUX_STATE_LINK_WIFI_CONNECTING); 493 break; 494 default: 495 break; 496 } 497 nwamd_object_release(ncu_obj); 498 499 return (NWAM_SUCCESS); 500 } 501 502 /* 503 * returns NULL if no key was recovered from libdladm. Passing in 504 * security mode of 0 means we don't care what key type it is. 505 */ 506 dladm_wlan_key_t * 507 nwamd_wlan_get_key_named(const char *name, uint32_t security_mode) 508 { 509 dladm_status_t status; 510 char errmsg[DLADM_STRSIZE]; 511 dladm_wlan_key_t *cooked_key; 512 dladm_secobj_class_t class; 513 514 if (security_mode == DLADM_WLAN_SECMODE_NONE) 515 return (NULL); 516 517 /* 518 * Newly-allocated key must be freed by caller, or by 519 * subsequent call to nwamd_wlan_get_key_named(). 520 */ 521 if ((cooked_key = malloc(sizeof (dladm_wlan_key_t))) == NULL) { 522 nlog(LOG_ERR, "nwamd_wlan_get_key_named: malloc failed"); 523 return (NULL); 524 } 525 526 /* 527 * Set name appropriately to retrieve key for this WLAN. Note that we 528 * cannot use the actual wk_name buffer size, as it's two times too 529 * large for dladm_get_secobj. 530 */ 531 (void) strlcpy(cooked_key->wk_name, name, DLADM_SECOBJ_NAME_MAX); 532 nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: len = %d, object = %s\n", 533 strlen(cooked_key->wk_name), cooked_key->wk_name); 534 cooked_key->wk_len = sizeof (cooked_key->wk_val); 535 cooked_key->wk_idx = 1; 536 537 /* Try the kernel first, then fall back to persistent storage. */ 538 status = dladm_get_secobj(dld_handle, cooked_key->wk_name, &class, 539 cooked_key->wk_val, &cooked_key->wk_len, 540 DLADM_OPT_ACTIVE); 541 if (status != DLADM_STATUS_OK) { 542 nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: " 543 "dladm_get_secobj(TEMP) failed: %s", 544 dladm_status2str(status, errmsg)); 545 status = dladm_get_secobj(dld_handle, cooked_key->wk_name, 546 &class, cooked_key->wk_val, &cooked_key->wk_len, 547 DLADM_OPT_PERSIST); 548 } 549 550 switch (status) { 551 case DLADM_STATUS_OK: 552 nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: " 553 "dladm_get_secobj succeeded: len %d", cooked_key->wk_len); 554 break; 555 case DLADM_STATUS_NOTFOUND: 556 /* 557 * We do not want an error in the case that the secobj 558 * is not found, since we then prompt for it. 559 */ 560 free(cooked_key); 561 return (NULL); 562 default: 563 nlog(LOG_ERR, "nwamd_wlan_get_key_named: could not get key " 564 "from secure object '%s': %s", cooked_key->wk_name, 565 dladm_status2str(status, errmsg)); 566 free(cooked_key); 567 return (NULL); 568 } 569 570 if (security_mode != 0) { 571 switch (class) { 572 case DLADM_SECOBJ_CLASS_WEP: 573 if (security_mode == DLADM_WLAN_SECMODE_WEP) 574 return (cooked_key); 575 break; 576 case DLADM_SECOBJ_CLASS_WPA: 577 if (security_mode == DLADM_WLAN_SECMODE_WPA) 578 return (cooked_key); 579 break; 580 default: 581 /* shouldn't happen */ 582 nlog(LOG_ERR, "nwamd_wlan_get_key: invalid class %d", 583 class); 584 break; 585 } 586 /* key type mismatch */ 587 nlog(LOG_ERR, "nwamd_wlan_get_key: key type mismatch" 588 " from secure object '%s'", cooked_key->wk_name); 589 free(cooked_key); 590 return (NULL); 591 } 592 593 return (cooked_key); 594 } 595 596 static dladm_wlan_key_t * 597 nwamd_wlan_get_key(const char *essid, const char *bssid, uint32_t security_mode) 598 { 599 char keyname[DLADM_SECOBJ_NAME_MAX]; 600 601 nwamd_set_key_name(essid, bssid, keyname, DLADM_SECOBJ_NAME_MAX); 602 603 return (nwamd_wlan_get_key_named(keyname, security_mode)); 604 } 605 606 /* 607 * Checks if a wireless network can be selected or not. A wireless network 608 * CANNOT be selected if the NCU is DISABLED, or the NCU is OFFLINE or 609 * ONLINE* and has lower priority than the currently active priority-group. 610 * Called with object lock held. 611 */ 612 static boolean_t 613 wireless_selection_possible(nwamd_object_t object) 614 { 615 nwamd_ncu_t *ncu = object->nwamd_object_data; 616 617 if (ncu->ncu_link.nwamd_link_media != DL_WIFI) 618 return (B_FALSE); 619 620 (void) pthread_mutex_lock(&active_ncp_mutex); 621 if (object->nwamd_object_state == NWAM_STATE_DISABLED || 622 ((object->nwamd_object_state == NWAM_STATE_OFFLINE || 623 object->nwamd_object_state == NWAM_STATE_ONLINE_TO_OFFLINE) && 624 ncu->ncu_link.nwamd_link_activation_mode == 625 NWAM_ACTIVATION_MODE_PRIORITIZED && 626 (current_ncu_priority_group == INVALID_PRIORITY_GROUP || 627 ncu->ncu_link.nwamd_link_priority_group > 628 current_ncu_priority_group))) { 629 (void) pthread_mutex_unlock(&active_ncp_mutex); 630 return (B_FALSE); 631 } 632 (void) pthread_mutex_unlock(&active_ncp_mutex); 633 634 return (B_TRUE); 635 } 636 637 /* 638 * Update the selected and/or connected values for the 639 * scan data. If these change, we need to trigger a scan 640 * event since the updated values need to be communicated 641 * to the GUI. 642 */ 643 void 644 nwamd_set_selected_connected(nwamd_ncu_t *ncu, boolean_t selected, 645 boolean_t connected) 646 { 647 nwamd_link_t *link = &ncu->ncu_link; 648 nwamd_wifi_scan_t *s = &link->nwamd_link_wifi_scan; 649 int i; 650 boolean_t trigger_scan_event = B_FALSE; 651 652 for (i = 0; i < s->nwamd_wifi_scan_curr_num; i++) { 653 if (strcmp(s->nwamd_wifi_scan_curr[i].nww_essid, 654 link->nwamd_link_wifi_essid) != 0 || 655 (link->nwamd_link_wifi_bssid[0] != '\0' && 656 strcmp(s->nwamd_wifi_scan_curr[i].nww_bssid, 657 link->nwamd_link_wifi_bssid) != 0)) 658 continue; 659 if (selected) { 660 if (!s->nwamd_wifi_scan_curr[i].nww_selected) 661 trigger_scan_event = B_TRUE; 662 s->nwamd_wifi_scan_curr[i].nww_selected = B_TRUE; 663 } else { 664 if (s->nwamd_wifi_scan_curr[i].nww_selected) 665 trigger_scan_event = B_TRUE; 666 s->nwamd_wifi_scan_curr[i].nww_selected = B_FALSE; 667 } 668 if (connected) { 669 if (!s->nwamd_wifi_scan_curr[i].nww_connected) 670 trigger_scan_event = B_TRUE; 671 s->nwamd_wifi_scan_curr[i].nww_connected = B_TRUE; 672 } else { 673 if (s->nwamd_wifi_scan_curr[i].nww_connected) 674 trigger_scan_event = B_TRUE; 675 s->nwamd_wifi_scan_curr[i].nww_connected = B_FALSE; 676 } 677 } 678 679 if (trigger_scan_event || s->nwamd_wifi_scan_changed) { 680 nwamd_event_t scan_event = nwamd_event_init_wlan 681 (ncu->ncu_name, NWAM_EVENT_TYPE_WLAN_SCAN_REPORT, connected, 682 s->nwamd_wifi_scan_curr, s->nwamd_wifi_scan_curr_num); 683 if (scan_event != NULL) { 684 /* Avoid sending same scan data multiple times */ 685 s->nwamd_wifi_scan_changed = B_FALSE; 686 nwamd_event_enqueue(scan_event); 687 } 688 } 689 } 690 691 /* 692 * Callback used on each known WLAN - if the BSSID is matched, set 693 * the ESSID of the hidden WLAN to the known WLAN name. 694 */ 695 static int 696 find_bssid_cb(nwam_known_wlan_handle_t kwh, void *data) 697 { 698 nwamd_link_t *link = data; 699 nwam_error_t err; 700 nwam_value_t bssidval; 701 char **bssids, *name; 702 uint_t num_bssids, i; 703 704 if ((err = nwam_known_wlan_get_prop_value(kwh, 705 NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidval)) != NWAM_SUCCESS) { 706 nlog(LOG_ERR, "find_bssid_cb: nwam_known_wlan_get_prop: %s", 707 nwam_strerror(err)); 708 return (0); 709 } 710 if ((err = nwam_value_get_string_array(bssidval, &bssids, &num_bssids)) 711 != NWAM_SUCCESS) { 712 nlog(LOG_ERR, "find_bssid_cb: nwam_value_get_string_array: %s", 713 nwam_strerror(err)); 714 nwam_value_free(bssidval); 715 return (0); 716 } 717 for (i = 0; i < num_bssids; i++) { 718 if (strcmp(bssids[i], link->nwamd_link_wifi_bssid) == 0) { 719 if ((err = nwam_known_wlan_get_name(kwh, &name)) 720 != NWAM_SUCCESS) { 721 nlog(LOG_ERR, "find_bssid_cb: " 722 "nwam_known_wlan_get_name: %s", 723 nwam_strerror(err)); 724 continue; 725 } 726 (void) strlcpy(link->nwamd_link_wifi_essid, name, 727 sizeof (link->nwamd_link_wifi_essid)); 728 free(name); 729 nwam_value_free(bssidval); 730 /* Found ESSID for BSSID so terminate walk */ 731 return (1); 732 } 733 } 734 nwam_value_free(bssidval); 735 736 return (0); 737 } 738 739 /* 740 * We may have encountered a BSSID for a hidden WLAN before and as a result 741 * may have a known WLAN entry with this BSSID. Walk known WLANs, searching 742 * for a BSSID match. Called with object lock held. 743 */ 744 static void 745 check_if_hidden_wlan_was_visited(nwamd_link_t *link) 746 { 747 (void) nwam_walk_known_wlans(find_bssid_cb, link, 748 NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, NULL); 749 } 750 751 nwam_error_t 752 nwamd_wlan_select(const char *linkname, const char *essid, const char *bssid, 753 uint32_t security_mode, boolean_t add_to_known_wlans) 754 { 755 nwamd_object_t ncu_obj; 756 nwamd_ncu_t *ncu; 757 nwamd_link_t *link; 758 char key[DLADM_STRSIZE]; 759 boolean_t found_old_key = B_FALSE, found_key = B_FALSE; 760 761 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 762 == NULL) { 763 nlog(LOG_ERR, "nwamd_wlan_select: could not find object " 764 "for link %s", linkname); 765 return (NWAM_ENTITY_NOT_FOUND); 766 } 767 ncu = ncu_obj->nwamd_object_data; 768 link = &ncu->ncu_link; 769 770 /* 771 * If wireless selection is not possible because of the current 772 * state or priority-group, then stop. 773 */ 774 if (!wireless_selection_possible(ncu_obj)) { 775 nwamd_object_release(ncu_obj); 776 return (NWAM_ENTITY_INVALID_STATE); 777 } 778 779 /* unset selected, connected flag for previously connected wlan */ 780 nwamd_set_selected_connected(ncu, B_FALSE, B_FALSE); 781 782 /* Disconnect to allow new selection to go ahead */ 783 (void) dladm_wlan_disconnect(dld_handle, link->nwamd_link_id); 784 785 (void) strlcpy(link->nwamd_link_wifi_essid, essid, 786 sizeof (link->nwamd_link_wifi_essid)); 787 (void) strlcpy(link->nwamd_link_wifi_bssid, bssid, 788 sizeof (link->nwamd_link_wifi_bssid)); 789 link->nwamd_link_wifi_security_mode = security_mode; 790 link->nwamd_link_wifi_add_to_known_wlans = add_to_known_wlans; 791 792 /* If this is a hidden wlan, then essid is empty */ 793 if (link->nwamd_link_wifi_essid[0] == '\0') 794 check_if_hidden_wlan_was_visited(link); 795 796 /* set selected flag for newly-selected WLAN */ 797 nwamd_set_selected_connected(ncu, B_TRUE, B_FALSE); 798 799 /* does this WLAN require a key? If so go to NEED_KEY */ 800 if (NEED_ENC(link->nwamd_link_wifi_security_mode)) { 801 /* 802 * First, if a key name may have been specified for a 803 * known WLAN. If so, use it. Otherwise, try both the 804 * new nwamd key name format (ESSID) and old (ESSID/BSSID). 805 * The user may have set the key without adding a known WLAN, 806 * so we need to try all these options to save going to 807 * NEED_KEY state. 808 */ 809 if (known_wlan_get_keyname(link->nwamd_link_wifi_essid, 810 link->nwamd_link_wifi_keyname) == NWAM_SUCCESS && 811 (link->nwamd_link_wifi_key = nwamd_wlan_get_key_named 812 (link->nwamd_link_wifi_keyname, 813 link->nwamd_link_wifi_security_mode)) != NULL) { 814 (void) known_wlan_get_keyslot 815 (link->nwamd_link_wifi_essid, 816 &link->nwamd_link_wifi_key->wk_idx); 817 nlog(LOG_DEBUG, "nwamd_wlan_select: got known WLAN " 818 "key %s, slot %d", link->nwamd_link_wifi_keyname, 819 link->nwamd_link_wifi_key->wk_idx); 820 found_key = B_TRUE; 821 } else if ((link->nwamd_link_wifi_key = nwamd_wlan_get_key 822 (link->nwamd_link_wifi_essid, NULL, 823 link->nwamd_link_wifi_security_mode)) != NULL) { 824 nwamd_set_key_name(link->nwamd_link_wifi_essid, NULL, 825 link->nwamd_link_wifi_keyname, 826 DLADM_SECOBJ_NAME_MAX); 827 nlog(LOG_DEBUG, "nwamd_wlan_select: got WLAN key %s", 828 link->nwamd_link_wifi_keyname); 829 found_key = B_TRUE; 830 } else if ((link->nwamd_link_wifi_key = nwamd_wlan_get_key 831 (link->nwamd_link_wifi_essid, link->nwamd_link_wifi_bssid, 832 link->nwamd_link_wifi_security_mode)) != NULL) { 833 /* 834 * Found old key format - prepare to save 835 * it as new ESSID-only key, but don't 836 * do it until we're released the object 837 * lock (since nwamd_wlan_set_key() 838 * takes the object lock). 839 */ 840 (void) strlcpy(key, 841 (char *)link->nwamd_link_wifi_key->wk_val, 842 link->nwamd_link_wifi_key->wk_len + 1); 843 found_old_key = B_TRUE; 844 found_key = B_TRUE; 845 nwamd_set_key_name(link->nwamd_link_wifi_essid, NULL, 846 link->nwamd_link_wifi_keyname, 847 DLADM_SECOBJ_NAME_MAX); 848 nlog(LOG_DEBUG, "nwamd_wlan_select: got old format " 849 "WLAN key, converting to %s", 850 link->nwamd_link_wifi_keyname); 851 } else { 852 nlog(LOG_ERR, "nwamd_wlan_select: could not " 853 "find key for WLAN '%s'", 854 link->nwamd_link_wifi_essid); 855 } 856 } else { 857 free(link->nwamd_link_wifi_key); 858 link->nwamd_link_wifi_key = NULL; 859 link->nwamd_link_wifi_keyname[0] = '\0'; 860 } 861 862 if (NEED_ENC(link->nwamd_link_wifi_security_mode) && !found_key) { 863 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 864 ncu_obj->nwamd_object_name, 865 NWAM_STATE_OFFLINE_TO_ONLINE, 866 NWAM_AUX_STATE_LINK_WIFI_NEED_KEY); 867 } else { 868 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 869 ncu_obj->nwamd_object_name, NWAM_STATE_OFFLINE_TO_ONLINE, 870 NWAM_AUX_STATE_LINK_WIFI_CONNECTING); 871 } 872 nwamd_object_release(ncu_obj); 873 874 if (found_old_key) { 875 (void) nwamd_wlan_set_key(linkname, essid, NULL, security_mode, 876 1, key); 877 } 878 return (NWAM_SUCCESS); 879 } 880 881 /* 882 * See if BSSID is in visited list of BSSIDs for known WLAN. Used for 883 * strict BSSID matching (depends on wireless_strict_bssid property value). 884 */ 885 static boolean_t 886 bssid_match(nwam_known_wlan_handle_t kwh, const char *bssid) 887 { 888 nwam_value_t bssidsval; 889 nwam_error_t err; 890 char **bssids; 891 uint_t nelem, i; 892 boolean_t found = B_FALSE; 893 894 if ((err = nwam_known_wlan_get_prop_value(kwh, 895 NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidsval)) != NWAM_SUCCESS) { 896 nlog(LOG_ERR, "bssid_match: %s", nwam_strerror(err)); 897 return (B_FALSE); 898 } 899 if ((err = nwam_value_get_string_array(bssidsval, &bssids, &nelem)) 900 != NWAM_SUCCESS) { 901 nwam_value_free(bssidsval); 902 return (B_FALSE); 903 } 904 for (i = 0; i < nelem; i++) { 905 if (strcmp(bssid, bssids[i]) == 0) { 906 found = B_TRUE; 907 break; 908 } 909 } 910 nwam_value_free(bssidsval); 911 912 return (found); 913 } 914 915 /* Find most prioritized AP with strongest signal in scan data. */ 916 static int 917 find_best_wlan_cb(nwam_known_wlan_handle_t kwh, void *data) 918 { 919 nwamd_ncu_t *ncu = data; 920 nwamd_link_t *link = &ncu->ncu_link; 921 nwamd_wifi_scan_t *s = &link->nwamd_link_wifi_scan; 922 nwam_error_t err; 923 char *name = NULL; 924 int i; 925 dladm_wlan_strength_t curr_strength = 0; 926 dladm_wlan_strength_t max_strength = 0; 927 boolean_t found = B_FALSE; 928 929 if ((err = nwam_known_wlan_get_name(kwh, &name)) != NWAM_SUCCESS) { 930 nlog(LOG_ERR, "find_best_wlan_cb: could not look up name: %s", 931 nwam_strerror(err)); 932 return (0); 933 } 934 935 if (link->nwamd_link_wifi_connected) { 936 (void) dladm_wlan_str2strength 937 (link->nwamd_link_wifi_signal_strength, &curr_strength); 938 } 939 940 /* 941 * If we're >= scan level, don't pick another Known WLAN if still 942 * connected (even if a Known WLAN with higher priority is available). 943 * If the user wants to connect to a different Known WLAN, it can be 944 * done from the GUI or select-wifi subcommand of nwamadm(1M). 945 */ 946 if (curr_strength >= wireless_scan_level && 947 link->nwamd_link_wifi_connected) { 948 free(name); 949 return (1); 950 } 951 952 for (i = 0; i < s->nwamd_wifi_scan_curr_num; i++) { 953 nwam_wlan_t *cur_wlan = &(s->nwamd_wifi_scan_curr[i]); 954 boolean_t b_match = bssid_match(kwh, cur_wlan->nww_bssid); 955 956 /* 957 * We need to either match the scanned essid, or in the case 958 * where the essid was not broadcast, match the scanned bssid. 959 */ 960 if (strcmp(cur_wlan->nww_essid, name) != 0 && 961 !(cur_wlan->nww_essid[0] == '\0' && b_match)) 962 continue; 963 /* 964 * If wireless_strict_bssid is specified, need to match 965 * BSSID too. 966 */ 967 if (wireless_strict_bssid && !b_match) 968 continue; 969 /* 970 * Found a match. Since we walk known WLANs in 971 * priority order, it's guaranteed to be the 972 * most prioritized. It may not be the strongest though - 973 * we continue the walk and record the strength along 974 * with the ESSID and BSSID, so that if we encounter 975 * another AP with the same ESSID but a higher signal strength, 976 * we will choose it - but only if the currently-connected 977 * WLAN is at or below wireless_scan_level. 978 */ 979 (void) dladm_wlan_str2strength 980 (cur_wlan->nww_signal_strength, &curr_strength); 981 982 if (curr_strength > max_strength) { 983 (void) strlcpy(link->nwamd_link_wifi_essid, 984 cur_wlan->nww_essid, 985 sizeof (link->nwamd_link_wifi_essid)); 986 /* 987 * Set BSSID if wireless_strict_bssid is specified or 988 * if this is a hidden WLAN. Store the BSSID here and 989 * then later determine the hidden WLAN's name in the 990 * connect thread. 991 */ 992 if (wireless_strict_bssid || 993 cur_wlan->nww_essid[0] == '\0') { 994 (void) strlcpy(link->nwamd_link_wifi_bssid, 995 cur_wlan->nww_bssid, 996 sizeof (link->nwamd_link_wifi_bssid)); 997 } 998 (void) strlcpy(link->nwamd_link_wifi_signal_strength, 999 cur_wlan->nww_signal_strength, 1000 sizeof (link->nwamd_link_wifi_signal_strength)); 1001 link->nwamd_link_wifi_security_mode = 1002 cur_wlan->nww_security_mode; 1003 found = B_TRUE; 1004 } 1005 (void) dladm_wlan_str2strength 1006 (link->nwamd_link_wifi_signal_strength, &max_strength); 1007 } 1008 free(name); 1009 return (found ? 1 : 0); 1010 } 1011 1012 static boolean_t 1013 nwamd_find_known_wlan(nwamd_object_t ncu_obj) 1014 { 1015 nwamd_ncu_t *ncu = ncu_obj->nwamd_object_data; 1016 int ret; 1017 1018 /* 1019 * Walk known WLANs, finding lowest priority (preferred) WLAN 1020 * in our scan results. 1021 */ 1022 (void) nwam_walk_known_wlans(find_best_wlan_cb, ncu, 1023 NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, &ret); 1024 1025 return (ret == 1); 1026 } 1027 1028 /* 1029 * WLAN scan code for WIFI link NCUs. 1030 */ 1031 1032 /* Create periodic scan event for object. Called with object lock held. */ 1033 void 1034 nwamd_ncu_create_periodic_scan_event(nwamd_object_t ncu_obj) 1035 { 1036 nwamd_event_t scan_event; 1037 1038 if (wireless_scan_interval == 0) { 1039 nlog(LOG_DEBUG, "nwamd_ncu_create_periodic_scan_event: " 1040 "wireless_scan_interval set to 0 so no periodic scanning"); 1041 return; 1042 } 1043 scan_event = nwamd_event_init(NWAM_EVENT_TYPE_PERIODIC_SCAN, 1044 NWAM_OBJECT_TYPE_NCU, 0, ncu_obj->nwamd_object_name); 1045 if (scan_event != NULL) { 1046 nwamd_event_enqueue_timed(scan_event, 1047 wireless_scan_interval > WIRELESS_SCAN_INTERVAL_MIN ? 1048 wireless_scan_interval : WIRELESS_SCAN_INTERVAL_MIN); 1049 } 1050 } 1051 1052 /* Handle periodic scan event (which puts link into WIFI_INIT state */ 1053 void 1054 nwamd_ncu_handle_periodic_scan_event(nwamd_event_t event) 1055 { 1056 nwamd_object_t ncu_obj; 1057 nwamd_ncu_t *ncu; 1058 1059 ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, 1060 event->event_object); 1061 if (ncu_obj == NULL) { 1062 nlog(LOG_ERR, "nwamd_ncu_handle_periodic_scan_event: " 1063 "no object %s", event->event_object); 1064 return; 1065 } 1066 ncu = ncu_obj->nwamd_object_data; 1067 1068 /* Only rescan if state is offline* or online */ 1069 nlog(LOG_DEBUG, "nwamd_ncu_handle_periodic_scan_event: doing rescan.."); 1070 1071 if (ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE || 1072 ncu_obj->nwamd_object_state == NWAM_STATE_ONLINE) { 1073 /* rescan, then create periodic scan event */ 1074 (void) nwamd_wlan_scan(ncu->ncu_name); 1075 nwamd_ncu_create_periodic_scan_event(ncu_obj); 1076 } 1077 nwamd_object_release(ncu_obj); 1078 } 1079 1080 static boolean_t 1081 get_scan_results(void *arg, dladm_wlan_attr_t *attrp) 1082 { 1083 nwamd_wifi_scan_t *s = arg; 1084 const char *linkname = s->nwamd_wifi_scan_link; 1085 char essid_name[DLADM_STRSIZE]; 1086 char bssid_name[DLADM_STRSIZE]; 1087 char strength[DLADM_STRSIZE]; 1088 uint_t i, index = 0; 1089 boolean_t found = B_FALSE; 1090 1091 (void) dladm_wlan_essid2str(&attrp->wa_essid, essid_name); 1092 (void) dladm_wlan_bssid2str(&attrp->wa_bssid, bssid_name); 1093 (void) dladm_wlan_strength2str(&attrp->wa_strength, strength); 1094 1095 index = s->nwamd_wifi_scan_curr_num; 1096 if (index == NWAMD_MAX_NUM_WLANS) { 1097 nlog(LOG_ERR, "get_scan_results: truncating WLAN scan results " 1098 "for link %s: ommiting (%s, %s)", linkname, essid_name, 1099 bssid_name); 1100 return (B_TRUE); 1101 } 1102 1103 (void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_essid, essid_name, 1104 sizeof (s->nwamd_wifi_scan_curr[index].nww_essid)); 1105 (void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_bssid, bssid_name, 1106 sizeof (s->nwamd_wifi_scan_curr[index].nww_bssid)); 1107 (void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_signal_strength, 1108 strength, 1109 sizeof (s->nwamd_wifi_scan_curr[index].nww_signal_strength)); 1110 s->nwamd_wifi_scan_curr[index].nww_security_mode = attrp->wa_secmode; 1111 s->nwamd_wifi_scan_curr[index].nww_speed = attrp->wa_speed; 1112 s->nwamd_wifi_scan_curr[index].nww_channel = attrp->wa_channel; 1113 s->nwamd_wifi_scan_curr[index].nww_bsstype = attrp->wa_bsstype; 1114 1115 /* 1116 * We fill in actual values for selected/connected/key later when we 1117 * reacquire the object lock. 1118 */ 1119 s->nwamd_wifi_scan_curr[index].nww_selected = B_FALSE; 1120 s->nwamd_wifi_scan_curr[index].nww_connected = B_FALSE; 1121 s->nwamd_wifi_scan_curr[index].nww_have_key = B_FALSE; 1122 s->nwamd_wifi_scan_curr[index].nww_keyindex = 1; 1123 s->nwamd_wifi_scan_curr_num++; 1124 1125 /* Check if this AP was in previous scan results */ 1126 for (i = 0; i < s->nwamd_wifi_scan_last_num; i++) { 1127 found = (strcmp(s->nwamd_wifi_scan_last[i].nww_essid, 1128 essid_name) == 0 && 1129 strcmp(s->nwamd_wifi_scan_last[i].nww_bssid, 1130 bssid_name) == 0); 1131 if (found) 1132 break; 1133 } 1134 if (!found) 1135 s->nwamd_wifi_scan_changed = B_TRUE; 1136 1137 nlog(LOG_DEBUG, "get_scan_results(%s, %d): ESSID %s, BSSID %s", 1138 linkname, index, essid_name, bssid_name); 1139 1140 return (B_TRUE); 1141 } 1142 1143 /* 1144 * Check if we're connected to the expected WLAN, or in the case of autoconf 1145 * record the WLAN we're connected to. 1146 */ 1147 boolean_t 1148 nwamd_wlan_connected(nwamd_object_t ncu_obj) 1149 { 1150 nwamd_ncu_t *ncu = ncu_obj->nwamd_object_data; 1151 nwamd_link_t *link = &ncu->ncu_link; 1152 dladm_wlan_linkattr_t attr; 1153 char essid[DLADM_STRSIZE]; 1154 char bssid[DLADM_STRSIZE]; 1155 boolean_t connected = B_FALSE; 1156 int retries = 0; 1157 1158 /* 1159 * This is awful, but some wireless drivers 1160 * (particularly 'ath') will erroneously report 1161 * "disconnected" if queried right after a scan. If we 1162 * see 'down' reported here, we retry a few times to 1163 * make sure it's really down. 1164 */ 1165 while (retries++ < 4) { 1166 if (dladm_wlan_get_linkattr(dld_handle, link->nwamd_link_id, 1167 &attr) != DLADM_STATUS_OK) { 1168 attr.la_status = DLADM_WLAN_LINK_DISCONNECTED; 1169 } else if (attr.la_status == DLADM_WLAN_LINK_CONNECTED) { 1170 break; 1171 } 1172 } 1173 1174 if (attr.la_status == DLADM_WLAN_LINK_CONNECTED) { 1175 (void) dladm_wlan_essid2str(&attr.la_wlan_attr.wa_essid, essid); 1176 (void) dladm_wlan_bssid2str(&attr.la_wlan_attr.wa_bssid, bssid); 1177 connected = B_TRUE; 1178 nlog(LOG_DEBUG, "nwamd_wlan_connected: %s connected to %s %s", 1179 ncu->ncu_name, essid, bssid); 1180 } else { 1181 return (B_FALSE); 1182 } 1183 /* 1184 * If we're using autoconf, we have no control over what we connect to, 1185 * so rather than verifying ESSSID, simply record ESSID/BSSID. 1186 */ 1187 if (link->nwamd_link_wifi_autoconf) { 1188 (void) strlcpy(link->nwamd_link_wifi_essid, essid, 1189 sizeof (link->nwamd_link_wifi_essid)); 1190 (void) strlcpy(link->nwamd_link_wifi_bssid, bssid, 1191 sizeof (link->nwamd_link_wifi_bssid)); 1192 } 1193 /* 1194 * Are we connected to expected WLAN? Note: 1195 * we'd like to verify BSSID, but we cannot due to CR 6772510. 1196 */ 1197 if (strcmp(essid, link->nwamd_link_wifi_essid) == 0) { 1198 /* Update connected signal strength */ 1199 (void) dladm_wlan_strength2str(&attr.la_wlan_attr.wa_strength, 1200 link->nwamd_link_wifi_signal_strength); 1201 1202 /* Store current BSSID */ 1203 (void) strlcpy(link->nwamd_link_wifi_bssid, bssid, 1204 sizeof (link->nwamd_link_wifi_bssid)); 1205 1206 if (attr.la_wlan_attr.wa_strength < wireless_scan_level) { 1207 /* 1208 * We're connected, but we've dropped below 1209 * scan threshold. Initiate a scan. 1210 */ 1211 nlog(LOG_DEBUG, "nwamd_wlan_connected: " 1212 "connected but signal under threshold..."); 1213 (void) nwamd_wlan_scan(ncu->ncu_name); 1214 } 1215 return (connected); 1216 } else if (strlen(essid) == 0) { 1217 /* 1218 * For hidden WLANs, no ESSID is specified, so we cannot verify 1219 * WLAN name. 1220 */ 1221 nlog(LOG_DEBUG, 1222 "nwamd_wlan_connected: connected to hidden WLAN, cannot " 1223 "verify connection details"); 1224 return (connected); 1225 } else { 1226 (void) nlog(LOG_ERR, 1227 "nwamd_wlan_connected: wrong AP on %s; expected %s %s", 1228 ncu->ncu_name, link->nwamd_link_wifi_essid, 1229 link->nwamd_link_wifi_bssid); 1230 (void) dladm_wlan_disconnect(dld_handle, link->nwamd_link_id); 1231 link->nwamd_link_wifi_connected = B_FALSE; 1232 return (B_FALSE); 1233 } 1234 } 1235 1236 /* 1237 * WLAN scan thread. Called with the per-link WiFi mutex held. 1238 */ 1239 static void * 1240 wlan_scan_thread(void *arg) 1241 { 1242 char *linkname = arg; 1243 nwamd_object_t ncu_obj; 1244 nwamd_ncu_t *ncu; 1245 nwamd_link_t *link; 1246 dladm_status_t status; 1247 char essid[DLADM_STRSIZE]; 1248 char bssid[DLADM_STRSIZE]; 1249 uint32_t now, link_id; 1250 nwamd_wifi_scan_t s; 1251 int i; 1252 1253 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 1254 == NULL) { 1255 nlog(LOG_ERR, "wlan_scan_thread: could not find object " 1256 "for link %s", linkname); 1257 free(linkname); 1258 return (NULL); 1259 } 1260 1261 ncu = ncu_obj->nwamd_object_data; 1262 link = &ncu->ncu_link; 1263 1264 /* 1265 * It is possible multiple scan threads have queued up waiting for the 1266 * object lock. We try to prevent excessive scanning by limiting the 1267 * interval between scans to WIRELESS_SCAN_REQUESTED_INTERVAL_MIN sec. 1268 */ 1269 now = NSEC_TO_SEC(gethrtime()); 1270 if ((now - link->nwamd_link_wifi_scan.nwamd_wifi_scan_last_time) < 1271 WIRELESS_SCAN_REQUESTED_INTERVAL_MIN) { 1272 nlog(LOG_DEBUG, "wlan_scan_thread: last scan for %s " 1273 "was < %d sec ago, ignoring scan request", 1274 linkname, WIRELESS_SCAN_REQUESTED_INTERVAL_MIN); 1275 nwamd_object_release(ncu_obj); 1276 free(linkname); 1277 return (NULL); 1278 } 1279 1280 /* 1281 * Prepare scan data - copy link name and copy previous "current" 1282 * scan results from the nwamd_link_t to the last scan results for 1283 * the next scan so that we can compare results to find if things 1284 * have changed since last time. 1285 */ 1286 (void) bzero(&s, sizeof (nwamd_wifi_scan_t)); 1287 (void) strlcpy(s.nwamd_wifi_scan_link, ncu->ncu_name, 1288 sizeof (s.nwamd_wifi_scan_link)); 1289 s.nwamd_wifi_scan_last_num = 1290 link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr_num; 1291 if (s.nwamd_wifi_scan_last_num > 0) { 1292 (void) memcpy(s.nwamd_wifi_scan_last, 1293 link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr, 1294 s.nwamd_wifi_scan_last_num * sizeof (nwam_wlan_t)); 1295 } 1296 link_id = link->nwamd_link_id; 1297 nwamd_object_release(ncu_obj); 1298 1299 nlog(LOG_DEBUG, "wlan_scan_thread: initiating scan on %s", 1300 s.nwamd_wifi_scan_link); 1301 1302 scanconnect_entry(); 1303 status = dladm_wlan_scan(dld_handle, link_id, &s, get_scan_results); 1304 s.nwamd_wifi_scan_last_time = NSEC_TO_SEC(gethrtime()); 1305 if (!s.nwamd_wifi_scan_changed) { 1306 /* Scan may have lost WLANs, if so this qualifies as change */ 1307 s.nwamd_wifi_scan_changed = (s.nwamd_wifi_scan_curr_num != 1308 s.nwamd_wifi_scan_last_num); 1309 } 1310 scanconnect_exit(); 1311 1312 if (status != DLADM_STATUS_OK) { 1313 nlog(LOG_ERR, "wlan_scan_thread: cannot scan link %s", 1314 s.nwamd_wifi_scan_link); 1315 free(linkname); 1316 return (NULL); 1317 } 1318 1319 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 1320 == NULL) { 1321 nlog(LOG_ERR, "wlan_scan_thread: could not find object " 1322 "for link %s after doing scan", linkname); 1323 free(linkname); 1324 return (NULL); 1325 } 1326 ncu = ncu_obj->nwamd_object_data; 1327 link = &ncu->ncu_link; 1328 1329 /* For new scan data, add key info from known WLANs */ 1330 for (i = 0; i < s.nwamd_wifi_scan_curr_num; i++) { 1331 if (NEED_ENC(s.nwamd_wifi_scan_curr[i].nww_security_mode)) { 1332 char keyname[NWAM_MAX_VALUE_LEN]; 1333 dladm_wlan_key_t *key = NULL; 1334 1335 if (known_wlan_get_keyname 1336 (s.nwamd_wifi_scan_curr[i].nww_essid, keyname) 1337 == NWAM_SUCCESS && 1338 (key = nwamd_wlan_get_key_named(keyname, 1339 s.nwamd_wifi_scan_curr[i].nww_security_mode)) 1340 != NULL) { 1341 s.nwamd_wifi_scan_curr[i].nww_have_key = 1342 B_TRUE; 1343 s.nwamd_wifi_scan_curr[i].nww_keyindex = 1344 s.nwamd_wifi_scan_curr[i]. 1345 nww_security_mode == 1346 DLADM_WLAN_SECMODE_WEP ? 1347 key->wk_idx : 1; 1348 free(key); 1349 } 1350 } 1351 } 1352 /* Copy scan data into nwamd_link_t */ 1353 link->nwamd_link_wifi_scan = s; 1354 /* Set selected, connected and send scan event if we've got new data */ 1355 nwamd_set_selected_connected(ncu, 1356 link->nwamd_link_wifi_essid[0] != '\0', 1357 link->nwamd_link_wifi_connected); 1358 1359 /* 1360 * If wireless selection is not possible because of the current 1361 * state or priority-group, then this was just a scan request. 1362 * Nothing else to do. 1363 */ 1364 if (!wireless_selection_possible(ncu_obj)) { 1365 nwamd_object_release(ncu_obj); 1366 free(linkname); 1367 return (NULL); 1368 } 1369 1370 /* 1371 * Check if WLAN is on our known WLAN list. If no 1372 * previously-visited WLANs are found in scan data, set 1373 * new state to NEED_SELECTION (provided we're not currently 1374 * connected, as can be the case during a periodic scan or 1375 * monitor-triggered scan where the signal strength recovers. 1376 */ 1377 if (!nwamd_find_known_wlan(ncu_obj)) { 1378 if (!nwamd_wlan_connected(ncu_obj)) { 1379 if (link->nwamd_link_wifi_connected) { 1380 nlog(LOG_DEBUG, "wlan_scan_thread: " 1381 "unexpected disconnect after scan"); 1382 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1383 ncu_obj->nwamd_object_name, 1384 NWAM_STATE_ONLINE_TO_OFFLINE, 1385 NWAM_AUX_STATE_DOWN); 1386 } else { 1387 nlog(LOG_DEBUG, "wlan_scan_thread: " 1388 "no known WLANs - ask user"); 1389 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1390 ncu_obj->nwamd_object_name, 1391 NWAM_STATE_OFFLINE_TO_ONLINE, 1392 NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION); 1393 } 1394 } else { 1395 /* still connected. if not online, change to online */ 1396 nlog(LOG_DEBUG, "wlan_scan_thread: still connected to " 1397 "%s %s", link->nwamd_link_wifi_essid, 1398 link->nwamd_link_wifi_bssid); 1399 if (ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) { 1400 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1401 ncu_obj->nwamd_object_name, 1402 NWAM_STATE_OFFLINE_TO_ONLINE, 1403 NWAM_AUX_STATE_UP); 1404 } 1405 } 1406 nwamd_object_release(ncu_obj); 1407 1408 } else { 1409 nlog(LOG_DEBUG, "wlan_scan_thread: found known WLAN %s %s", 1410 link->nwamd_link_wifi_essid, link->nwamd_link_wifi_bssid); 1411 1412 if (!nwamd_wlan_connected(ncu_obj)) { 1413 /* Copy selected ESSID/BSSID, unlock, call select */ 1414 (void) strlcpy(essid, link->nwamd_link_wifi_essid, 1415 sizeof (essid)); 1416 (void) strlcpy(bssid, link->nwamd_link_wifi_bssid, 1417 sizeof (bssid)); 1418 nwamd_object_release(ncu_obj); 1419 (void) nwamd_wlan_select(linkname, essid, bssid, 1420 link->nwamd_link_wifi_security_mode, B_TRUE); 1421 } else { 1422 /* still connected. if not online, change to online */ 1423 nlog(LOG_DEBUG, "wlan_scan_thread: still connected to " 1424 "known WLAN %s %s", link->nwamd_link_wifi_essid, 1425 link->nwamd_link_wifi_bssid); 1426 if (ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) { 1427 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1428 ncu_obj->nwamd_object_name, 1429 NWAM_STATE_OFFLINE_TO_ONLINE, 1430 NWAM_AUX_STATE_UP); 1431 } 1432 nwamd_object_release(ncu_obj); 1433 } 1434 } 1435 free(linkname); 1436 return (NULL); 1437 } 1438 1439 nwam_error_t 1440 nwamd_wlan_scan(const char *linkname) 1441 { 1442 pthread_t wifi_thread; 1443 char *link = strdup(linkname); 1444 1445 if (link == NULL) { 1446 nlog(LOG_ERR, "nwamd_wlan_scan: out of memory"); 1447 return (NWAM_NO_MEMORY); 1448 } 1449 1450 nlog(LOG_DEBUG, "nwamd_wlan_scan: WLAN scan for %s", 1451 link); 1452 1453 if (pthread_create(&wifi_thread, NULL, wlan_scan_thread, 1454 link) != 0) { 1455 nlog(LOG_ERR, "nwamd_wlan_scan: could not start scan"); 1456 free(link); 1457 return (NWAM_ERROR_INTERNAL); 1458 } 1459 /* detach thread so that it doesn't become a zombie */ 1460 (void) pthread_detach(wifi_thread); 1461 return (NWAM_SUCCESS); 1462 } 1463 1464 /* 1465 * WLAN connection code. 1466 */ 1467 1468 static dladm_status_t 1469 do_connect(uint32_t link_id, dladm_wlan_attr_t *attrp, dladm_wlan_key_t *key, 1470 uint_t keycount, uint_t flags) 1471 { 1472 dladm_status_t status; 1473 char errmsg[DLADM_STRSIZE]; 1474 1475 scanconnect_entry(); 1476 status = dladm_wlan_connect(dld_handle, link_id, attrp, 1477 DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT, key, keycount, flags); 1478 scanconnect_exit(); 1479 1480 nlog(LOG_DEBUG, "nwamd_do_connect: dladm_wlan_connect returned %s", 1481 dladm_status2str(status, errmsg)); 1482 1483 return (status); 1484 } 1485 1486 static void * 1487 wlan_connect_thread(void *arg) 1488 { 1489 char *linkname = arg; 1490 nwamd_object_t ncu_obj; 1491 nwamd_ncu_t *ncu; 1492 nwamd_link_t *link; 1493 nwam_error_t err; 1494 uint_t keycount; 1495 uint32_t link_id; 1496 dladm_wlan_key_t *key = NULL; 1497 dladm_wlan_attr_t attr; 1498 dladm_status_t status; 1499 boolean_t autoconf = B_FALSE; 1500 1501 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 1502 == NULL) { 1503 nlog(LOG_ERR, "wlan_connect_thread: could not find object " 1504 "for link %s", linkname); 1505 free(linkname); 1506 return (NULL); 1507 } 1508 1509 ncu = ncu_obj->nwamd_object_data; 1510 link = &ncu->ncu_link; 1511 1512 if (!wireless_selection_possible(ncu_obj)) { 1513 nlog(LOG_DEBUG, "wlan_connect_thread: %s in invalid state or " 1514 "has lower priority", ncu->ncu_name); 1515 goto done; 1516 } 1517 1518 /* If it is already connected to the required AP, just return. */ 1519 if (nwamd_wlan_connected(ncu_obj)) { 1520 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1521 ncu_obj->nwamd_object_name, 1522 ncu_obj->nwamd_object_state, NWAM_AUX_STATE_UP); 1523 goto done; 1524 } 1525 1526 (void) memset(&attr, 0, sizeof (attr)); 1527 if (dladm_wlan_str2essid(link->nwamd_link_wifi_essid, &attr.wa_essid) 1528 != DLADM_STATUS_OK) { 1529 nlog(LOG_ERR, "wlan_connect_thread: invalid ESSID '%s' " 1530 "for '%s'", link->nwamd_link_wifi_essid, ncu->ncu_name); 1531 goto done; 1532 } 1533 attr.wa_valid = DLADM_WLAN_ATTR_ESSID; 1534 1535 /* note: bssid logic here is non-functional */ 1536 if (link->nwamd_link_wifi_bssid[0] != '\0') { 1537 if (dladm_wlan_str2bssid(link->nwamd_link_wifi_bssid, 1538 &attr.wa_bssid) != DLADM_STATUS_OK) { 1539 nlog(LOG_ERR, "wlan_connect_thread: invalid BSSID '%s'", 1540 "for '%s'", link->nwamd_link_wifi_bssid, 1541 ncu->ncu_name); 1542 } else { 1543 attr.wa_valid |= DLADM_WLAN_ATTR_BSSID; 1544 } 1545 } 1546 1547 /* First check for the key */ 1548 if (NEED_ENC(link->nwamd_link_wifi_security_mode)) { 1549 if (link->nwamd_link_wifi_key == NULL) { 1550 nlog(LOG_ERR, "wlan_connect_thread: could not find " 1551 "key for WLAN '%s'", link->nwamd_link_wifi_essid); 1552 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1553 ncu_obj->nwamd_object_name, 1554 NWAM_STATE_OFFLINE_TO_ONLINE, 1555 NWAM_AUX_STATE_LINK_WIFI_NEED_KEY); 1556 goto done; 1557 } 1558 /* Make a copy of the key as we need to unlock the object */ 1559 if ((key = calloc(1, sizeof (dladm_wlan_key_t))) == NULL) { 1560 nlog(LOG_ERR, "wlan_connect_thread: out of memory"); 1561 goto done; 1562 } 1563 (void) memcpy(key, link->nwamd_link_wifi_key, 1564 sizeof (dladm_wlan_key_t)); 1565 1566 attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE; 1567 attr.wa_secmode = link->nwamd_link_wifi_security_mode; 1568 keycount = 1; 1569 nlog(LOG_DEBUG, "wlan_connect_thread: retrieved key"); 1570 } else { 1571 key = NULL; 1572 keycount = 0; 1573 } 1574 1575 /* 1576 * Connect; only scan if a bssid was not specified. If it times out, 1577 * try a second time using autoconf. Drop the object lock during the 1578 * connect attempt since connecting may take some time, and access to 1579 * the link object during that period would be impossible if we held the 1580 * lock. 1581 */ 1582 1583 link->nwamd_link_wifi_autoconf = B_FALSE; 1584 link_id = link->nwamd_link_id; 1585 1586 nwamd_object_release(ncu_obj); 1587 1588 status = do_connect(link_id, &attr, key, keycount, 1589 DLADM_WLAN_CONNECT_NOSCAN); 1590 if (status != DLADM_STATUS_OK) { 1591 /* Connect failed, try autoconf */ 1592 if (!wireless_autoconf || (status = do_connect(link_id, &attr, 1593 NULL, 0, 0)) != DLADM_STATUS_OK) { 1594 nlog(LOG_ERR, "wlan_connect_thread: connect failed for " 1595 "%s", linkname); 1596 goto done_unlocked; 1597 } 1598 if (status == DLADM_STATUS_OK) 1599 autoconf = B_TRUE; 1600 } 1601 1602 /* Connect succeeded, reacquire object */ 1603 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname)) 1604 == NULL) { 1605 nlog(LOG_ERR, "wlan_connect_thread: could not find object " 1606 "for link %s", linkname); 1607 goto done_unlocked; 1608 } 1609 1610 ncu = ncu_obj->nwamd_object_data; 1611 link = &ncu->ncu_link; 1612 1613 if (autoconf) 1614 link->nwamd_link_wifi_autoconf = B_TRUE; 1615 1616 /* 1617 * If WLAN is WEP/WPA, we would like to test the connection as the key 1618 * may be wrong. It is difficult to find a reliable test that works 1619 * across APs however. Do nothing for now. 1620 */ 1621 link->nwamd_link_wifi_connected = nwamd_wlan_connected(ncu_obj); 1622 1623 if (link->nwamd_link_wifi_connected) { 1624 if (link->nwamd_link_wifi_add_to_known_wlans) { 1625 /* add to known WLANs */ 1626 nlog(LOG_DEBUG, "wlan_connect_thread: " 1627 "add '%s' to known WLANs", 1628 link->nwamd_link_wifi_essid); 1629 if ((err = nwam_known_wlan_add_to_known_wlans 1630 (link->nwamd_link_wifi_essid, 1631 link->nwamd_link_wifi_bssid[0] != '\0' ? 1632 link->nwamd_link_wifi_bssid : NULL, 1633 link->nwamd_link_wifi_security_mode, 1634 link->nwamd_link_wifi_security_mode == 1635 DLADM_WLAN_SECMODE_WEP ? 1636 (uint_t)link->nwamd_link_wifi_key->wk_idx : 1, 1637 NEED_ENC(link->nwamd_link_wifi_security_mode) ? 1638 link->nwamd_link_wifi_keyname : NULL)) 1639 != NWAM_SUCCESS) { 1640 nlog(LOG_ERR, "wlan_connect_thread: " 1641 "could not add to known WLANs: %s", 1642 nwam_strerror(err)); 1643 } 1644 } 1645 nwamd_set_selected_connected(ncu, B_TRUE, B_TRUE); 1646 nlog(LOG_DEBUG, "wlan_connect_thread: connect " 1647 "succeeded, setting state online"); 1648 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1649 ncu_obj->nwamd_object_name, NWAM_STATE_ONLINE, 1650 NWAM_AUX_STATE_UP); 1651 } 1652 1653 done: 1654 nwamd_object_release(ncu_obj); 1655 done_unlocked: 1656 free(linkname); 1657 free(key); 1658 1659 return (NULL); 1660 } 1661 1662 void 1663 nwamd_wlan_connect(const char *linkname) 1664 { 1665 pthread_t wifi_thread; 1666 char *link = strdup(linkname); 1667 1668 if (link == NULL) { 1669 nlog(LOG_ERR, "nwamd_wlan_connect: out of memory"); 1670 return; 1671 } 1672 1673 nlog(LOG_DEBUG, "nwamd_wlan_connect: WLAN connect for %s", 1674 link); 1675 1676 if (pthread_create(&wifi_thread, NULL, wlan_connect_thread, link) != 0) 1677 nlog(LOG_ERR, "nwamd_wlan_connect: could not start connect"); 1678 1679 /* detach thread so that it doesn't become a zombie */ 1680 (void) pthread_detach(wifi_thread); 1681 } 1682 1683 /* 1684 * Launch signal strength-monitoring thread which periodically 1685 * checks connection and signal strength. If we become disconnected 1686 * or signal drops below threshold specified by wireless_scan_level, 1687 * initiate a scan. The scan initiation is taken care of by 1688 * the call to nwamd_wlan_connected(). 1689 */ 1690 static void * 1691 wlan_monitor_signal_thread(void *arg) 1692 { 1693 char *linkname = arg; 1694 nwamd_object_t ncu_obj; 1695 nwamd_ncu_t *ncu; 1696 nwamd_link_t *link; 1697 boolean_t first_time = B_TRUE; 1698 1699 for (;;) { 1700 if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, 1701 linkname)) == NULL) { 1702 nlog(LOG_ERR, "wlan_monitor_signal_thread: could " 1703 "not find object for link %s", linkname); 1704 break; 1705 } 1706 ncu = ncu_obj->nwamd_object_data; 1707 link = &ncu->ncu_link; 1708 1709 /* If the NCU is DISABLED/OFFLINE, exit the monitoring thread */ 1710 if (ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE || 1711 ncu_obj->nwamd_object_state == NWAM_STATE_DISABLED) { 1712 nlog(LOG_INFO, "wlan_monitor_signal_thread: " 1713 "%s is %s, stopping thread", linkname, 1714 nwam_state_to_string(ncu_obj->nwamd_object_state)); 1715 link->nwamd_link_wifi_monitor_thread = 0; 1716 nwamd_object_release(ncu_obj); 1717 break; 1718 } 1719 1720 /* 1721 * First time thru loop, we check if there is another 1722 * link monitoring thread in operation - if so exit this 1723 * thread. 1724 */ 1725 if (first_time) { 1726 first_time = B_FALSE; 1727 1728 if (link->nwamd_link_wifi_monitor_thread != 0) { 1729 /* Already have a monitor thread for link? */ 1730 nwamd_object_release(ncu_obj); 1731 break; 1732 } else { 1733 link->nwamd_link_wifi_monitor_thread = 1734 pthread_self(); 1735 } 1736 } 1737 if (!nwamd_wlan_connected(ncu_obj)) { 1738 nlog(LOG_ERR, "wlan_monitor_signal_thread: " 1739 "disconnect occured for WLAN on link %s", linkname); 1740 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1741 ncu_obj->nwamd_object_name, 1742 NWAM_STATE_ONLINE_TO_OFFLINE, 1743 NWAM_AUX_STATE_DOWN); 1744 link->nwamd_link_wifi_monitor_thread = 0; 1745 nwamd_object_release(ncu_obj); 1746 break; 1747 } 1748 nwamd_object_release(ncu_obj); 1749 (void) sleep(WIRELESS_MONITOR_SIGNAL_INTERVAL); 1750 } 1751 free(linkname); 1752 1753 return (NULL); 1754 } 1755 1756 void 1757 nwamd_wlan_monitor_signal(const char *linkname) 1758 { 1759 pthread_t wifi_thread; 1760 char *link = strdup(linkname); 1761 1762 if (link == NULL) { 1763 nlog(LOG_ERR, "nwamd_wlan_monitor_signal: out of memory"); 1764 return; 1765 } 1766 1767 nlog(LOG_DEBUG, "nwamd_wlan_monitor_signal: WLAN monitor for %s", 1768 link); 1769 1770 if (pthread_create(&wifi_thread, NULL, wlan_monitor_signal_thread, 1771 link) != 0) { 1772 nlog(LOG_ERR, "nwamd_wlan_monitor_signal: could not monitor " 1773 "link %s", link); 1774 free(link); 1775 return; 1776 } 1777 1778 /* detach thread so that it doesn't become a zombie */ 1779 (void) pthread_detach(wifi_thread); 1780 } 1781 1782 void 1783 nwamd_ncu_handle_link_state_event(nwamd_event_t event) 1784 { 1785 nwam_event_t evm; 1786 nwamd_object_t ncu_obj; 1787 nwamd_ncu_t *ncu; 1788 nwamd_link_t *link; 1789 1790 ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, event->event_object); 1791 if (ncu_obj == NULL) { 1792 nlog(LOG_ERR, "nwamd_ncu_handle_link_state_event: no object %s", 1793 event->event_object); 1794 nwamd_event_do_not_send(event); 1795 return; 1796 } 1797 ncu = ncu_obj->nwamd_object_data; 1798 link = &ncu->ncu_link; 1799 evm = event->event_msg; 1800 1801 /* 1802 * We ignore link state events for WiFi because it is very flaky. 1803 * Instead we use the monitor thread and drive WiFi state changes from 1804 * there. 1805 */ 1806 if (link->nwamd_link_media == DL_WIFI) { 1807 nwamd_object_release(ncu_obj); 1808 return; 1809 } 1810 1811 /* 1812 * If it's a link up event and we're not disabled, go online. 1813 */ 1814 if (evm->nwe_data.nwe_link_state.nwe_link_up && 1815 ncu_obj->nwamd_object_state != NWAM_STATE_DISABLED) { 1816 1817 if (link->nwamd_link_activation_mode == 1818 NWAM_ACTIVATION_MODE_PRIORITIZED) { 1819 int64_t priority_group; 1820 1821 (void) pthread_mutex_lock(&active_ncp_mutex); 1822 priority_group = current_ncu_priority_group; 1823 (void) pthread_mutex_unlock(&active_ncp_mutex); 1824 1825 /* compare priority groups */ 1826 if (link->nwamd_link_priority_group > priority_group) { 1827 nlog(LOG_DEBUG, 1828 "nwamd_ncu_handle_link_state_event: " 1829 "got LINK UP event for priority group " 1830 "%lld, less preferred than current %lld, " 1831 "ignoring", 1832 link->nwamd_link_priority_group, 1833 priority_group); 1834 1835 } else if (link->nwamd_link_priority_group == 1836 priority_group) { 1837 nlog(LOG_DEBUG, 1838 "nwamd_ncu_handle_link_state_event: " 1839 "got LINK UP event for priority group " 1840 "%lld, same as current %lld", 1841 link->nwamd_link_priority_group, 1842 priority_group); 1843 /* 1844 * Change link state to UP. It will be 1845 * propagated to IP state machine. Only do 1846 * the NCU check if and when the interface 1847 * NCU is online. 1848 */ 1849 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1850 event->event_object, 1851 NWAM_STATE_OFFLINE_TO_ONLINE, 1852 NWAM_AUX_STATE_UP); 1853 } else { 1854 nlog(LOG_DEBUG, 1855 "nwamd_ncu_handle_link_state_event: " 1856 "got LINK UP event for priority group " 1857 "%lld, more preferred than current %lld", 1858 link->nwamd_link_priority_group, 1859 priority_group); 1860 1861 /* 1862 * We need to mark the link as up so that when 1863 * it is activated we will bring the interface 1864 * up. 1865 */ 1866 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1867 event->event_object, 1868 NWAM_STATE_OFFLINE_TO_ONLINE, 1869 NWAM_AUX_STATE_UP); 1870 nwamd_object_release(ncu_obj); 1871 nwamd_ncp_deactivate_priority_group 1872 (priority_group); 1873 nwamd_ncp_activate_priority_group 1874 (link->nwamd_link_priority_group); 1875 return; 1876 } 1877 1878 } else if (link->nwamd_link_activation_mode == 1879 NWAM_ACTIVATION_MODE_MANUAL) { 1880 nlog(LOG_DEBUG, "nwamd_ncu_handle_link_state_event: " 1881 "got LINK UP event for manual NCU %s", 1882 ncu_obj->nwamd_object_name); 1883 1884 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1885 event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE, 1886 NWAM_AUX_STATE_UP); 1887 } 1888 } 1889 1890 /* 1891 * If the link is down then start or continue transition down. 1892 */ 1893 if (!evm->nwe_data.nwe_link_state.nwe_link_up && 1894 (ncu_obj->nwamd_object_state == NWAM_STATE_ONLINE || 1895 ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE)) { 1896 1897 if (link->nwamd_link_activation_mode == 1898 NWAM_ACTIVATION_MODE_PRIORITIZED) { 1899 nlog(LOG_DEBUG, 1900 "nwamd_ncu_handle_link_state_event: " 1901 "got LINK DOWN for priority group %lld", 1902 link->nwamd_link_priority_group); 1903 /* Moving to offline checks priority group */ 1904 } else { 1905 nlog(LOG_DEBUG, "nwamd_ncu_handle_link_state_event: " 1906 "got LINK DOWN event for manual NCU %s", 1907 ncu_obj->nwamd_object_name); 1908 } 1909 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1910 event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE, 1911 NWAM_AUX_STATE_DOWN); 1912 } 1913 1914 nwamd_object_release(ncu_obj); 1915 } 1916