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