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