ieee80211_proto.c revision 148299
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3139530Ssam * Copyright (c) 2002-2005 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 148299 2005-07-22 17:29:03Z 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 ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; 98148291Ssam ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT; 99148290Ssam ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; 100127648Ssam ic->ic_protmode = IEEE80211_PROT_CTSONLY; 101138568Ssam ic->ic_roaming = IEEE80211_ROAMING_AUTO; 102116742Ssam 103138568Ssam ic->ic_wme.wme_hipri_switch_hysteresis = 104138568Ssam AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 105138568Ssam 106121816Sbrooks mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); 107116742Ssam 108117811Ssam /* protocol state change handler */ 109117811Ssam ic->ic_newstate = ieee80211_newstate; 110117811Ssam 111116742Ssam /* initialize management frame handlers */ 112116742Ssam ic->ic_recv_mgmt = ieee80211_recv_mgmt; 113116742Ssam ic->ic_send_mgmt = ieee80211_send_mgmt; 114116742Ssam} 115116742Ssam 116116742Ssamvoid 117138568Ssamieee80211_proto_detach(struct ieee80211com *ic) 118116742Ssam{ 119116742Ssam 120138568Ssam /* 121138568Ssam * This should not be needed as we detach when reseting 122138568Ssam * the state but be conservative here since the 123138568Ssam * authenticator may do things like spawn kernel threads. 124138568Ssam */ 125138568Ssam if (ic->ic_auth->ia_detach) 126138568Ssam ic->ic_auth->ia_detach(ic); 127138568Ssam 128116742Ssam IF_DRAIN(&ic->ic_mgtq); 129116742Ssam mtx_destroy(&ic->ic_mgtq.ifq_mtx); 130138568Ssam 131138568Ssam /* 132138568Ssam * Detach any ACL'ator. 133138568Ssam */ 134138568Ssam if (ic->ic_acl != NULL) 135138568Ssam ic->ic_acl->iac_detach(ic); 136116742Ssam} 137116742Ssam 138138568Ssam/* 139138568Ssam * Simple-minded authenticator module support. 140138568Ssam */ 141138568Ssam 142138568Ssam#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 143138568Ssam/* XXX well-known names */ 144138568Ssamstatic const char *auth_modnames[IEEE80211_AUTH_MAX] = { 145138568Ssam "wlan_internal", /* IEEE80211_AUTH_NONE */ 146138568Ssam "wlan_internal", /* IEEE80211_AUTH_OPEN */ 147138568Ssam "wlan_internal", /* IEEE80211_AUTH_SHARED */ 148138568Ssam "wlan_xauth", /* IEEE80211_AUTH_8021X */ 149138568Ssam "wlan_internal", /* IEEE80211_AUTH_AUTO */ 150138568Ssam "wlan_xauth", /* IEEE80211_AUTH_WPA */ 151138568Ssam}; 152138568Ssamstatic const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 153138568Ssam 154138568Ssamstatic const struct ieee80211_authenticator auth_internal = { 155138568Ssam .ia_name = "wlan_internal", 156138568Ssam .ia_attach = NULL, 157138568Ssam .ia_detach = NULL, 158138568Ssam .ia_node_join = NULL, 159138568Ssam .ia_node_leave = NULL, 160138568Ssam}; 161138568Ssam 162138568Ssam/* 163138568Ssam * Setup internal authenticators once; they are never unregistered. 164138568Ssam */ 165138568Ssamstatic void 166138568Ssamieee80211_auth_setup(void) 167138568Ssam{ 168138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 169138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 170138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 171138568Ssam} 172138568SsamSYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 173138568Ssam 174138568Ssamconst struct ieee80211_authenticator * 175138568Ssamieee80211_authenticator_get(int auth) 176138568Ssam{ 177138568Ssam if (auth >= IEEE80211_AUTH_MAX) 178138568Ssam return NULL; 179138568Ssam if (authenticators[auth] == NULL) 180138568Ssam ieee80211_load_module(auth_modnames[auth]); 181138568Ssam return authenticators[auth]; 182138568Ssam} 183138568Ssam 184116742Ssamvoid 185138568Ssamieee80211_authenticator_register(int type, 186138568Ssam const struct ieee80211_authenticator *auth) 187116742Ssam{ 188138568Ssam if (type >= IEEE80211_AUTH_MAX) 189138568Ssam return; 190138568Ssam authenticators[type] = auth; 191138568Ssam} 192138568Ssam 193138568Ssamvoid 194138568Ssamieee80211_authenticator_unregister(int type) 195138568Ssam{ 196138568Ssam 197138568Ssam if (type >= IEEE80211_AUTH_MAX) 198138568Ssam return; 199138568Ssam authenticators[type] = NULL; 200138568Ssam} 201138568Ssam 202138568Ssam/* 203138568Ssam * Very simple-minded ACL module support. 204138568Ssam */ 205138568Ssam/* XXX just one for now */ 206138568Ssamstatic const struct ieee80211_aclator *acl = NULL; 207138568Ssam 208138568Ssamvoid 209138568Ssamieee80211_aclator_register(const struct ieee80211_aclator *iac) 210138568Ssam{ 211138568Ssam printf("wlan: %s acl policy registered\n", iac->iac_name); 212138568Ssam acl = iac; 213138568Ssam} 214138568Ssam 215138568Ssamvoid 216138568Ssamieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 217138568Ssam{ 218138568Ssam if (acl == iac) 219138568Ssam acl = NULL; 220138568Ssam printf("wlan: %s acl policy unregistered\n", iac->iac_name); 221138568Ssam} 222138568Ssam 223138568Ssamconst struct ieee80211_aclator * 224138568Ssamieee80211_aclator_get(const char *name) 225138568Ssam{ 226138568Ssam if (acl == NULL) 227138568Ssam ieee80211_load_module("wlan_acl"); 228138568Ssam return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 229138568Ssam} 230138568Ssam 231138568Ssamvoid 232138568Ssamieee80211_print_essid(const u_int8_t *essid, int len) 233138568Ssam{ 234138568Ssam const u_int8_t *p; 235116742Ssam int i; 236116742Ssam 237116742Ssam if (len > IEEE80211_NWID_LEN) 238116742Ssam len = IEEE80211_NWID_LEN; 239116742Ssam /* determine printable or not */ 240116742Ssam for (i = 0, p = essid; i < len; i++, p++) { 241116742Ssam if (*p < ' ' || *p > 0x7e) 242116742Ssam break; 243116742Ssam } 244116742Ssam if (i == len) { 245116742Ssam printf("\""); 246116742Ssam for (i = 0, p = essid; i < len; i++, p++) 247116742Ssam printf("%c", *p); 248116742Ssam printf("\""); 249116742Ssam } else { 250116742Ssam printf("0x"); 251116742Ssam for (i = 0, p = essid; i < len; i++, p++) 252116742Ssam printf("%02x", *p); 253116742Ssam } 254116742Ssam} 255116742Ssam 256116742Ssamvoid 257138568Ssamieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) 258116742Ssam{ 259138568Ssam const struct ieee80211_frame *wh; 260116742Ssam int i; 261116742Ssam 262138568Ssam wh = (const struct ieee80211_frame *)buf; 263116742Ssam switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 264116742Ssam case IEEE80211_FC1_DIR_NODS: 265116742Ssam printf("NODS %s", ether_sprintf(wh->i_addr2)); 266116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 267116742Ssam printf("(%s)", ether_sprintf(wh->i_addr3)); 268116742Ssam break; 269116742Ssam case IEEE80211_FC1_DIR_TODS: 270116742Ssam printf("TODS %s", ether_sprintf(wh->i_addr2)); 271116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 272116742Ssam printf("(%s)", ether_sprintf(wh->i_addr1)); 273116742Ssam break; 274116742Ssam case IEEE80211_FC1_DIR_FROMDS: 275116742Ssam printf("FRDS %s", ether_sprintf(wh->i_addr3)); 276116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 277116742Ssam printf("(%s)", ether_sprintf(wh->i_addr2)); 278116742Ssam break; 279116742Ssam case IEEE80211_FC1_DIR_DSTODS: 280138568Ssam printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1])); 281116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 282116742Ssam printf("(%s", ether_sprintf(wh->i_addr2)); 283116742Ssam printf("->%s)", ether_sprintf(wh->i_addr1)); 284116742Ssam break; 285116742Ssam } 286116742Ssam switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 287116742Ssam case IEEE80211_FC0_TYPE_DATA: 288116742Ssam printf(" data"); 289116742Ssam break; 290116742Ssam case IEEE80211_FC0_TYPE_MGT: 291116742Ssam printf(" %s", ieee80211_mgt_subtype_name[ 292116742Ssam (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 293116742Ssam >> IEEE80211_FC0_SUBTYPE_SHIFT]); 294116742Ssam break; 295116742Ssam default: 296116742Ssam printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 297116742Ssam break; 298116742Ssam } 299138568Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 300138568Ssam int i; 301138568Ssam printf(" WEP [IV"); 302138568Ssam for (i = 0; i < IEEE80211_WEP_IVLEN; i++) 303138568Ssam printf(" %.02x", buf[sizeof(*wh)+i]); 304138568Ssam printf(" KID %u]", buf[sizeof(*wh)+i] >> 6); 305138568Ssam } 306116742Ssam if (rate >= 0) 307116742Ssam printf(" %dM", rate / 2); 308116742Ssam if (rssi >= 0) 309116742Ssam printf(" +%d", rssi); 310116742Ssam printf("\n"); 311116742Ssam if (len > 0) { 312116742Ssam for (i = 0; i < len; i++) { 313116742Ssam if ((i & 1) == 0) 314116742Ssam printf(" "); 315116742Ssam printf("%02x", buf[i]); 316116742Ssam } 317116742Ssam printf("\n"); 318116742Ssam } 319116742Ssam} 320116742Ssam 321116742Ssamint 322148299Ssamieee80211_fix_rate(struct ieee80211_node *ni, int flags) 323116742Ssam{ 324116742Ssam#define RV(v) ((v) & IEEE80211_RATE_VAL) 325148299Ssam struct ieee80211com *ic = ni->ni_ic; 326116742Ssam int i, j, ignore, error; 327138568Ssam int okrate, badrate, fixedrate; 328116742Ssam struct ieee80211_rateset *srs, *nrs; 329116742Ssam u_int8_t r; 330116742Ssam 331138568Ssam /* 332138568Ssam * If the fixed rate check was requested but no 333138568Ssam * fixed has been defined then just remove it. 334138568Ssam */ 335148290Ssam if ((flags & IEEE80211_F_DOFRATE) && 336148290Ssam ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) 337138568Ssam flags &= ~IEEE80211_F_DOFRATE; 338116742Ssam error = 0; 339138568Ssam okrate = badrate = fixedrate = 0; 340116742Ssam srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; 341116742Ssam nrs = &ni->ni_rates; 342120482Ssam for (i = 0; i < nrs->rs_nrates; ) { 343116742Ssam ignore = 0; 344116742Ssam if (flags & IEEE80211_F_DOSORT) { 345116742Ssam /* 346116742Ssam * Sort rates. 347116742Ssam */ 348116742Ssam for (j = i + 1; j < nrs->rs_nrates; j++) { 349116742Ssam if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 350116742Ssam r = nrs->rs_rates[i]; 351116742Ssam nrs->rs_rates[i] = nrs->rs_rates[j]; 352116742Ssam nrs->rs_rates[j] = r; 353116742Ssam } 354116742Ssam } 355116742Ssam } 356116742Ssam r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 357116742Ssam badrate = r; 358116742Ssam if (flags & IEEE80211_F_DOFRATE) { 359116742Ssam /* 360138568Ssam * Check any fixed rate is included. 361116742Ssam */ 362138568Ssam if (r == RV(srs->rs_rates[ic->ic_fixed_rate])) 363138568Ssam fixedrate = r; 364116742Ssam } 365116742Ssam if (flags & IEEE80211_F_DONEGO) { 366116742Ssam /* 367116742Ssam * Check against supported rates. 368116742Ssam */ 369116742Ssam for (j = 0; j < srs->rs_nrates; j++) { 370127761Ssam if (r == RV(srs->rs_rates[j])) { 371127761Ssam /* 372127761Ssam * Overwrite with the supported rate 373127761Ssam * value so any basic rate bit is set. 374127761Ssam * This insures that response we send 375127761Ssam * to stations have the necessary basic 376127761Ssam * rate bit set. 377127761Ssam */ 378127761Ssam nrs->rs_rates[i] = srs->rs_rates[j]; 379116742Ssam break; 380127761Ssam } 381116742Ssam } 382116742Ssam if (j == srs->rs_nrates) { 383120482Ssam /* 384120482Ssam * A rate in the node's rate set is not 385120482Ssam * supported. If this is a basic rate and we 386120482Ssam * are operating as an AP then this is an error. 387120482Ssam * Otherwise we just discard/ignore the rate. 388120482Ssam * Note that this is important for 11b stations 389120482Ssam * when they want to associate with an 11g AP. 390120482Ssam */ 391120482Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && 392120482Ssam (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 393116742Ssam error++; 394116742Ssam ignore++; 395116742Ssam } 396116742Ssam } 397116742Ssam if (flags & IEEE80211_F_DODEL) { 398116742Ssam /* 399116742Ssam * Delete unacceptable rates. 400116742Ssam */ 401116742Ssam if (ignore) { 402116742Ssam nrs->rs_nrates--; 403116742Ssam for (j = i; j < nrs->rs_nrates; j++) 404116742Ssam nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 405116742Ssam nrs->rs_rates[j] = 0; 406116742Ssam continue; 407116742Ssam } 408116742Ssam } 409116742Ssam if (!ignore) 410116742Ssam okrate = nrs->rs_rates[i]; 411116742Ssam i++; 412116742Ssam } 413138568Ssam if (okrate == 0 || error != 0 || 414138568Ssam ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0)) 415116742Ssam return badrate | IEEE80211_RATE_BASIC; 416116742Ssam else 417116742Ssam return RV(okrate); 418116742Ssam#undef RV 419116742Ssam} 420116742Ssam 421138568Ssam/* 422138568Ssam * Reset 11g-related state. 423138568Ssam */ 424138568Ssamvoid 425138568Ssamieee80211_reset_erp(struct ieee80211com *ic) 426138568Ssam{ 427138568Ssam ic->ic_flags &= ~IEEE80211_F_USEPROT; 428138568Ssam ic->ic_nonerpsta = 0; 429138568Ssam ic->ic_longslotsta = 0; 430138568Ssam /* 431138568Ssam * Short slot time is enabled only when operating in 11g 432138568Ssam * and not in an IBSS. We must also honor whether or not 433138568Ssam * the driver is capable of doing it. 434138568Ssam */ 435138568Ssam ieee80211_set_shortslottime(ic, 436138568Ssam ic->ic_curmode == IEEE80211_MODE_11A || 437138568Ssam (ic->ic_curmode == IEEE80211_MODE_11G && 438138568Ssam ic->ic_opmode == IEEE80211_M_HOSTAP && 439138568Ssam (ic->ic_caps & IEEE80211_C_SHSLOT))); 440138568Ssam /* 441138568Ssam * Set short preamble and ERP barker-preamble flags. 442138568Ssam */ 443138568Ssam if (ic->ic_curmode == IEEE80211_MODE_11A || 444138568Ssam (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 445138568Ssam ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 446138568Ssam ic->ic_flags &= ~IEEE80211_F_USEBARKER; 447138568Ssam } else { 448138568Ssam ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 449138568Ssam ic->ic_flags |= IEEE80211_F_USEBARKER; 450138568Ssam } 451138568Ssam} 452138568Ssam 453138568Ssam/* 454138568Ssam * Set the short slot time state and notify the driver. 455138568Ssam */ 456138568Ssamvoid 457138568Ssamieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 458138568Ssam{ 459138568Ssam if (onoff) 460138568Ssam ic->ic_flags |= IEEE80211_F_SHSLOT; 461138568Ssam else 462138568Ssam ic->ic_flags &= ~IEEE80211_F_SHSLOT; 463138568Ssam /* notify driver */ 464138568Ssam if (ic->ic_updateslot != NULL) 465138568Ssam ic->ic_updateslot(ic->ic_ifp); 466138568Ssam} 467138568Ssam 468138568Ssam/* 469138568Ssam * Check if the specified rate set supports ERP. 470138568Ssam * NB: the rate set is assumed to be sorted. 471138568Ssam */ 472138568Ssamint 473138568Ssamieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) 474138568Ssam{ 475138568Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 476138568Ssam static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 477138568Ssam int i, j; 478138568Ssam 479138568Ssam if (rs->rs_nrates < N(rates)) 480138568Ssam return 0; 481138568Ssam for (i = 0; i < N(rates); i++) { 482138568Ssam for (j = 0; j < rs->rs_nrates; j++) { 483138568Ssam int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 484138568Ssam if (rates[i] == r) 485138568Ssam goto next; 486138568Ssam if (r > rates[i]) 487138568Ssam return 0; 488138568Ssam } 489138568Ssam return 0; 490138568Ssam next: 491138568Ssam ; 492138568Ssam } 493138568Ssam return 1; 494138568Ssam#undef N 495138568Ssam} 496138568Ssam 497138568Ssam/* 498138568Ssam * Mark the basic rates for the 11g rate table based on the 499138568Ssam * operating mode. For real 11g we mark all the 11b rates 500138568Ssam * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 501138568Ssam * 11b rates. There's also a pseudo 11a-mode used to mark only 502138568Ssam * the basic OFDM rates. 503138568Ssam */ 504138568Ssamvoid 505138568Ssamieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) 506138568Ssam{ 507138568Ssam static const struct ieee80211_rateset basic[] = { 508138568Ssam { 0 }, /* IEEE80211_MODE_AUTO */ 509138568Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 510138568Ssam { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ 511138568Ssam { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ 512138568Ssam { 0 }, /* IEEE80211_MODE_FH */ 513138568Ssam /* IEEE80211_MODE_PUREG (not yet) */ 514138568Ssam { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 515138568Ssam }; 516138568Ssam int i, j; 517138568Ssam 518138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 519138568Ssam rs->rs_rates[i] &= IEEE80211_RATE_VAL; 520138568Ssam for (j = 0; j < basic[mode].rs_nrates; j++) 521138568Ssam if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 522138568Ssam rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 523138568Ssam break; 524138568Ssam } 525138568Ssam } 526138568Ssam} 527138568Ssam 528138568Ssam/* 529138568Ssam * WME protocol support. The following parameters come from the spec. 530138568Ssam */ 531138568Ssamtypedef struct phyParamType { 532138568Ssam u_int8_t aifsn; 533138568Ssam u_int8_t logcwmin; 534138568Ssam u_int8_t logcwmax; 535138568Ssam u_int16_t txopLimit; 536138568Ssam u_int8_t acm; 537138568Ssam} paramType; 538138568Ssam 539138568Ssamstatic const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 540138568Ssam { 3, 4, 6 }, /* IEEE80211_MODE_AUTO */ 541138568Ssam { 3, 4, 6 }, /* IEEE80211_MODE_11A */ 542138568Ssam { 3, 5, 7 }, /* IEEE80211_MODE_11B */ 543138568Ssam { 3, 4, 6 }, /* IEEE80211_MODE_11G */ 544138568Ssam { 3, 5, 7 }, /* IEEE80211_MODE_FH */ 545138568Ssam { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_A */ 546138568Ssam { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_G */ 547138568Ssam}; 548138568Ssamstatic const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 549138568Ssam { 7, 4, 10 }, /* IEEE80211_MODE_AUTO */ 550138568Ssam { 7, 4, 10 }, /* IEEE80211_MODE_11A */ 551138568Ssam { 7, 5, 10 }, /* IEEE80211_MODE_11B */ 552138568Ssam { 7, 4, 10 }, /* IEEE80211_MODE_11G */ 553138568Ssam { 7, 5, 10 }, /* IEEE80211_MODE_FH */ 554138568Ssam { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ 555138568Ssam { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ 556138568Ssam}; 557138568Ssamstatic const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 558138568Ssam { 1, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ 559138568Ssam { 1, 3, 4, 94 }, /* IEEE80211_MODE_11A */ 560138568Ssam { 1, 4, 5, 188 }, /* IEEE80211_MODE_11B */ 561138568Ssam { 1, 3, 4, 94 }, /* IEEE80211_MODE_11G */ 562138568Ssam { 1, 4, 5, 188 }, /* IEEE80211_MODE_FH */ 563138568Ssam { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ 564138568Ssam { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ 565138568Ssam}; 566138568Ssamstatic const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 567138568Ssam { 1, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ 568138568Ssam { 1, 2, 3, 47 }, /* IEEE80211_MODE_11A */ 569138568Ssam { 1, 3, 4, 102 }, /* IEEE80211_MODE_11B */ 570138568Ssam { 1, 2, 3, 47 }, /* IEEE80211_MODE_11G */ 571138568Ssam { 1, 3, 4, 102 }, /* IEEE80211_MODE_FH */ 572138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ 573138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ 574138568Ssam}; 575138568Ssam 576138568Ssamstatic const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 577138568Ssam { 3, 4, 10 }, /* IEEE80211_MODE_AUTO */ 578138568Ssam { 3, 4, 10 }, /* IEEE80211_MODE_11A */ 579138568Ssam { 3, 5, 10 }, /* IEEE80211_MODE_11B */ 580138568Ssam { 3, 4, 10 }, /* IEEE80211_MODE_11G */ 581138568Ssam { 3, 5, 10 }, /* IEEE80211_MODE_FH */ 582138568Ssam { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ 583138568Ssam { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ 584138568Ssam}; 585138568Ssamstatic const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 586138568Ssam { 2, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ 587138568Ssam { 2, 3, 4, 94 }, /* IEEE80211_MODE_11A */ 588138568Ssam { 2, 4, 5, 188 }, /* IEEE80211_MODE_11B */ 589138568Ssam { 2, 3, 4, 94 }, /* IEEE80211_MODE_11G */ 590138568Ssam { 2, 4, 5, 188 }, /* IEEE80211_MODE_FH */ 591138568Ssam { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ 592138568Ssam { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ 593138568Ssam}; 594138568Ssamstatic const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 595138568Ssam { 2, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ 596138568Ssam { 2, 2, 3, 47 }, /* IEEE80211_MODE_11A */ 597138568Ssam { 2, 3, 4, 102 }, /* IEEE80211_MODE_11B */ 598138568Ssam { 2, 2, 3, 47 }, /* IEEE80211_MODE_11G */ 599138568Ssam { 2, 3, 4, 102 }, /* IEEE80211_MODE_FH */ 600138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ 601138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ 602138568Ssam}; 603138568Ssam 604138568Ssamvoid 605138568Ssamieee80211_wme_initparams(struct ieee80211com *ic) 606138568Ssam{ 607138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 608138568Ssam const paramType *pPhyParam, *pBssPhyParam; 609138568Ssam struct wmeParams *wmep; 610138568Ssam int i; 611138568Ssam 612138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 613138568Ssam return; 614138568Ssam 615138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 616138568Ssam switch (i) { 617138568Ssam case WME_AC_BK: 618138568Ssam pPhyParam = &phyParamForAC_BK[ic->ic_curmode]; 619138568Ssam pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode]; 620138568Ssam break; 621138568Ssam case WME_AC_VI: 622138568Ssam pPhyParam = &phyParamForAC_VI[ic->ic_curmode]; 623138568Ssam pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode]; 624138568Ssam break; 625138568Ssam case WME_AC_VO: 626138568Ssam pPhyParam = &phyParamForAC_VO[ic->ic_curmode]; 627138568Ssam pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode]; 628138568Ssam break; 629138568Ssam case WME_AC_BE: 630138568Ssam default: 631138568Ssam pPhyParam = &phyParamForAC_BE[ic->ic_curmode]; 632138568Ssam pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode]; 633138568Ssam break; 634138568Ssam } 635138568Ssam 636138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 637138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 638138568Ssam wmep->wmep_acm = pPhyParam->acm; 639138568Ssam wmep->wmep_aifsn = pPhyParam->aifsn; 640138568Ssam wmep->wmep_logcwmin = pPhyParam->logcwmin; 641138568Ssam wmep->wmep_logcwmax = pPhyParam->logcwmax; 642138568Ssam wmep->wmep_txopLimit = pPhyParam->txopLimit; 643138568Ssam } else { 644138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 645138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 646138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 647138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 648138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 649138568Ssam 650138568Ssam } 651138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 652138568Ssam "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " 653138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 654138568Ssam , ieee80211_wme_acnames[i] 655138568Ssam , wmep->wmep_acm 656138568Ssam , wmep->wmep_aifsn 657138568Ssam , wmep->wmep_logcwmin 658138568Ssam , wmep->wmep_logcwmax 659138568Ssam , wmep->wmep_txopLimit 660138568Ssam ); 661138568Ssam 662138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 663138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 664138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 665138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 666138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 667138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 668138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 669138568Ssam "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " 670138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 671138568Ssam , ieee80211_wme_acnames[i] 672138568Ssam , wmep->wmep_acm 673138568Ssam , wmep->wmep_aifsn 674138568Ssam , wmep->wmep_logcwmin 675138568Ssam , wmep->wmep_logcwmax 676138568Ssam , wmep->wmep_txopLimit 677138568Ssam ); 678138568Ssam } 679138568Ssam /* NB: check ic_bss to avoid NULL deref on initial attach */ 680138568Ssam if (ic->ic_bss != NULL) { 681138568Ssam /* 682138568Ssam * Calculate agressive mode switching threshold based 683138568Ssam * on beacon interval. This doesn't need locking since 684138568Ssam * we're only called before entering the RUN state at 685138568Ssam * which point we start sending beacon frames. 686138568Ssam */ 687138568Ssam wme->wme_hipri_switch_thresh = 688138568Ssam (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; 689138568Ssam ieee80211_wme_updateparams(ic); 690138568Ssam } 691138568Ssam} 692138568Ssam 693138568Ssam/* 694138568Ssam * Update WME parameters for ourself and the BSS. 695138568Ssam */ 696138568Ssamvoid 697138568Ssamieee80211_wme_updateparams_locked(struct ieee80211com *ic) 698138568Ssam{ 699138568Ssam static const paramType phyParam[IEEE80211_MODE_MAX] = { 700138568Ssam { 2, 4, 10, 64 }, /* IEEE80211_MODE_AUTO */ 701138568Ssam { 2, 4, 10, 64 }, /* IEEE80211_MODE_11A */ 702138568Ssam { 2, 5, 10, 64 }, /* IEEE80211_MODE_11B */ 703138568Ssam { 2, 4, 10, 64 }, /* IEEE80211_MODE_11G */ 704138568Ssam { 2, 5, 10, 64 }, /* IEEE80211_MODE_FH */ 705138568Ssam { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_A */ 706138568Ssam { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_G */ 707138568Ssam }; 708138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 709138568Ssam const struct wmeParams *wmep; 710138568Ssam struct wmeParams *chanp, *bssp; 711138568Ssam int i; 712138568Ssam 713138568Ssam /* set up the channel access parameters for the physical device */ 714138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 715138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[i]; 716138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 717138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 718138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 719138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 720138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 721138568Ssam 722138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 723138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 724138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 725138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 726138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 727138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 728138568Ssam } 729138568Ssam 730138568Ssam /* 731138568Ssam * This implements agressive mode as found in certain 732138568Ssam * vendors' AP's. When there is significant high 733138568Ssam * priority (VI/VO) traffic in the BSS throttle back BE 734138568Ssam * traffic by using conservative parameters. Otherwise 735138568Ssam * BE uses agressive params to optimize performance of 736138568Ssam * legacy/non-QoS traffic. 737138568Ssam */ 738138568Ssam if ((ic->ic_opmode == IEEE80211_M_HOSTAP && 739138568Ssam (wme->wme_flags & WME_F_AGGRMODE) == 0) || 740138568Ssam (ic->ic_opmode != IEEE80211_M_HOSTAP && 741138568Ssam (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 742138568Ssam (ic->ic_flags & IEEE80211_F_WME) == 0) { 743138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 744138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 745138568Ssam 746138568Ssam chanp->wmep_aifsn = bssp->wmep_aifsn = 747138568Ssam phyParam[ic->ic_curmode].aifsn; 748138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 749138568Ssam phyParam[ic->ic_curmode].logcwmin; 750138568Ssam chanp->wmep_logcwmax = bssp->wmep_logcwmax = 751138568Ssam phyParam[ic->ic_curmode].logcwmax; 752138568Ssam chanp->wmep_txopLimit = bssp->wmep_txopLimit = 753138568Ssam (ic->ic_caps & IEEE80211_C_BURST) ? 754138568Ssam phyParam[ic->ic_curmode].txopLimit : 0; 755138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 756138568Ssam "%s: %s [acm %u aifsn %u log2(cwmin) %u " 757138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 758138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 759138568Ssam , chanp->wmep_acm 760138568Ssam , chanp->wmep_aifsn 761138568Ssam , chanp->wmep_logcwmin 762138568Ssam , chanp->wmep_logcwmax 763138568Ssam , chanp->wmep_txopLimit 764138568Ssam ); 765138568Ssam } 766138568Ssam 767138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && 768138568Ssam ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) == 0) { 769138568Ssam static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = { 770138568Ssam 3, /* IEEE80211_MODE_AUTO */ 771138568Ssam 3, /* IEEE80211_MODE_11A */ 772138568Ssam 4, /* IEEE80211_MODE_11B */ 773138568Ssam 3, /* IEEE80211_MODE_11G */ 774138568Ssam 4, /* IEEE80211_MODE_FH */ 775138568Ssam 3, /* IEEE80211_MODE_TURBO_A */ 776138568Ssam 3, /* IEEE80211_MODE_TURBO_G */ 777138568Ssam }; 778138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 779138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 780138568Ssam 781138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 782138568Ssam logCwMin[ic->ic_curmode]; 783138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 784138568Ssam "%s: %s log2(cwmin) %u\n", __func__ 785138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 786138568Ssam , chanp->wmep_logcwmin 787138568Ssam ); 788138568Ssam } 789138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 790138568Ssam /* 791138568Ssam * Arrange for a beacon update and bump the parameter 792138568Ssam * set number so associated stations load the new values. 793138568Ssam */ 794138568Ssam wme->wme_bssChanParams.cap_info = 795138568Ssam (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 796138568Ssam ic->ic_flags |= IEEE80211_F_WMEUPDATE; 797138568Ssam } 798138568Ssam 799138568Ssam wme->wme_update(ic); 800138568Ssam 801138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 802138568Ssam "%s: WME params updated, cap_info 0x%x\n", __func__, 803138568Ssam ic->ic_opmode == IEEE80211_M_STA ? 804138568Ssam wme->wme_wmeChanParams.cap_info : 805138568Ssam wme->wme_bssChanParams.cap_info); 806138568Ssam} 807138568Ssam 808138568Ssamvoid 809138568Ssamieee80211_wme_updateparams(struct ieee80211com *ic) 810138568Ssam{ 811138568Ssam 812138568Ssam if (ic->ic_caps & IEEE80211_C_WME) { 813138568Ssam IEEE80211_BEACON_LOCK(ic); 814138568Ssam ieee80211_wme_updateparams_locked(ic); 815138568Ssam IEEE80211_BEACON_UNLOCK(ic); 816138568Ssam } 817138568Ssam} 818138568Ssam 819147765Ssamstatic void 820147765Ssamsta_disassoc(void *arg, struct ieee80211_node *ni) 821147765Ssam{ 822147765Ssam struct ieee80211com *ic = arg; 823147765Ssam 824147765Ssam if (ni->ni_associd != 0) { 825147765Ssam IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, 826147765Ssam IEEE80211_REASON_ASSOC_LEAVE); 827147765Ssam ieee80211_node_leave(ic, ni); 828147765Ssam } 829147765Ssam} 830147765Ssam 831147765Ssamstatic void 832147765Ssamsta_deauth(void *arg, struct ieee80211_node *ni) 833147765Ssam{ 834147765Ssam struct ieee80211com *ic = arg; 835147765Ssam 836147765Ssam IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 837147765Ssam IEEE80211_REASON_ASSOC_LEAVE); 838147765Ssam} 839147765Ssam 840117811Ssamstatic int 841138568Ssamieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 842116742Ssam{ 843138568Ssam struct ifnet *ifp = ic->ic_ifp; 844116742Ssam struct ieee80211_node *ni; 845117811Ssam enum ieee80211_state ostate; 846116742Ssam 847116742Ssam ostate = ic->ic_state; 848138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, 849138568Ssam ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 850117811Ssam ic->ic_state = nstate; /* state transition */ 851116742Ssam ni = ic->ic_bss; /* NB: no reference held */ 852116742Ssam switch (nstate) { 853116742Ssam case IEEE80211_S_INIT: 854116742Ssam switch (ostate) { 855116742Ssam case IEEE80211_S_INIT: 856116742Ssam break; 857116742Ssam case IEEE80211_S_RUN: 858116742Ssam switch (ic->ic_opmode) { 859116742Ssam case IEEE80211_M_STA: 860116742Ssam IEEE80211_SEND_MGMT(ic, ni, 861116742Ssam IEEE80211_FC0_SUBTYPE_DISASSOC, 862116742Ssam IEEE80211_REASON_ASSOC_LEAVE); 863138568Ssam ieee80211_sta_leave(ic, ni); 864116742Ssam break; 865116742Ssam case IEEE80211_M_HOSTAP: 866147765Ssam ieee80211_iterate_nodes(&ic->ic_sta, 867147765Ssam sta_disassoc, ic); 868116742Ssam break; 869116742Ssam default: 870116742Ssam break; 871116742Ssam } 872138568Ssam goto reset; 873116742Ssam case IEEE80211_S_ASSOC: 874116742Ssam switch (ic->ic_opmode) { 875116742Ssam case IEEE80211_M_STA: 876116742Ssam IEEE80211_SEND_MGMT(ic, ni, 877116742Ssam IEEE80211_FC0_SUBTYPE_DEAUTH, 878116742Ssam IEEE80211_REASON_AUTH_LEAVE); 879116742Ssam break; 880116742Ssam case IEEE80211_M_HOSTAP: 881147765Ssam ieee80211_iterate_nodes(&ic->ic_sta, 882147765Ssam sta_deauth, ic); 883116742Ssam break; 884116742Ssam default: 885116742Ssam break; 886116742Ssam } 887138568Ssam goto reset; 888140441Ssam case IEEE80211_S_SCAN: 889140441Ssam ieee80211_cancel_scan(ic); 890140441Ssam goto reset; 891116742Ssam case IEEE80211_S_AUTH: 892138568Ssam reset: 893116742Ssam ic->ic_mgt_timer = 0; 894116742Ssam IF_DRAIN(&ic->ic_mgtq); 895138568Ssam ieee80211_reset_bss(ic); 896116742Ssam break; 897116742Ssam } 898138568Ssam if (ic->ic_auth->ia_detach != NULL) 899138568Ssam ic->ic_auth->ia_detach(ic); 900116742Ssam break; 901116742Ssam case IEEE80211_S_SCAN: 902116742Ssam switch (ostate) { 903116742Ssam case IEEE80211_S_INIT: 904138568Ssam if ((ic->ic_opmode == IEEE80211_M_HOSTAP || 905138568Ssam ic->ic_opmode == IEEE80211_M_IBSS || 906138568Ssam ic->ic_opmode == IEEE80211_M_AHDEMO) && 907116742Ssam ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 908116742Ssam /* 909116742Ssam * AP operation and we already have a channel; 910116742Ssam * bypass the scan and startup immediately. 911116742Ssam */ 912116742Ssam ieee80211_create_ibss(ic, ic->ic_des_chan); 913116742Ssam } else { 914138568Ssam ieee80211_begin_scan(ic, arg); 915116742Ssam } 916116742Ssam break; 917116742Ssam case IEEE80211_S_SCAN: 918138568Ssam /* 919138568Ssam * Scan next. If doing an active scan and the 920138568Ssam * channel is not marked passive-only then send 921138568Ssam * a probe request. Otherwise just listen for 922138568Ssam * beacons on the channel. 923138568Ssam */ 924138568Ssam if ((ic->ic_flags & IEEE80211_F_ASCAN) && 925138568Ssam (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { 926116742Ssam IEEE80211_SEND_MGMT(ic, ni, 927116742Ssam IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); 928116742Ssam } 929116742Ssam break; 930116742Ssam case IEEE80211_S_RUN: 931116742Ssam /* beacon miss */ 932138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, 933138568Ssam "no recent beacons from %s; rescanning\n", 934138568Ssam ether_sprintf(ic->ic_bss->ni_bssid)); 935138568Ssam ieee80211_sta_leave(ic, ni); 936138568Ssam ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ 937116742Ssam /* FALLTHRU */ 938116742Ssam case IEEE80211_S_AUTH: 939116742Ssam case IEEE80211_S_ASSOC: 940116742Ssam /* timeout restart scan */ 941138568Ssam ni = ieee80211_find_node(&ic->ic_scan, 942138568Ssam ic->ic_bss->ni_macaddr); 943116742Ssam if (ni != NULL) { 944116742Ssam ni->ni_fails++; 945116742Ssam ieee80211_unref_node(&ni); 946116742Ssam } 947147116Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 948147116Ssam ieee80211_begin_scan(ic, arg); 949116742Ssam break; 950116742Ssam } 951116742Ssam break; 952116742Ssam case IEEE80211_S_AUTH: 953116742Ssam switch (ostate) { 954116742Ssam case IEEE80211_S_INIT: 955116742Ssam case IEEE80211_S_SCAN: 956116742Ssam IEEE80211_SEND_MGMT(ic, ni, 957116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1); 958116742Ssam break; 959116742Ssam case IEEE80211_S_AUTH: 960116742Ssam case IEEE80211_S_ASSOC: 961138568Ssam switch (arg) { 962116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 963116742Ssam /* ??? */ 964116742Ssam IEEE80211_SEND_MGMT(ic, ni, 965116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 2); 966116742Ssam break; 967116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 968116742Ssam /* ignore and retry scan on timeout */ 969116742Ssam break; 970116742Ssam } 971116742Ssam break; 972116742Ssam case IEEE80211_S_RUN: 973138568Ssam switch (arg) { 974116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 975116742Ssam IEEE80211_SEND_MGMT(ic, ni, 976116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 2); 977116742Ssam ic->ic_state = ostate; /* stay RUN */ 978116742Ssam break; 979116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 980138568Ssam ieee80211_sta_leave(ic, ni); 981147116Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 982147116Ssam /* try to reauth */ 983147116Ssam IEEE80211_SEND_MGMT(ic, ni, 984147116Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1); 985147116Ssam } 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: 1004138568Ssam ieee80211_sta_leave(ic, ni); 1005147116Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 1006147116Ssam IEEE80211_SEND_MGMT(ic, ni, 1007147116Ssam IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); 1008147116Ssam } 1009116742Ssam break; 1010116742Ssam } 1011116742Ssam break; 1012116742Ssam case IEEE80211_S_RUN: 1013138568Ssam if (ic->ic_flags & IEEE80211_F_WPA) { 1014138568Ssam /* XXX validate prerequisites */ 1015138568Ssam } 1016116742Ssam switch (ostate) { 1017116742Ssam case IEEE80211_S_INIT: 1018138568Ssam if (ic->ic_opmode == IEEE80211_M_MONITOR) 1019138568Ssam break; 1020138568Ssam /* fall thru... */ 1021116742Ssam case IEEE80211_S_AUTH: 1022138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 1023138568Ssam "%s: invalid transition\n", __func__); 1024140763Ssam /* fall thru... */ 1025140763Ssam case IEEE80211_S_RUN: 1026116742Ssam break; 1027116742Ssam case IEEE80211_S_SCAN: /* adhoc/hostap mode */ 1028116742Ssam case IEEE80211_S_ASSOC: /* infra mode */ 1029116742Ssam KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, 1030116742Ssam ("%s: bogus xmit rate %u setup\n", __func__, 1031116742Ssam ni->ni_txrate)); 1032138568Ssam#ifdef IEEE80211_DEBUG 1033138568Ssam if (ieee80211_msg_debug(ic)) { 1034116742Ssam if (ic->ic_opmode == IEEE80211_M_STA) 1035138568Ssam if_printf(ifp, "associated "); 1036116742Ssam else 1037138568Ssam if_printf(ifp, "synchronized "); 1038116742Ssam printf("with %s ssid ", 1039116742Ssam ether_sprintf(ni->ni_bssid)); 1040116742Ssam ieee80211_print_essid(ic->ic_bss->ni_essid, 1041116742Ssam ni->ni_esslen); 1042116742Ssam printf(" channel %d start %uMb\n", 1043116742Ssam ieee80211_chan2ieee(ic, ni->ni_chan), 1044116742Ssam IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); 1045116742Ssam } 1046138568Ssam#endif 1047116742Ssam ic->ic_mgt_timer = 0; 1048138568Ssam if (ic->ic_opmode == IEEE80211_M_STA) 1049138568Ssam ieee80211_notify_node_join(ic, ni, 1050138568Ssam arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 1051138568Ssam if_start(ifp); /* XXX not authorized yet */ 1052116742Ssam break; 1053116742Ssam } 1054138568Ssam /* 1055138568Ssam * Start/stop the authenticator when operating as an 1056138568Ssam * AP. We delay until here to allow configuration to 1057138568Ssam * happen out of order. 1058138568Ssam */ 1059138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ 1060138568Ssam ic->ic_auth->ia_attach != NULL) { 1061138568Ssam /* XXX check failure */ 1062138568Ssam ic->ic_auth->ia_attach(ic); 1063138568Ssam } else if (ic->ic_auth->ia_detach != NULL) { 1064138568Ssam ic->ic_auth->ia_detach(ic); 1065138568Ssam } 1066138568Ssam /* 1067138568Ssam * When 802.1x is not in use mark the port authorized 1068138568Ssam * at this point so traffic can flow. 1069138568Ssam */ 1070138568Ssam if (ni->ni_authmode != IEEE80211_AUTH_8021X) 1071138568Ssam ieee80211_node_authorize(ic, ni); 1072138568Ssam /* 1073138568Ssam * Enable inactivity processing. 1074138568Ssam * XXX 1075138568Ssam */ 1076138568Ssam ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT; 1077140753Ssam ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT; 1078116742Ssam break; 1079116742Ssam } 1080116742Ssam return 0; 1081116742Ssam} 1082