ieee80211_proto.c revision 138568
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3138568Ssam * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting 4116742Ssam * All rights reserved. 5116742Ssam * 6116742Ssam * Redistribution and use in source and binary forms, with or without 7116742Ssam * modification, are permitted provided that the following conditions 8116742Ssam * are met: 9116742Ssam * 1. Redistributions of source code must retain the above copyright 10116904Ssam * notice, this list of conditions and the following disclaimer. 11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright 12116904Ssam * notice, this list of conditions and the following disclaimer in the 13116904Ssam * documentation and/or other materials provided with the distribution. 14116904Ssam * 3. The name of the author may not be used to endorse or promote products 15116904Ssam * derived from this software without specific prior written permission. 16116742Ssam * 17116742Ssam * Alternatively, this software may be distributed under the terms of the 18116742Ssam * GNU General Public License ("GPL") version 2 as published by the Free 19116742Ssam * Software Foundation. 20116742Ssam * 21116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31116742Ssam */ 32116742Ssam 33116742Ssam#include <sys/cdefs.h> 34116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_proto.c 138568 2004-12-08 17:26:47Z sam $"); 35116742Ssam 36116742Ssam/* 37116742Ssam * IEEE 802.11 protocol support. 38116742Ssam */ 39116742Ssam 40116742Ssam#include "opt_inet.h" 41116742Ssam 42116742Ssam#include <sys/param.h> 43138568Ssam#include <sys/kernel.h> 44116742Ssam#include <sys/systm.h> 45138568Ssam 46116742Ssam#include <sys/socket.h> 47116742Ssam 48116742Ssam#include <net/if.h> 49116742Ssam#include <net/if_media.h> 50138568Ssam#include <net/ethernet.h> /* XXX for ether_sprintf */ 51116742Ssam 52116742Ssam#include <net80211/ieee80211_var.h> 53116742Ssam 54138568Ssam/* XXX tunables */ 55138568Ssam#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ 56138568Ssam#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ 57116742Ssam 58116742Ssam#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) 59116742Ssam 60116742Ssamconst char *ieee80211_mgt_subtype_name[] = { 61116742Ssam "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", 62116742Ssam "probe_req", "probe_resp", "reserved#6", "reserved#7", 63116742Ssam "beacon", "atim", "disassoc", "auth", 64116742Ssam "deauth", "reserved#13", "reserved#14", "reserved#15" 65116742Ssam}; 66138568Ssamconst char *ieee80211_ctl_subtype_name[] = { 67138568Ssam "reserved#0", "reserved#1", "reserved#2", "reserved#3", 68138568Ssam "reserved#3", "reserved#5", "reserved#6", "reserved#7", 69138568Ssam "reserved#8", "reserved#9", "ps_poll", "rts", 70138568Ssam "cts", "ack", "cf_end", "cf_end_ack" 71138568Ssam}; 72117811Ssamconst char *ieee80211_state_name[IEEE80211_S_MAX] = { 73117811Ssam "INIT", /* IEEE80211_S_INIT */ 74117811Ssam "SCAN", /* IEEE80211_S_SCAN */ 75117811Ssam "AUTH", /* IEEE80211_S_AUTH */ 76117811Ssam "ASSOC", /* IEEE80211_S_ASSOC */ 77117811Ssam "RUN" /* IEEE80211_S_RUN */ 78117811Ssam}; 79138568Ssamconst char *ieee80211_wme_acnames[] = { 80138568Ssam "WME_AC_BE", 81138568Ssam "WME_AC_BK", 82138568Ssam "WME_AC_VI", 83138568Ssam "WME_AC_VO", 84138568Ssam "WME_UPSD", 85138568Ssam}; 86116742Ssam 87117811Ssamstatic int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); 88117811Ssam 89116742Ssamvoid 90138568Ssamieee80211_proto_attach(struct ieee80211com *ic) 91116742Ssam{ 92138568Ssam struct ifnet *ifp = ic->ic_ifp; 93116742Ssam 94138568Ssam /* XXX room for crypto */ 95138568Ssam ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); 96116742Ssam 97116742Ssam#ifdef notdef 98116742Ssam ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; 99116742Ssam#else 100116742Ssam ic->ic_rtsthreshold = IEEE80211_RTS_MAX; 101116742Ssam#endif 102116742Ssam ic->ic_fragthreshold = 2346; /* XXX not used yet */ 103116742Ssam ic->ic_fixed_rate = -1; /* no fixed rate */ 104127648Ssam ic->ic_protmode = IEEE80211_PROT_CTSONLY; 105138568Ssam ic->ic_roaming = IEEE80211_ROAMING_AUTO; 106116742Ssam 107138568Ssam ic->ic_wme.wme_hipri_switch_hysteresis = 108138568Ssam AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 109138568Ssam 110121816Sbrooks mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); 111116742Ssam 112117811Ssam /* protocol state change handler */ 113117811Ssam ic->ic_newstate = ieee80211_newstate; 114117811Ssam 115116742Ssam /* initialize management frame handlers */ 116116742Ssam ic->ic_recv_mgmt = ieee80211_recv_mgmt; 117116742Ssam ic->ic_send_mgmt = ieee80211_send_mgmt; 118116742Ssam} 119116742Ssam 120116742Ssamvoid 121138568Ssamieee80211_proto_detach(struct ieee80211com *ic) 122116742Ssam{ 123116742Ssam 124138568Ssam /* 125138568Ssam * This should not be needed as we detach when reseting 126138568Ssam * the state but be conservative here since the 127138568Ssam * authenticator may do things like spawn kernel threads. 128138568Ssam */ 129138568Ssam if (ic->ic_auth->ia_detach) 130138568Ssam ic->ic_auth->ia_detach(ic); 131138568Ssam 132116742Ssam IF_DRAIN(&ic->ic_mgtq); 133116742Ssam mtx_destroy(&ic->ic_mgtq.ifq_mtx); 134138568Ssam 135138568Ssam /* 136138568Ssam * Detach any ACL'ator. 137138568Ssam */ 138138568Ssam if (ic->ic_acl != NULL) 139138568Ssam ic->ic_acl->iac_detach(ic); 140116742Ssam} 141116742Ssam 142138568Ssam/* 143138568Ssam * Simple-minded authenticator module support. 144138568Ssam */ 145138568Ssam 146138568Ssam#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 147138568Ssam/* XXX well-known names */ 148138568Ssamstatic const char *auth_modnames[IEEE80211_AUTH_MAX] = { 149138568Ssam "wlan_internal", /* IEEE80211_AUTH_NONE */ 150138568Ssam "wlan_internal", /* IEEE80211_AUTH_OPEN */ 151138568Ssam "wlan_internal", /* IEEE80211_AUTH_SHARED */ 152138568Ssam "wlan_xauth", /* IEEE80211_AUTH_8021X */ 153138568Ssam "wlan_internal", /* IEEE80211_AUTH_AUTO */ 154138568Ssam "wlan_xauth", /* IEEE80211_AUTH_WPA */ 155138568Ssam}; 156138568Ssamstatic const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 157138568Ssam 158138568Ssamstatic const struct ieee80211_authenticator auth_internal = { 159138568Ssam .ia_name = "wlan_internal", 160138568Ssam .ia_attach = NULL, 161138568Ssam .ia_detach = NULL, 162138568Ssam .ia_node_join = NULL, 163138568Ssam .ia_node_leave = NULL, 164138568Ssam}; 165138568Ssam 166138568Ssam/* 167138568Ssam * Setup internal authenticators once; they are never unregistered. 168138568Ssam */ 169138568Ssamstatic void 170138568Ssamieee80211_auth_setup(void) 171138568Ssam{ 172138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 173138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 174138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 175138568Ssam} 176138568SsamSYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 177138568Ssam 178138568Ssamconst struct ieee80211_authenticator * 179138568Ssamieee80211_authenticator_get(int auth) 180138568Ssam{ 181138568Ssam if (auth >= IEEE80211_AUTH_MAX) 182138568Ssam return NULL; 183138568Ssam if (authenticators[auth] == NULL) 184138568Ssam ieee80211_load_module(auth_modnames[auth]); 185138568Ssam return authenticators[auth]; 186138568Ssam} 187138568Ssam 188116742Ssamvoid 189138568Ssamieee80211_authenticator_register(int type, 190138568Ssam const struct ieee80211_authenticator *auth) 191116742Ssam{ 192138568Ssam if (type >= IEEE80211_AUTH_MAX) 193138568Ssam return; 194138568Ssam authenticators[type] = auth; 195138568Ssam} 196138568Ssam 197138568Ssamvoid 198138568Ssamieee80211_authenticator_unregister(int type) 199138568Ssam{ 200138568Ssam 201138568Ssam if (type >= IEEE80211_AUTH_MAX) 202138568Ssam return; 203138568Ssam authenticators[type] = NULL; 204138568Ssam} 205138568Ssam 206138568Ssam/* 207138568Ssam * Very simple-minded ACL module support. 208138568Ssam */ 209138568Ssam/* XXX just one for now */ 210138568Ssamstatic const struct ieee80211_aclator *acl = NULL; 211138568Ssam 212138568Ssamvoid 213138568Ssamieee80211_aclator_register(const struct ieee80211_aclator *iac) 214138568Ssam{ 215138568Ssam printf("wlan: %s acl policy registered\n", iac->iac_name); 216138568Ssam acl = iac; 217138568Ssam} 218138568Ssam 219138568Ssamvoid 220138568Ssamieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 221138568Ssam{ 222138568Ssam if (acl == iac) 223138568Ssam acl = NULL; 224138568Ssam printf("wlan: %s acl policy unregistered\n", iac->iac_name); 225138568Ssam} 226138568Ssam 227138568Ssamconst struct ieee80211_aclator * 228138568Ssamieee80211_aclator_get(const char *name) 229138568Ssam{ 230138568Ssam if (acl == NULL) 231138568Ssam ieee80211_load_module("wlan_acl"); 232138568Ssam return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 233138568Ssam} 234138568Ssam 235138568Ssamvoid 236138568Ssamieee80211_print_essid(const u_int8_t *essid, int len) 237138568Ssam{ 238138568Ssam const u_int8_t *p; 239116742Ssam int i; 240116742Ssam 241116742Ssam if (len > IEEE80211_NWID_LEN) 242116742Ssam len = IEEE80211_NWID_LEN; 243116742Ssam /* determine printable or not */ 244116742Ssam for (i = 0, p = essid; i < len; i++, p++) { 245116742Ssam if (*p < ' ' || *p > 0x7e) 246116742Ssam break; 247116742Ssam } 248116742Ssam if (i == len) { 249116742Ssam printf("\""); 250116742Ssam for (i = 0, p = essid; i < len; i++, p++) 251116742Ssam printf("%c", *p); 252116742Ssam printf("\""); 253116742Ssam } else { 254116742Ssam printf("0x"); 255116742Ssam for (i = 0, p = essid; i < len; i++, p++) 256116742Ssam printf("%02x", *p); 257116742Ssam } 258116742Ssam} 259116742Ssam 260116742Ssamvoid 261138568Ssamieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) 262116742Ssam{ 263138568Ssam const struct ieee80211_frame *wh; 264116742Ssam int i; 265116742Ssam 266138568Ssam wh = (const struct ieee80211_frame *)buf; 267116742Ssam switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 268116742Ssam case IEEE80211_FC1_DIR_NODS: 269116742Ssam printf("NODS %s", ether_sprintf(wh->i_addr2)); 270116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 271116742Ssam printf("(%s)", ether_sprintf(wh->i_addr3)); 272116742Ssam break; 273116742Ssam case IEEE80211_FC1_DIR_TODS: 274116742Ssam printf("TODS %s", ether_sprintf(wh->i_addr2)); 275116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 276116742Ssam printf("(%s)", ether_sprintf(wh->i_addr1)); 277116742Ssam break; 278116742Ssam case IEEE80211_FC1_DIR_FROMDS: 279116742Ssam printf("FRDS %s", ether_sprintf(wh->i_addr3)); 280116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 281116742Ssam printf("(%s)", ether_sprintf(wh->i_addr2)); 282116742Ssam break; 283116742Ssam case IEEE80211_FC1_DIR_DSTODS: 284138568Ssam printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1])); 285116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 286116742Ssam printf("(%s", ether_sprintf(wh->i_addr2)); 287116742Ssam printf("->%s)", ether_sprintf(wh->i_addr1)); 288116742Ssam break; 289116742Ssam } 290116742Ssam switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 291116742Ssam case IEEE80211_FC0_TYPE_DATA: 292116742Ssam printf(" data"); 293116742Ssam break; 294116742Ssam case IEEE80211_FC0_TYPE_MGT: 295116742Ssam printf(" %s", ieee80211_mgt_subtype_name[ 296116742Ssam (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 297116742Ssam >> IEEE80211_FC0_SUBTYPE_SHIFT]); 298116742Ssam break; 299116742Ssam default: 300116742Ssam printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 301116742Ssam break; 302116742Ssam } 303138568Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 304138568Ssam int i; 305138568Ssam printf(" WEP [IV"); 306138568Ssam for (i = 0; i < IEEE80211_WEP_IVLEN; i++) 307138568Ssam printf(" %.02x", buf[sizeof(*wh)+i]); 308138568Ssam printf(" KID %u]", buf[sizeof(*wh)+i] >> 6); 309138568Ssam } 310116742Ssam if (rate >= 0) 311116742Ssam printf(" %dM", rate / 2); 312116742Ssam if (rssi >= 0) 313116742Ssam printf(" +%d", rssi); 314116742Ssam printf("\n"); 315116742Ssam if (len > 0) { 316116742Ssam for (i = 0; i < len; i++) { 317116742Ssam if ((i & 1) == 0) 318116742Ssam printf(" "); 319116742Ssam printf("%02x", buf[i]); 320116742Ssam } 321116742Ssam printf("\n"); 322116742Ssam } 323116742Ssam} 324116742Ssam 325116742Ssamint 326116742Ssamieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags) 327116742Ssam{ 328116742Ssam#define RV(v) ((v) & IEEE80211_RATE_VAL) 329116742Ssam int i, j, ignore, error; 330138568Ssam int okrate, badrate, fixedrate; 331116742Ssam struct ieee80211_rateset *srs, *nrs; 332116742Ssam u_int8_t r; 333116742Ssam 334138568Ssam /* 335138568Ssam * If the fixed rate check was requested but no 336138568Ssam * fixed has been defined then just remove it. 337138568Ssam */ 338138568Ssam if ((flags & IEEE80211_F_DOFRATE) && ic->ic_fixed_rate < 0) 339138568Ssam flags &= ~IEEE80211_F_DOFRATE; 340116742Ssam error = 0; 341138568Ssam okrate = badrate = fixedrate = 0; 342116742Ssam srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; 343116742Ssam nrs = &ni->ni_rates; 344120482Ssam for (i = 0; i < nrs->rs_nrates; ) { 345116742Ssam ignore = 0; 346116742Ssam if (flags & IEEE80211_F_DOSORT) { 347116742Ssam /* 348116742Ssam * Sort rates. 349116742Ssam */ 350116742Ssam for (j = i + 1; j < nrs->rs_nrates; j++) { 351116742Ssam if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 352116742Ssam r = nrs->rs_rates[i]; 353116742Ssam nrs->rs_rates[i] = nrs->rs_rates[j]; 354116742Ssam nrs->rs_rates[j] = r; 355116742Ssam } 356116742Ssam } 357116742Ssam } 358116742Ssam r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 359116742Ssam badrate = r; 360116742Ssam if (flags & IEEE80211_F_DOFRATE) { 361116742Ssam /* 362138568Ssam * Check any fixed rate is included. 363116742Ssam */ 364138568Ssam if (r == RV(srs->rs_rates[ic->ic_fixed_rate])) 365138568Ssam fixedrate = r; 366116742Ssam } 367116742Ssam if (flags & IEEE80211_F_DONEGO) { 368116742Ssam /* 369116742Ssam * Check against supported rates. 370116742Ssam */ 371116742Ssam for (j = 0; j < srs->rs_nrates; j++) { 372127761Ssam if (r == RV(srs->rs_rates[j])) { 373127761Ssam /* 374127761Ssam * Overwrite with the supported rate 375127761Ssam * value so any basic rate bit is set. 376127761Ssam * This insures that response we send 377127761Ssam * to stations have the necessary basic 378127761Ssam * rate bit set. 379127761Ssam */ 380127761Ssam nrs->rs_rates[i] = srs->rs_rates[j]; 381116742Ssam break; 382127761Ssam } 383116742Ssam } 384116742Ssam if (j == srs->rs_nrates) { 385120482Ssam /* 386120482Ssam * A rate in the node's rate set is not 387120482Ssam * supported. If this is a basic rate and we 388120482Ssam * are operating as an AP then this is an error. 389120482Ssam * Otherwise we just discard/ignore the rate. 390120482Ssam * Note that this is important for 11b stations 391120482Ssam * when they want to associate with an 11g AP. 392120482Ssam */ 393120482Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && 394120482Ssam (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 395116742Ssam error++; 396116742Ssam ignore++; 397116742Ssam } 398116742Ssam } 399116742Ssam if (flags & IEEE80211_F_DODEL) { 400116742Ssam /* 401116742Ssam * Delete unacceptable rates. 402116742Ssam */ 403116742Ssam if (ignore) { 404116742Ssam nrs->rs_nrates--; 405116742Ssam for (j = i; j < nrs->rs_nrates; j++) 406116742Ssam nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 407116742Ssam nrs->rs_rates[j] = 0; 408116742Ssam continue; 409116742Ssam } 410116742Ssam } 411116742Ssam if (!ignore) 412116742Ssam okrate = nrs->rs_rates[i]; 413116742Ssam i++; 414116742Ssam } 415138568Ssam if (okrate == 0 || error != 0 || 416138568Ssam ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0)) 417116742Ssam return badrate | IEEE80211_RATE_BASIC; 418116742Ssam else 419116742Ssam return RV(okrate); 420116742Ssam#undef RV 421116742Ssam} 422116742Ssam 423138568Ssam/* 424138568Ssam * Reset 11g-related state. 425138568Ssam */ 426138568Ssamvoid 427138568Ssamieee80211_reset_erp(struct ieee80211com *ic) 428138568Ssam{ 429138568Ssam ic->ic_flags &= ~IEEE80211_F_USEPROT; 430138568Ssam ic->ic_nonerpsta = 0; 431138568Ssam ic->ic_longslotsta = 0; 432138568Ssam /* 433138568Ssam * Short slot time is enabled only when operating in 11g 434138568Ssam * and not in an IBSS. We must also honor whether or not 435138568Ssam * the driver is capable of doing it. 436138568Ssam */ 437138568Ssam ieee80211_set_shortslottime(ic, 438138568Ssam ic->ic_curmode == IEEE80211_MODE_11A || 439138568Ssam (ic->ic_curmode == IEEE80211_MODE_11G && 440138568Ssam ic->ic_opmode == IEEE80211_M_HOSTAP && 441138568Ssam (ic->ic_caps & IEEE80211_C_SHSLOT))); 442138568Ssam /* 443138568Ssam * Set short preamble and ERP barker-preamble flags. 444138568Ssam */ 445138568Ssam if (ic->ic_curmode == IEEE80211_MODE_11A || 446138568Ssam (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 447138568Ssam ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 448138568Ssam ic->ic_flags &= ~IEEE80211_F_USEBARKER; 449138568Ssam } else { 450138568Ssam ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 451138568Ssam ic->ic_flags |= IEEE80211_F_USEBARKER; 452138568Ssam } 453138568Ssam} 454138568Ssam 455138568Ssam/* 456138568Ssam * Set the short slot time state and notify the driver. 457138568Ssam */ 458138568Ssamvoid 459138568Ssamieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 460138568Ssam{ 461138568Ssam if (onoff) 462138568Ssam ic->ic_flags |= IEEE80211_F_SHSLOT; 463138568Ssam else 464138568Ssam ic->ic_flags &= ~IEEE80211_F_SHSLOT; 465138568Ssam /* notify driver */ 466138568Ssam if (ic->ic_updateslot != NULL) 467138568Ssam ic->ic_updateslot(ic->ic_ifp); 468138568Ssam} 469138568Ssam 470138568Ssam/* 471138568Ssam * Check if the specified rate set supports ERP. 472138568Ssam * NB: the rate set is assumed to be sorted. 473138568Ssam */ 474138568Ssamint 475138568Ssamieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) 476138568Ssam{ 477138568Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 478138568Ssam static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 479138568Ssam int i, j; 480138568Ssam 481138568Ssam if (rs->rs_nrates < N(rates)) 482138568Ssam return 0; 483138568Ssam for (i = 0; i < N(rates); i++) { 484138568Ssam for (j = 0; j < rs->rs_nrates; j++) { 485138568Ssam int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 486138568Ssam if (rates[i] == r) 487138568Ssam goto next; 488138568Ssam if (r > rates[i]) 489138568Ssam return 0; 490138568Ssam } 491138568Ssam return 0; 492138568Ssam next: 493138568Ssam ; 494138568Ssam } 495138568Ssam return 1; 496138568Ssam#undef N 497138568Ssam} 498138568Ssam 499138568Ssam/* 500138568Ssam * Mark the basic rates for the 11g rate table based on the 501138568Ssam * operating mode. For real 11g we mark all the 11b rates 502138568Ssam * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 503138568Ssam * 11b rates. There's also a pseudo 11a-mode used to mark only 504138568Ssam * the basic OFDM rates. 505138568Ssam */ 506138568Ssamvoid 507138568Ssamieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) 508138568Ssam{ 509138568Ssam static const struct ieee80211_rateset basic[] = { 510138568Ssam { 0 }, /* IEEE80211_MODE_AUTO */ 511138568Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 512138568Ssam { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ 513138568Ssam { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ 514138568Ssam { 0 }, /* IEEE80211_MODE_FH */ 515138568Ssam /* IEEE80211_MODE_PUREG (not yet) */ 516138568Ssam { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 517138568Ssam }; 518138568Ssam int i, j; 519138568Ssam 520138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 521138568Ssam rs->rs_rates[i] &= IEEE80211_RATE_VAL; 522138568Ssam for (j = 0; j < basic[mode].rs_nrates; j++) 523138568Ssam if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 524138568Ssam rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 525138568Ssam break; 526138568Ssam } 527138568Ssam } 528138568Ssam} 529138568Ssam 530138568Ssam/* 531138568Ssam * WME protocol support. The following parameters come from the spec. 532138568Ssam */ 533138568Ssamtypedef struct phyParamType { 534138568Ssam u_int8_t aifsn; 535138568Ssam u_int8_t logcwmin; 536138568Ssam u_int8_t logcwmax; 537138568Ssam u_int16_t txopLimit; 538138568Ssam u_int8_t acm; 539138568Ssam} paramType; 540138568Ssam 541138568Ssamstatic const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 542138568Ssam { 3, 4, 6 }, /* IEEE80211_MODE_AUTO */ 543138568Ssam { 3, 4, 6 }, /* IEEE80211_MODE_11A */ 544138568Ssam { 3, 5, 7 }, /* IEEE80211_MODE_11B */ 545138568Ssam { 3, 4, 6 }, /* IEEE80211_MODE_11G */ 546138568Ssam { 3, 5, 7 }, /* IEEE80211_MODE_FH */ 547138568Ssam { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_A */ 548138568Ssam { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_G */ 549138568Ssam}; 550138568Ssamstatic const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 551138568Ssam { 7, 4, 10 }, /* IEEE80211_MODE_AUTO */ 552138568Ssam { 7, 4, 10 }, /* IEEE80211_MODE_11A */ 553138568Ssam { 7, 5, 10 }, /* IEEE80211_MODE_11B */ 554138568Ssam { 7, 4, 10 }, /* IEEE80211_MODE_11G */ 555138568Ssam { 7, 5, 10 }, /* IEEE80211_MODE_FH */ 556138568Ssam { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ 557138568Ssam { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ 558138568Ssam}; 559138568Ssamstatic const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 560138568Ssam { 1, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ 561138568Ssam { 1, 3, 4, 94 }, /* IEEE80211_MODE_11A */ 562138568Ssam { 1, 4, 5, 188 }, /* IEEE80211_MODE_11B */ 563138568Ssam { 1, 3, 4, 94 }, /* IEEE80211_MODE_11G */ 564138568Ssam { 1, 4, 5, 188 }, /* IEEE80211_MODE_FH */ 565138568Ssam { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ 566138568Ssam { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ 567138568Ssam}; 568138568Ssamstatic const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 569138568Ssam { 1, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ 570138568Ssam { 1, 2, 3, 47 }, /* IEEE80211_MODE_11A */ 571138568Ssam { 1, 3, 4, 102 }, /* IEEE80211_MODE_11B */ 572138568Ssam { 1, 2, 3, 47 }, /* IEEE80211_MODE_11G */ 573138568Ssam { 1, 3, 4, 102 }, /* IEEE80211_MODE_FH */ 574138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ 575138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ 576138568Ssam}; 577138568Ssam 578138568Ssamstatic const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 579138568Ssam { 3, 4, 10 }, /* IEEE80211_MODE_AUTO */ 580138568Ssam { 3, 4, 10 }, /* IEEE80211_MODE_11A */ 581138568Ssam { 3, 5, 10 }, /* IEEE80211_MODE_11B */ 582138568Ssam { 3, 4, 10 }, /* IEEE80211_MODE_11G */ 583138568Ssam { 3, 5, 10 }, /* IEEE80211_MODE_FH */ 584138568Ssam { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ 585138568Ssam { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ 586138568Ssam}; 587138568Ssamstatic const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 588138568Ssam { 2, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ 589138568Ssam { 2, 3, 4, 94 }, /* IEEE80211_MODE_11A */ 590138568Ssam { 2, 4, 5, 188 }, /* IEEE80211_MODE_11B */ 591138568Ssam { 2, 3, 4, 94 }, /* IEEE80211_MODE_11G */ 592138568Ssam { 2, 4, 5, 188 }, /* IEEE80211_MODE_FH */ 593138568Ssam { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ 594138568Ssam { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ 595138568Ssam}; 596138568Ssamstatic const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 597138568Ssam { 2, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ 598138568Ssam { 2, 2, 3, 47 }, /* IEEE80211_MODE_11A */ 599138568Ssam { 2, 3, 4, 102 }, /* IEEE80211_MODE_11B */ 600138568Ssam { 2, 2, 3, 47 }, /* IEEE80211_MODE_11G */ 601138568Ssam { 2, 3, 4, 102 }, /* IEEE80211_MODE_FH */ 602138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ 603138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ 604138568Ssam}; 605138568Ssam 606138568Ssamvoid 607138568Ssamieee80211_wme_initparams(struct ieee80211com *ic) 608138568Ssam{ 609138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 610138568Ssam const paramType *pPhyParam, *pBssPhyParam; 611138568Ssam struct wmeParams *wmep; 612138568Ssam int i; 613138568Ssam 614138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 615138568Ssam return; 616138568Ssam 617138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 618138568Ssam switch (i) { 619138568Ssam case WME_AC_BK: 620138568Ssam pPhyParam = &phyParamForAC_BK[ic->ic_curmode]; 621138568Ssam pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode]; 622138568Ssam break; 623138568Ssam case WME_AC_VI: 624138568Ssam pPhyParam = &phyParamForAC_VI[ic->ic_curmode]; 625138568Ssam pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode]; 626138568Ssam break; 627138568Ssam case WME_AC_VO: 628138568Ssam pPhyParam = &phyParamForAC_VO[ic->ic_curmode]; 629138568Ssam pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode]; 630138568Ssam break; 631138568Ssam case WME_AC_BE: 632138568Ssam default: 633138568Ssam pPhyParam = &phyParamForAC_BE[ic->ic_curmode]; 634138568Ssam pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode]; 635138568Ssam break; 636138568Ssam } 637138568Ssam 638138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 639138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 640138568Ssam wmep->wmep_acm = pPhyParam->acm; 641138568Ssam wmep->wmep_aifsn = pPhyParam->aifsn; 642138568Ssam wmep->wmep_logcwmin = pPhyParam->logcwmin; 643138568Ssam wmep->wmep_logcwmax = pPhyParam->logcwmax; 644138568Ssam wmep->wmep_txopLimit = pPhyParam->txopLimit; 645138568Ssam } else { 646138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 647138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 648138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 649138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 650138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 651138568Ssam 652138568Ssam } 653138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 654138568Ssam "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " 655138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 656138568Ssam , ieee80211_wme_acnames[i] 657138568Ssam , wmep->wmep_acm 658138568Ssam , wmep->wmep_aifsn 659138568Ssam , wmep->wmep_logcwmin 660138568Ssam , wmep->wmep_logcwmax 661138568Ssam , wmep->wmep_txopLimit 662138568Ssam ); 663138568Ssam 664138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 665138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 666138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 667138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 668138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 669138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 670138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 671138568Ssam "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " 672138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 673138568Ssam , ieee80211_wme_acnames[i] 674138568Ssam , wmep->wmep_acm 675138568Ssam , wmep->wmep_aifsn 676138568Ssam , wmep->wmep_logcwmin 677138568Ssam , wmep->wmep_logcwmax 678138568Ssam , wmep->wmep_txopLimit 679138568Ssam ); 680138568Ssam } 681138568Ssam /* NB: check ic_bss to avoid NULL deref on initial attach */ 682138568Ssam if (ic->ic_bss != NULL) { 683138568Ssam /* 684138568Ssam * Calculate agressive mode switching threshold based 685138568Ssam * on beacon interval. This doesn't need locking since 686138568Ssam * we're only called before entering the RUN state at 687138568Ssam * which point we start sending beacon frames. 688138568Ssam */ 689138568Ssam wme->wme_hipri_switch_thresh = 690138568Ssam (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; 691138568Ssam ieee80211_wme_updateparams(ic); 692138568Ssam } 693138568Ssam} 694138568Ssam 695138568Ssam/* 696138568Ssam * Update WME parameters for ourself and the BSS. 697138568Ssam */ 698138568Ssamvoid 699138568Ssamieee80211_wme_updateparams_locked(struct ieee80211com *ic) 700138568Ssam{ 701138568Ssam static const paramType phyParam[IEEE80211_MODE_MAX] = { 702138568Ssam { 2, 4, 10, 64 }, /* IEEE80211_MODE_AUTO */ 703138568Ssam { 2, 4, 10, 64 }, /* IEEE80211_MODE_11A */ 704138568Ssam { 2, 5, 10, 64 }, /* IEEE80211_MODE_11B */ 705138568Ssam { 2, 4, 10, 64 }, /* IEEE80211_MODE_11G */ 706138568Ssam { 2, 5, 10, 64 }, /* IEEE80211_MODE_FH */ 707138568Ssam { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_A */ 708138568Ssam { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_G */ 709138568Ssam }; 710138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 711138568Ssam const struct wmeParams *wmep; 712138568Ssam struct wmeParams *chanp, *bssp; 713138568Ssam int i; 714138568Ssam 715138568Ssam /* set up the channel access parameters for the physical device */ 716138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 717138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[i]; 718138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 719138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 720138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 721138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 722138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 723138568Ssam 724138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 725138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 726138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 727138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 728138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 729138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 730138568Ssam } 731138568Ssam 732138568Ssam /* 733138568Ssam * This implements agressive mode as found in certain 734138568Ssam * vendors' AP's. When there is significant high 735138568Ssam * priority (VI/VO) traffic in the BSS throttle back BE 736138568Ssam * traffic by using conservative parameters. Otherwise 737138568Ssam * BE uses agressive params to optimize performance of 738138568Ssam * legacy/non-QoS traffic. 739138568Ssam */ 740138568Ssam if ((ic->ic_opmode == IEEE80211_M_HOSTAP && 741138568Ssam (wme->wme_flags & WME_F_AGGRMODE) == 0) || 742138568Ssam (ic->ic_opmode != IEEE80211_M_HOSTAP && 743138568Ssam (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 744138568Ssam (ic->ic_flags & IEEE80211_F_WME) == 0) { 745138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 746138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 747138568Ssam 748138568Ssam chanp->wmep_aifsn = bssp->wmep_aifsn = 749138568Ssam phyParam[ic->ic_curmode].aifsn; 750138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 751138568Ssam phyParam[ic->ic_curmode].logcwmin; 752138568Ssam chanp->wmep_logcwmax = bssp->wmep_logcwmax = 753138568Ssam phyParam[ic->ic_curmode].logcwmax; 754138568Ssam chanp->wmep_txopLimit = bssp->wmep_txopLimit = 755138568Ssam (ic->ic_caps & IEEE80211_C_BURST) ? 756138568Ssam phyParam[ic->ic_curmode].txopLimit : 0; 757138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 758138568Ssam "%s: %s [acm %u aifsn %u log2(cwmin) %u " 759138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 760138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 761138568Ssam , chanp->wmep_acm 762138568Ssam , chanp->wmep_aifsn 763138568Ssam , chanp->wmep_logcwmin 764138568Ssam , chanp->wmep_logcwmax 765138568Ssam , chanp->wmep_txopLimit 766138568Ssam ); 767138568Ssam } 768138568Ssam 769138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && 770138568Ssam ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) == 0) { 771138568Ssam static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = { 772138568Ssam 3, /* IEEE80211_MODE_AUTO */ 773138568Ssam 3, /* IEEE80211_MODE_11A */ 774138568Ssam 4, /* IEEE80211_MODE_11B */ 775138568Ssam 3, /* IEEE80211_MODE_11G */ 776138568Ssam 4, /* IEEE80211_MODE_FH */ 777138568Ssam 3, /* IEEE80211_MODE_TURBO_A */ 778138568Ssam 3, /* IEEE80211_MODE_TURBO_G */ 779138568Ssam }; 780138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 781138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 782138568Ssam 783138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 784138568Ssam logCwMin[ic->ic_curmode]; 785138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 786138568Ssam "%s: %s log2(cwmin) %u\n", __func__ 787138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 788138568Ssam , chanp->wmep_logcwmin 789138568Ssam ); 790138568Ssam } 791138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 792138568Ssam /* 793138568Ssam * Arrange for a beacon update and bump the parameter 794138568Ssam * set number so associated stations load the new values. 795138568Ssam */ 796138568Ssam wme->wme_bssChanParams.cap_info = 797138568Ssam (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 798138568Ssam ic->ic_flags |= IEEE80211_F_WMEUPDATE; 799138568Ssam } 800138568Ssam 801138568Ssam wme->wme_update(ic); 802138568Ssam 803138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 804138568Ssam "%s: WME params updated, cap_info 0x%x\n", __func__, 805138568Ssam ic->ic_opmode == IEEE80211_M_STA ? 806138568Ssam wme->wme_wmeChanParams.cap_info : 807138568Ssam wme->wme_bssChanParams.cap_info); 808138568Ssam} 809138568Ssam 810138568Ssamvoid 811138568Ssamieee80211_wme_updateparams(struct ieee80211com *ic) 812138568Ssam{ 813138568Ssam 814138568Ssam if (ic->ic_caps & IEEE80211_C_WME) { 815138568Ssam IEEE80211_BEACON_LOCK(ic); 816138568Ssam ieee80211_wme_updateparams_locked(ic); 817138568Ssam IEEE80211_BEACON_UNLOCK(ic); 818138568Ssam } 819138568Ssam} 820138568Ssam 821117811Ssamstatic int 822138568Ssamieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 823116742Ssam{ 824138568Ssam struct ifnet *ifp = ic->ic_ifp; 825138568Ssam struct ieee80211_node_table *nt; 826116742Ssam struct ieee80211_node *ni; 827117811Ssam enum ieee80211_state ostate; 828116742Ssam 829116742Ssam ostate = ic->ic_state; 830138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, 831138568Ssam ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 832117811Ssam ic->ic_state = nstate; /* state transition */ 833116742Ssam ni = ic->ic_bss; /* NB: no reference held */ 834116742Ssam switch (nstate) { 835116742Ssam case IEEE80211_S_INIT: 836116742Ssam switch (ostate) { 837116742Ssam case IEEE80211_S_INIT: 838116742Ssam break; 839116742Ssam case IEEE80211_S_RUN: 840116742Ssam switch (ic->ic_opmode) { 841116742Ssam case IEEE80211_M_STA: 842116742Ssam IEEE80211_SEND_MGMT(ic, ni, 843116742Ssam IEEE80211_FC0_SUBTYPE_DISASSOC, 844116742Ssam IEEE80211_REASON_ASSOC_LEAVE); 845138568Ssam ieee80211_sta_leave(ic, ni); 846116742Ssam break; 847116742Ssam case IEEE80211_M_HOSTAP: 848138568Ssam nt = ic->ic_sta; 849138568Ssam if (nt == NULL) { /* XXX cannot happen */ 850138568Ssam if_printf(ifp, "no sta table (run)\n"); 851138568Ssam break; 852138568Ssam } 853138568Ssam IEEE80211_NODE_LOCK(nt); 854138568Ssam TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { 855116742Ssam if (ni->ni_associd == 0) 856116742Ssam continue; 857116742Ssam IEEE80211_SEND_MGMT(ic, ni, 858116742Ssam IEEE80211_FC0_SUBTYPE_DISASSOC, 859116742Ssam IEEE80211_REASON_ASSOC_LEAVE); 860116742Ssam } 861138568Ssam IEEE80211_NODE_UNLOCK(nt); 862116742Ssam break; 863116742Ssam default: 864116742Ssam break; 865116742Ssam } 866138568Ssam goto reset; 867116742Ssam case IEEE80211_S_ASSOC: 868116742Ssam switch (ic->ic_opmode) { 869116742Ssam case IEEE80211_M_STA: 870116742Ssam IEEE80211_SEND_MGMT(ic, ni, 871116742Ssam IEEE80211_FC0_SUBTYPE_DEAUTH, 872116742Ssam IEEE80211_REASON_AUTH_LEAVE); 873116742Ssam break; 874116742Ssam case IEEE80211_M_HOSTAP: 875138568Ssam nt = ic->ic_sta; 876138568Ssam if (nt == NULL) { /* XXX cannot happen */ 877138568Ssam if_printf(ifp, "no sta table (assoc)\n"); 878138568Ssam break; 879138568Ssam } 880138568Ssam IEEE80211_NODE_LOCK(nt); 881138568Ssam TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { 882116742Ssam IEEE80211_SEND_MGMT(ic, ni, 883116742Ssam IEEE80211_FC0_SUBTYPE_DEAUTH, 884116742Ssam IEEE80211_REASON_AUTH_LEAVE); 885116742Ssam } 886138568Ssam IEEE80211_NODE_UNLOCK(nt); 887116742Ssam break; 888116742Ssam default: 889116742Ssam break; 890116742Ssam } 891138568Ssam goto reset; 892116742Ssam case IEEE80211_S_AUTH: 893116742Ssam case IEEE80211_S_SCAN: 894138568Ssam reset: 895116742Ssam ic->ic_mgt_timer = 0; 896116742Ssam IF_DRAIN(&ic->ic_mgtq); 897138568Ssam ieee80211_reset_bss(ic); 898138568Ssam ieee80211_crypto_delglobalkeys(ic); 899116742Ssam break; 900116742Ssam } 901138568Ssam if (ic->ic_auth->ia_detach != NULL) 902138568Ssam ic->ic_auth->ia_detach(ic); 903116742Ssam break; 904116742Ssam case IEEE80211_S_SCAN: 905116742Ssam switch (ostate) { 906116742Ssam case IEEE80211_S_INIT: 907138568Ssam if ((ic->ic_opmode == IEEE80211_M_HOSTAP || 908138568Ssam ic->ic_opmode == IEEE80211_M_IBSS || 909138568Ssam ic->ic_opmode == IEEE80211_M_AHDEMO) && 910116742Ssam ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 911116742Ssam /* 912116742Ssam * AP operation and we already have a channel; 913116742Ssam * bypass the scan and startup immediately. 914116742Ssam */ 915116742Ssam ieee80211_create_ibss(ic, ic->ic_des_chan); 916116742Ssam } else { 917138568Ssam ieee80211_begin_scan(ic, arg); 918116742Ssam } 919116742Ssam break; 920116742Ssam case IEEE80211_S_SCAN: 921138568Ssam /* 922138568Ssam * Scan next. If doing an active scan and the 923138568Ssam * channel is not marked passive-only then send 924138568Ssam * a probe request. Otherwise just listen for 925138568Ssam * beacons on the channel. 926138568Ssam */ 927138568Ssam if ((ic->ic_flags & IEEE80211_F_ASCAN) && 928138568Ssam (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { 929116742Ssam IEEE80211_SEND_MGMT(ic, ni, 930116742Ssam IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); 931116742Ssam } 932116742Ssam break; 933116742Ssam case IEEE80211_S_RUN: 934116742Ssam /* beacon miss */ 935138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, 936138568Ssam "no recent beacons from %s; rescanning\n", 937138568Ssam ether_sprintf(ic->ic_bss->ni_bssid)); 938138568Ssam ieee80211_sta_leave(ic, ni); 939138568Ssam ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ 940116742Ssam /* FALLTHRU */ 941116742Ssam case IEEE80211_S_AUTH: 942116742Ssam case IEEE80211_S_ASSOC: 943116742Ssam /* timeout restart scan */ 944138568Ssam ni = ieee80211_find_node(&ic->ic_scan, 945138568Ssam ic->ic_bss->ni_macaddr); 946116742Ssam if (ni != NULL) { 947116742Ssam ni->ni_fails++; 948116742Ssam ieee80211_unref_node(&ni); 949116742Ssam } 950138568Ssam ieee80211_begin_scan(ic, arg); 951116742Ssam break; 952116742Ssam } 953116742Ssam break; 954116742Ssam case IEEE80211_S_AUTH: 955116742Ssam switch (ostate) { 956116742Ssam case IEEE80211_S_INIT: 957116742Ssam case IEEE80211_S_SCAN: 958116742Ssam IEEE80211_SEND_MGMT(ic, ni, 959116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1); 960116742Ssam break; 961116742Ssam case IEEE80211_S_AUTH: 962116742Ssam case IEEE80211_S_ASSOC: 963138568Ssam switch (arg) { 964116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 965116742Ssam /* ??? */ 966116742Ssam IEEE80211_SEND_MGMT(ic, ni, 967116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 2); 968116742Ssam break; 969116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 970116742Ssam /* ignore and retry scan on timeout */ 971116742Ssam break; 972116742Ssam } 973116742Ssam break; 974116742Ssam case IEEE80211_S_RUN: 975138568Ssam switch (arg) { 976116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 977116742Ssam IEEE80211_SEND_MGMT(ic, ni, 978116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 2); 979116742Ssam ic->ic_state = ostate; /* stay RUN */ 980116742Ssam break; 981116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 982116742Ssam /* try to reauth */ 983116742Ssam IEEE80211_SEND_MGMT(ic, ni, 984116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1); 985138568Ssam ieee80211_sta_leave(ic, ni); 986116742Ssam break; 987116742Ssam } 988116742Ssam break; 989116742Ssam } 990116742Ssam break; 991116742Ssam case IEEE80211_S_ASSOC: 992116742Ssam switch (ostate) { 993116742Ssam case IEEE80211_S_INIT: 994116742Ssam case IEEE80211_S_SCAN: 995116742Ssam case IEEE80211_S_ASSOC: 996138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 997138568Ssam "%s: invalid transition\n", __func__); 998116742Ssam break; 999116742Ssam case IEEE80211_S_AUTH: 1000116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1001116742Ssam IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 1002116742Ssam break; 1003116742Ssam case IEEE80211_S_RUN: 1004116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1005116742Ssam IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); 1006138568Ssam ieee80211_sta_leave(ic, ni); 1007116742Ssam break; 1008116742Ssam } 1009116742Ssam break; 1010116742Ssam case IEEE80211_S_RUN: 1011138568Ssam if (ic->ic_flags & IEEE80211_F_WPA) { 1012138568Ssam /* XXX validate prerequisites */ 1013138568Ssam } 1014116742Ssam switch (ostate) { 1015116742Ssam case IEEE80211_S_INIT: 1016138568Ssam if (ic->ic_opmode == IEEE80211_M_MONITOR) 1017138568Ssam break; 1018138568Ssam /* fall thru... */ 1019116742Ssam case IEEE80211_S_AUTH: 1020116742Ssam case IEEE80211_S_RUN: 1021138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 1022138568Ssam "%s: invalid transition\n", __func__); 1023116742Ssam break; 1024116742Ssam case IEEE80211_S_SCAN: /* adhoc/hostap mode */ 1025116742Ssam case IEEE80211_S_ASSOC: /* infra mode */ 1026116742Ssam KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, 1027116742Ssam ("%s: bogus xmit rate %u setup\n", __func__, 1028116742Ssam ni->ni_txrate)); 1029138568Ssam#ifdef IEEE80211_DEBUG 1030138568Ssam if (ieee80211_msg_debug(ic)) { 1031116742Ssam if (ic->ic_opmode == IEEE80211_M_STA) 1032138568Ssam if_printf(ifp, "associated "); 1033116742Ssam else 1034138568Ssam if_printf(ifp, "synchronized "); 1035116742Ssam printf("with %s ssid ", 1036116742Ssam ether_sprintf(ni->ni_bssid)); 1037116742Ssam ieee80211_print_essid(ic->ic_bss->ni_essid, 1038116742Ssam ni->ni_esslen); 1039116742Ssam printf(" channel %d start %uMb\n", 1040116742Ssam ieee80211_chan2ieee(ic, ni->ni_chan), 1041116742Ssam IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); 1042116742Ssam } 1043138568Ssam#endif 1044116742Ssam ic->ic_mgt_timer = 0; 1045138568Ssam if (ic->ic_opmode == IEEE80211_M_STA) 1046138568Ssam ieee80211_notify_node_join(ic, ni, 1047138568Ssam arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 1048138568Ssam if_start(ifp); /* XXX not authorized yet */ 1049116742Ssam break; 1050116742Ssam } 1051138568Ssam /* 1052138568Ssam * Start/stop the authenticator when operating as an 1053138568Ssam * AP. We delay until here to allow configuration to 1054138568Ssam * happen out of order. 1055138568Ssam */ 1056138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ 1057138568Ssam ic->ic_auth->ia_attach != NULL) { 1058138568Ssam /* XXX check failure */ 1059138568Ssam ic->ic_auth->ia_attach(ic); 1060138568Ssam } else if (ic->ic_auth->ia_detach != NULL) { 1061138568Ssam ic->ic_auth->ia_detach(ic); 1062138568Ssam } 1063138568Ssam /* 1064138568Ssam * When 802.1x is not in use mark the port authorized 1065138568Ssam * at this point so traffic can flow. 1066138568Ssam */ 1067138568Ssam if (ni->ni_authmode != IEEE80211_AUTH_8021X) 1068138568Ssam ieee80211_node_authorize(ic, ni); 1069138568Ssam /* 1070138568Ssam * Enable inactivity processing. 1071138568Ssam * XXX 1072138568Ssam */ 1073138568Ssam ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT; 1074138568Ssam if (ic->ic_sta != NULL) 1075138568Ssam ic->ic_sta->nt_inact_timer = IEEE80211_INACT_WAIT; 1076116742Ssam break; 1077116742Ssam } 1078116742Ssam return 0; 1079116742Ssam} 1080