ieee80211_proto.c revision 167283
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 167283 2007-03-07 04:35:07Z 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}; 72167283Ssamconst char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { 73167283Ssam "IBSS", /* IEEE80211_M_IBSS */ 74167283Ssam "STA", /* IEEE80211_M_STA */ 75167283Ssam "#2", 76167283Ssam "AHDEMO", /* IEEE80211_M_AHDEMO */ 77167283Ssam "#4", "#5", 78167283Ssam "HOSTAP", /* IEEE80211_M_HOSTAP */ 79167283Ssam "#7", 80167283Ssam "MONITOR" /* IEEE80211_M_MONITOR */ 81167283Ssam}; 82117811Ssamconst char *ieee80211_state_name[IEEE80211_S_MAX] = { 83117811Ssam "INIT", /* IEEE80211_S_INIT */ 84117811Ssam "SCAN", /* IEEE80211_S_SCAN */ 85117811Ssam "AUTH", /* IEEE80211_S_AUTH */ 86117811Ssam "ASSOC", /* IEEE80211_S_ASSOC */ 87117811Ssam "RUN" /* IEEE80211_S_RUN */ 88117811Ssam}; 89138568Ssamconst char *ieee80211_wme_acnames[] = { 90138568Ssam "WME_AC_BE", 91138568Ssam "WME_AC_BK", 92138568Ssam "WME_AC_VI", 93138568Ssam "WME_AC_VO", 94138568Ssam "WME_UPSD", 95138568Ssam}; 96116742Ssam 97117811Ssamstatic int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); 98117811Ssam 99116742Ssamvoid 100138568Ssamieee80211_proto_attach(struct ieee80211com *ic) 101116742Ssam{ 102138568Ssam struct ifnet *ifp = ic->ic_ifp; 103116742Ssam 104138568Ssam /* XXX room for crypto */ 105138568Ssam ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); 106116742Ssam 107116742Ssam ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; 108148291Ssam ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT; 109148290Ssam ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; 110153349Ssam ic->ic_bmiss_max = IEEE80211_BMISS_MAX; 111154736Ssam callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE); 112153346Ssam ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT; 113127648Ssam ic->ic_protmode = IEEE80211_PROT_CTSONLY; 114138568Ssam ic->ic_roaming = IEEE80211_ROAMING_AUTO; 115116742Ssam 116138568Ssam ic->ic_wme.wme_hipri_switch_hysteresis = 117138568Ssam AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 118138568Ssam 119121816Sbrooks mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); 120116742Ssam 121117811Ssam /* protocol state change handler */ 122117811Ssam ic->ic_newstate = ieee80211_newstate; 123117811Ssam 124116742Ssam /* initialize management frame handlers */ 125116742Ssam ic->ic_recv_mgmt = ieee80211_recv_mgmt; 126116742Ssam ic->ic_send_mgmt = ieee80211_send_mgmt; 127160690Ssam ic->ic_raw_xmit = ieee80211_raw_xmit; 128116742Ssam} 129116742Ssam 130116742Ssamvoid 131138568Ssamieee80211_proto_detach(struct ieee80211com *ic) 132116742Ssam{ 133116742Ssam 134138568Ssam /* 135138568Ssam * This should not be needed as we detach when reseting 136138568Ssam * the state but be conservative here since the 137138568Ssam * authenticator may do things like spawn kernel threads. 138138568Ssam */ 139138568Ssam if (ic->ic_auth->ia_detach) 140138568Ssam ic->ic_auth->ia_detach(ic); 141138568Ssam 142165894Ssam ieee80211_drain_ifq(&ic->ic_mgtq); 143116742Ssam mtx_destroy(&ic->ic_mgtq.ifq_mtx); 144138568Ssam 145138568Ssam /* 146138568Ssam * Detach any ACL'ator. 147138568Ssam */ 148138568Ssam if (ic->ic_acl != NULL) 149138568Ssam ic->ic_acl->iac_detach(ic); 150116742Ssam} 151116742Ssam 152138568Ssam/* 153138568Ssam * Simple-minded authenticator module support. 154138568Ssam */ 155138568Ssam 156138568Ssam#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 157138568Ssam/* XXX well-known names */ 158138568Ssamstatic const char *auth_modnames[IEEE80211_AUTH_MAX] = { 159138568Ssam "wlan_internal", /* IEEE80211_AUTH_NONE */ 160138568Ssam "wlan_internal", /* IEEE80211_AUTH_OPEN */ 161138568Ssam "wlan_internal", /* IEEE80211_AUTH_SHARED */ 162138568Ssam "wlan_xauth", /* IEEE80211_AUTH_8021X */ 163138568Ssam "wlan_internal", /* IEEE80211_AUTH_AUTO */ 164138568Ssam "wlan_xauth", /* IEEE80211_AUTH_WPA */ 165138568Ssam}; 166138568Ssamstatic const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 167138568Ssam 168138568Ssamstatic const struct ieee80211_authenticator auth_internal = { 169138568Ssam .ia_name = "wlan_internal", 170138568Ssam .ia_attach = NULL, 171138568Ssam .ia_detach = NULL, 172138568Ssam .ia_node_join = NULL, 173138568Ssam .ia_node_leave = NULL, 174138568Ssam}; 175138568Ssam 176138568Ssam/* 177138568Ssam * Setup internal authenticators once; they are never unregistered. 178138568Ssam */ 179138568Ssamstatic void 180138568Ssamieee80211_auth_setup(void) 181138568Ssam{ 182138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 183138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 184138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 185138568Ssam} 186138568SsamSYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 187138568Ssam 188138568Ssamconst struct ieee80211_authenticator * 189138568Ssamieee80211_authenticator_get(int auth) 190138568Ssam{ 191138568Ssam if (auth >= IEEE80211_AUTH_MAX) 192138568Ssam return NULL; 193138568Ssam if (authenticators[auth] == NULL) 194138568Ssam ieee80211_load_module(auth_modnames[auth]); 195138568Ssam return authenticators[auth]; 196138568Ssam} 197138568Ssam 198116742Ssamvoid 199138568Ssamieee80211_authenticator_register(int type, 200138568Ssam const struct ieee80211_authenticator *auth) 201116742Ssam{ 202138568Ssam if (type >= IEEE80211_AUTH_MAX) 203138568Ssam return; 204138568Ssam authenticators[type] = auth; 205138568Ssam} 206138568Ssam 207138568Ssamvoid 208138568Ssamieee80211_authenticator_unregister(int type) 209138568Ssam{ 210138568Ssam 211138568Ssam if (type >= IEEE80211_AUTH_MAX) 212138568Ssam return; 213138568Ssam authenticators[type] = NULL; 214138568Ssam} 215138568Ssam 216138568Ssam/* 217138568Ssam * Very simple-minded ACL module support. 218138568Ssam */ 219138568Ssam/* XXX just one for now */ 220138568Ssamstatic const struct ieee80211_aclator *acl = NULL; 221138568Ssam 222138568Ssamvoid 223138568Ssamieee80211_aclator_register(const struct ieee80211_aclator *iac) 224138568Ssam{ 225138568Ssam printf("wlan: %s acl policy registered\n", iac->iac_name); 226138568Ssam acl = iac; 227138568Ssam} 228138568Ssam 229138568Ssamvoid 230138568Ssamieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 231138568Ssam{ 232138568Ssam if (acl == iac) 233138568Ssam acl = NULL; 234138568Ssam printf("wlan: %s acl policy unregistered\n", iac->iac_name); 235138568Ssam} 236138568Ssam 237138568Ssamconst struct ieee80211_aclator * 238138568Ssamieee80211_aclator_get(const char *name) 239138568Ssam{ 240138568Ssam if (acl == NULL) 241138568Ssam ieee80211_load_module("wlan_acl"); 242138568Ssam return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 243138568Ssam} 244138568Ssam 245138568Ssamvoid 246138568Ssamieee80211_print_essid(const u_int8_t *essid, int len) 247138568Ssam{ 248138568Ssam const u_int8_t *p; 249116742Ssam int i; 250116742Ssam 251116742Ssam if (len > IEEE80211_NWID_LEN) 252116742Ssam len = IEEE80211_NWID_LEN; 253116742Ssam /* determine printable or not */ 254116742Ssam for (i = 0, p = essid; i < len; i++, p++) { 255116742Ssam if (*p < ' ' || *p > 0x7e) 256116742Ssam break; 257116742Ssam } 258116742Ssam if (i == len) { 259116742Ssam printf("\""); 260116742Ssam for (i = 0, p = essid; i < len; i++, p++) 261116742Ssam printf("%c", *p); 262116742Ssam printf("\""); 263116742Ssam } else { 264116742Ssam printf("0x"); 265116742Ssam for (i = 0, p = essid; i < len; i++, p++) 266116742Ssam printf("%02x", *p); 267116742Ssam } 268116742Ssam} 269116742Ssam 270116742Ssamvoid 271138568Ssamieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) 272116742Ssam{ 273138568Ssam const struct ieee80211_frame *wh; 274116742Ssam int i; 275116742Ssam 276138568Ssam wh = (const struct ieee80211_frame *)buf; 277116742Ssam switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 278116742Ssam case IEEE80211_FC1_DIR_NODS: 279116742Ssam printf("NODS %s", ether_sprintf(wh->i_addr2)); 280116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 281116742Ssam printf("(%s)", ether_sprintf(wh->i_addr3)); 282116742Ssam break; 283116742Ssam case IEEE80211_FC1_DIR_TODS: 284116742Ssam printf("TODS %s", ether_sprintf(wh->i_addr2)); 285116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 286116742Ssam printf("(%s)", ether_sprintf(wh->i_addr1)); 287116742Ssam break; 288116742Ssam case IEEE80211_FC1_DIR_FROMDS: 289116742Ssam printf("FRDS %s", ether_sprintf(wh->i_addr3)); 290116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 291116742Ssam printf("(%s)", ether_sprintf(wh->i_addr2)); 292116742Ssam break; 293116742Ssam case IEEE80211_FC1_DIR_DSTODS: 294138568Ssam printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1])); 295116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 296116742Ssam printf("(%s", ether_sprintf(wh->i_addr2)); 297116742Ssam printf("->%s)", ether_sprintf(wh->i_addr1)); 298116742Ssam break; 299116742Ssam } 300116742Ssam switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 301116742Ssam case IEEE80211_FC0_TYPE_DATA: 302116742Ssam printf(" data"); 303116742Ssam break; 304116742Ssam case IEEE80211_FC0_TYPE_MGT: 305116742Ssam printf(" %s", ieee80211_mgt_subtype_name[ 306116742Ssam (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 307116742Ssam >> IEEE80211_FC0_SUBTYPE_SHIFT]); 308116742Ssam break; 309116742Ssam default: 310116742Ssam printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 311116742Ssam break; 312116742Ssam } 313138568Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 314138568Ssam int i; 315138568Ssam printf(" WEP [IV"); 316138568Ssam for (i = 0; i < IEEE80211_WEP_IVLEN; i++) 317138568Ssam printf(" %.02x", buf[sizeof(*wh)+i]); 318138568Ssam printf(" KID %u]", buf[sizeof(*wh)+i] >> 6); 319138568Ssam } 320116742Ssam if (rate >= 0) 321116742Ssam printf(" %dM", rate / 2); 322116742Ssam if (rssi >= 0) 323116742Ssam printf(" +%d", rssi); 324116742Ssam printf("\n"); 325116742Ssam if (len > 0) { 326116742Ssam for (i = 0; i < len; i++) { 327116742Ssam if ((i & 1) == 0) 328116742Ssam printf(" "); 329116742Ssam printf("%02x", buf[i]); 330116742Ssam } 331116742Ssam printf("\n"); 332116742Ssam } 333116742Ssam} 334116742Ssam 335165887Ssamstatic __inline int 336165887Ssamfindrix(const struct ieee80211_rateset *rs, int r) 337165887Ssam{ 338165887Ssam int i; 339165887Ssam 340165887Ssam for (i = 0; i < rs->rs_nrates; i++) 341165887Ssam if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) 342165887Ssam return i; 343165887Ssam return -1; 344165887Ssam} 345165887Ssam 346116742Ssamint 347148299Ssamieee80211_fix_rate(struct ieee80211_node *ni, int flags) 348116742Ssam{ 349116742Ssam#define RV(v) ((v) & IEEE80211_RATE_VAL) 350148299Ssam struct ieee80211com *ic = ni->ni_ic; 351165887Ssam int i, j, rix, error; 352138568Ssam int okrate, badrate, fixedrate; 353165569Ssam const struct ieee80211_rateset *srs; 354165569Ssam struct ieee80211_rateset *nrs; 355116742Ssam u_int8_t r; 356116742Ssam 357138568Ssam /* 358138568Ssam * If the fixed rate check was requested but no 359138568Ssam * fixed has been defined then just remove it. 360138568Ssam */ 361148290Ssam if ((flags & IEEE80211_F_DOFRATE) && 362148290Ssam ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) 363138568Ssam flags &= ~IEEE80211_F_DOFRATE; 364116742Ssam error = 0; 365138568Ssam okrate = badrate = fixedrate = 0; 366165569Ssam srs = ieee80211_get_suprates(ic, ni->ni_chan); 367116742Ssam nrs = &ni->ni_rates; 368120482Ssam for (i = 0; i < nrs->rs_nrates; ) { 369116742Ssam if (flags & IEEE80211_F_DOSORT) { 370116742Ssam /* 371116742Ssam * Sort rates. 372116742Ssam */ 373116742Ssam for (j = i + 1; j < nrs->rs_nrates; j++) { 374116742Ssam if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 375116742Ssam r = nrs->rs_rates[i]; 376116742Ssam nrs->rs_rates[i] = nrs->rs_rates[j]; 377116742Ssam nrs->rs_rates[j] = r; 378116742Ssam } 379116742Ssam } 380116742Ssam } 381116742Ssam r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 382116742Ssam badrate = r; 383116742Ssam if (flags & IEEE80211_F_DOFRATE) { 384116742Ssam /* 385138568Ssam * Check any fixed rate is included. 386116742Ssam */ 387138568Ssam if (r == RV(srs->rs_rates[ic->ic_fixed_rate])) 388138568Ssam fixedrate = r; 389116742Ssam } 390165887Ssam /* 391165887Ssam * Check against supported rates. 392165887Ssam */ 393165887Ssam rix = findrix(srs, r); 394116742Ssam if (flags & IEEE80211_F_DONEGO) { 395165887Ssam if (rix < 0) { 396120482Ssam /* 397120482Ssam * A rate in the node's rate set is not 398120482Ssam * supported. If this is a basic rate and we 399165887Ssam * are operating as a STA then this is an error. 400120482Ssam * Otherwise we just discard/ignore the rate. 401120482Ssam */ 402165887Ssam if ((flags & IEEE80211_F_JOIN) && 403120482Ssam (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 404116742Ssam error++; 405165887Ssam } else if ((flags & IEEE80211_F_JOIN) == 0) { 406165887Ssam /* 407165887Ssam * Overwrite with the supported rate 408165887Ssam * value so any basic rate bit is set. 409165887Ssam */ 410165887Ssam nrs->rs_rates[i] = srs->rs_rates[rix]; 411116742Ssam } 412116742Ssam } 413165887Ssam if ((flags & IEEE80211_F_DODEL) && rix < 0) { 414116742Ssam /* 415116742Ssam * Delete unacceptable rates. 416116742Ssam */ 417165887Ssam nrs->rs_nrates--; 418165887Ssam for (j = i; j < nrs->rs_nrates; j++) 419165887Ssam nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 420165887Ssam nrs->rs_rates[j] = 0; 421165887Ssam continue; 422116742Ssam } 423165887Ssam if (rix >= 0) 424116742Ssam okrate = nrs->rs_rates[i]; 425116742Ssam i++; 426116742Ssam } 427138568Ssam if (okrate == 0 || error != 0 || 428138568Ssam ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0)) 429116742Ssam return badrate | IEEE80211_RATE_BASIC; 430116742Ssam else 431116742Ssam return RV(okrate); 432116742Ssam#undef RV 433116742Ssam} 434116742Ssam 435138568Ssam/* 436138568Ssam * Reset 11g-related state. 437138568Ssam */ 438138568Ssamvoid 439138568Ssamieee80211_reset_erp(struct ieee80211com *ic) 440138568Ssam{ 441138568Ssam ic->ic_flags &= ~IEEE80211_F_USEPROT; 442138568Ssam ic->ic_nonerpsta = 0; 443138568Ssam ic->ic_longslotsta = 0; 444138568Ssam /* 445138568Ssam * Short slot time is enabled only when operating in 11g 446138568Ssam * and not in an IBSS. We must also honor whether or not 447138568Ssam * the driver is capable of doing it. 448138568Ssam */ 449138568Ssam ieee80211_set_shortslottime(ic, 450138568Ssam ic->ic_curmode == IEEE80211_MODE_11A || 451138568Ssam (ic->ic_curmode == IEEE80211_MODE_11G && 452138568Ssam ic->ic_opmode == IEEE80211_M_HOSTAP && 453138568Ssam (ic->ic_caps & IEEE80211_C_SHSLOT))); 454138568Ssam /* 455138568Ssam * Set short preamble and ERP barker-preamble flags. 456138568Ssam */ 457138568Ssam if (ic->ic_curmode == IEEE80211_MODE_11A || 458138568Ssam (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 459138568Ssam ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 460138568Ssam ic->ic_flags &= ~IEEE80211_F_USEBARKER; 461138568Ssam } else { 462138568Ssam ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 463138568Ssam ic->ic_flags |= IEEE80211_F_USEBARKER; 464138568Ssam } 465138568Ssam} 466138568Ssam 467138568Ssam/* 468138568Ssam * Set the short slot time state and notify the driver. 469138568Ssam */ 470138568Ssamvoid 471138568Ssamieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 472138568Ssam{ 473138568Ssam if (onoff) 474138568Ssam ic->ic_flags |= IEEE80211_F_SHSLOT; 475138568Ssam else 476138568Ssam ic->ic_flags &= ~IEEE80211_F_SHSLOT; 477138568Ssam /* notify driver */ 478138568Ssam if (ic->ic_updateslot != NULL) 479138568Ssam ic->ic_updateslot(ic->ic_ifp); 480138568Ssam} 481138568Ssam 482138568Ssam/* 483138568Ssam * Check if the specified rate set supports ERP. 484138568Ssam * NB: the rate set is assumed to be sorted. 485138568Ssam */ 486138568Ssamint 487138568Ssamieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) 488138568Ssam{ 489138568Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 490138568Ssam static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 491138568Ssam int i, j; 492138568Ssam 493138568Ssam if (rs->rs_nrates < N(rates)) 494138568Ssam return 0; 495138568Ssam for (i = 0; i < N(rates); i++) { 496138568Ssam for (j = 0; j < rs->rs_nrates; j++) { 497138568Ssam int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 498138568Ssam if (rates[i] == r) 499138568Ssam goto next; 500138568Ssam if (r > rates[i]) 501138568Ssam return 0; 502138568Ssam } 503138568Ssam return 0; 504138568Ssam next: 505138568Ssam ; 506138568Ssam } 507138568Ssam return 1; 508138568Ssam#undef N 509138568Ssam} 510138568Ssam 511138568Ssam/* 512138568Ssam * Mark the basic rates for the 11g rate table based on the 513138568Ssam * operating mode. For real 11g we mark all the 11b rates 514138568Ssam * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 515138568Ssam * 11b rates. There's also a pseudo 11a-mode used to mark only 516138568Ssam * the basic OFDM rates. 517138568Ssam */ 518138568Ssamvoid 519138568Ssamieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) 520138568Ssam{ 521138568Ssam static const struct ieee80211_rateset basic[] = { 522138568Ssam { 0 }, /* IEEE80211_MODE_AUTO */ 523138568Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 524138568Ssam { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ 525138568Ssam { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ 526138568Ssam { 0 }, /* IEEE80211_MODE_FH */ 527138568Ssam /* IEEE80211_MODE_PUREG (not yet) */ 528138568Ssam { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 529138568Ssam }; 530138568Ssam int i, j; 531138568Ssam 532138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 533138568Ssam rs->rs_rates[i] &= IEEE80211_RATE_VAL; 534138568Ssam for (j = 0; j < basic[mode].rs_nrates; j++) 535138568Ssam if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 536138568Ssam rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 537138568Ssam break; 538138568Ssam } 539138568Ssam } 540138568Ssam} 541138568Ssam 542138568Ssam/* 543138568Ssam * WME protocol support. The following parameters come from the spec. 544138568Ssam */ 545138568Ssamtypedef struct phyParamType { 546138568Ssam u_int8_t aifsn; 547138568Ssam u_int8_t logcwmin; 548138568Ssam u_int8_t logcwmax; 549138568Ssam u_int16_t txopLimit; 550138568Ssam u_int8_t acm; 551138568Ssam} paramType; 552138568Ssam 553138568Ssamstatic const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 554138568Ssam { 3, 4, 6 }, /* IEEE80211_MODE_AUTO */ 555138568Ssam { 3, 4, 6 }, /* IEEE80211_MODE_11A */ 556138568Ssam { 3, 5, 7 }, /* IEEE80211_MODE_11B */ 557138568Ssam { 3, 4, 6 }, /* IEEE80211_MODE_11G */ 558138568Ssam { 3, 5, 7 }, /* IEEE80211_MODE_FH */ 559138568Ssam { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_A */ 560138568Ssam { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_G */ 561138568Ssam}; 562138568Ssamstatic const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 563138568Ssam { 7, 4, 10 }, /* IEEE80211_MODE_AUTO */ 564138568Ssam { 7, 4, 10 }, /* IEEE80211_MODE_11A */ 565138568Ssam { 7, 5, 10 }, /* IEEE80211_MODE_11B */ 566138568Ssam { 7, 4, 10 }, /* IEEE80211_MODE_11G */ 567138568Ssam { 7, 5, 10 }, /* IEEE80211_MODE_FH */ 568138568Ssam { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ 569138568Ssam { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ 570138568Ssam}; 571138568Ssamstatic const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 572138568Ssam { 1, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ 573138568Ssam { 1, 3, 4, 94 }, /* IEEE80211_MODE_11A */ 574138568Ssam { 1, 4, 5, 188 }, /* IEEE80211_MODE_11B */ 575138568Ssam { 1, 3, 4, 94 }, /* IEEE80211_MODE_11G */ 576138568Ssam { 1, 4, 5, 188 }, /* IEEE80211_MODE_FH */ 577138568Ssam { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ 578138568Ssam { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ 579138568Ssam}; 580138568Ssamstatic const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 581138568Ssam { 1, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ 582138568Ssam { 1, 2, 3, 47 }, /* IEEE80211_MODE_11A */ 583138568Ssam { 1, 3, 4, 102 }, /* IEEE80211_MODE_11B */ 584138568Ssam { 1, 2, 3, 47 }, /* IEEE80211_MODE_11G */ 585138568Ssam { 1, 3, 4, 102 }, /* IEEE80211_MODE_FH */ 586138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ 587138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ 588138568Ssam}; 589138568Ssam 590138568Ssamstatic const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 591138568Ssam { 3, 4, 10 }, /* IEEE80211_MODE_AUTO */ 592138568Ssam { 3, 4, 10 }, /* IEEE80211_MODE_11A */ 593138568Ssam { 3, 5, 10 }, /* IEEE80211_MODE_11B */ 594138568Ssam { 3, 4, 10 }, /* IEEE80211_MODE_11G */ 595138568Ssam { 3, 5, 10 }, /* IEEE80211_MODE_FH */ 596138568Ssam { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ 597138568Ssam { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ 598138568Ssam}; 599138568Ssamstatic const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 600138568Ssam { 2, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ 601138568Ssam { 2, 3, 4, 94 }, /* IEEE80211_MODE_11A */ 602138568Ssam { 2, 4, 5, 188 }, /* IEEE80211_MODE_11B */ 603138568Ssam { 2, 3, 4, 94 }, /* IEEE80211_MODE_11G */ 604138568Ssam { 2, 4, 5, 188 }, /* IEEE80211_MODE_FH */ 605138568Ssam { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ 606138568Ssam { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ 607138568Ssam}; 608138568Ssamstatic const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 609138568Ssam { 2, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ 610138568Ssam { 2, 2, 3, 47 }, /* IEEE80211_MODE_11A */ 611138568Ssam { 2, 3, 4, 102 }, /* IEEE80211_MODE_11B */ 612138568Ssam { 2, 2, 3, 47 }, /* IEEE80211_MODE_11G */ 613138568Ssam { 2, 3, 4, 102 }, /* IEEE80211_MODE_FH */ 614138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ 615138568Ssam { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ 616138568Ssam}; 617138568Ssam 618138568Ssamvoid 619138568Ssamieee80211_wme_initparams(struct ieee80211com *ic) 620138568Ssam{ 621138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 622138568Ssam const paramType *pPhyParam, *pBssPhyParam; 623138568Ssam struct wmeParams *wmep; 624138568Ssam int i; 625138568Ssam 626138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 627138568Ssam return; 628138568Ssam 629138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 630138568Ssam switch (i) { 631138568Ssam case WME_AC_BK: 632138568Ssam pPhyParam = &phyParamForAC_BK[ic->ic_curmode]; 633138568Ssam pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode]; 634138568Ssam break; 635138568Ssam case WME_AC_VI: 636138568Ssam pPhyParam = &phyParamForAC_VI[ic->ic_curmode]; 637138568Ssam pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode]; 638138568Ssam break; 639138568Ssam case WME_AC_VO: 640138568Ssam pPhyParam = &phyParamForAC_VO[ic->ic_curmode]; 641138568Ssam pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode]; 642138568Ssam break; 643138568Ssam case WME_AC_BE: 644138568Ssam default: 645138568Ssam pPhyParam = &phyParamForAC_BE[ic->ic_curmode]; 646138568Ssam pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode]; 647138568Ssam break; 648138568Ssam } 649138568Ssam 650138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 651138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 652138568Ssam wmep->wmep_acm = pPhyParam->acm; 653138568Ssam wmep->wmep_aifsn = pPhyParam->aifsn; 654138568Ssam wmep->wmep_logcwmin = pPhyParam->logcwmin; 655138568Ssam wmep->wmep_logcwmax = pPhyParam->logcwmax; 656138568Ssam wmep->wmep_txopLimit = pPhyParam->txopLimit; 657138568Ssam } else { 658138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 659138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 660138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 661138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 662138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 663138568Ssam 664138568Ssam } 665138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 666138568Ssam "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " 667138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 668138568Ssam , ieee80211_wme_acnames[i] 669138568Ssam , wmep->wmep_acm 670138568Ssam , wmep->wmep_aifsn 671138568Ssam , wmep->wmep_logcwmin 672138568Ssam , wmep->wmep_logcwmax 673138568Ssam , wmep->wmep_txopLimit 674138568Ssam ); 675138568Ssam 676138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 677138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 678138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 679138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 680138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 681138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 682138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 683138568Ssam "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " 684138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 685138568Ssam , ieee80211_wme_acnames[i] 686138568Ssam , wmep->wmep_acm 687138568Ssam , wmep->wmep_aifsn 688138568Ssam , wmep->wmep_logcwmin 689138568Ssam , wmep->wmep_logcwmax 690138568Ssam , wmep->wmep_txopLimit 691138568Ssam ); 692138568Ssam } 693138568Ssam /* NB: check ic_bss to avoid NULL deref on initial attach */ 694138568Ssam if (ic->ic_bss != NULL) { 695138568Ssam /* 696138568Ssam * Calculate agressive mode switching threshold based 697138568Ssam * on beacon interval. This doesn't need locking since 698138568Ssam * we're only called before entering the RUN state at 699138568Ssam * which point we start sending beacon frames. 700138568Ssam */ 701138568Ssam wme->wme_hipri_switch_thresh = 702138568Ssam (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; 703138568Ssam ieee80211_wme_updateparams(ic); 704138568Ssam } 705138568Ssam} 706138568Ssam 707138568Ssam/* 708138568Ssam * Update WME parameters for ourself and the BSS. 709138568Ssam */ 710138568Ssamvoid 711138568Ssamieee80211_wme_updateparams_locked(struct ieee80211com *ic) 712138568Ssam{ 713138568Ssam static const paramType phyParam[IEEE80211_MODE_MAX] = { 714138568Ssam { 2, 4, 10, 64 }, /* IEEE80211_MODE_AUTO */ 715138568Ssam { 2, 4, 10, 64 }, /* IEEE80211_MODE_11A */ 716138568Ssam { 2, 5, 10, 64 }, /* IEEE80211_MODE_11B */ 717138568Ssam { 2, 4, 10, 64 }, /* IEEE80211_MODE_11G */ 718138568Ssam { 2, 5, 10, 64 }, /* IEEE80211_MODE_FH */ 719138568Ssam { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_A */ 720138568Ssam { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_G */ 721138568Ssam }; 722138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 723138568Ssam const struct wmeParams *wmep; 724138568Ssam struct wmeParams *chanp, *bssp; 725138568Ssam int i; 726138568Ssam 727138568Ssam /* set up the channel access parameters for the physical device */ 728138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 729138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[i]; 730138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 731138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 732138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 733138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 734138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 735138568Ssam 736138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 737138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 738138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 739138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 740138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 741138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 742138568Ssam } 743138568Ssam 744138568Ssam /* 745138568Ssam * This implements agressive mode as found in certain 746138568Ssam * vendors' AP's. When there is significant high 747138568Ssam * priority (VI/VO) traffic in the BSS throttle back BE 748138568Ssam * traffic by using conservative parameters. Otherwise 749138568Ssam * BE uses agressive params to optimize performance of 750138568Ssam * legacy/non-QoS traffic. 751138568Ssam */ 752138568Ssam if ((ic->ic_opmode == IEEE80211_M_HOSTAP && 753156524Ssam (wme->wme_flags & WME_F_AGGRMODE) != 0) || 754153974Ssam (ic->ic_opmode == IEEE80211_M_STA && 755138568Ssam (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 756138568Ssam (ic->ic_flags & IEEE80211_F_WME) == 0) { 757138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 758138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 759138568Ssam 760138568Ssam chanp->wmep_aifsn = bssp->wmep_aifsn = 761138568Ssam phyParam[ic->ic_curmode].aifsn; 762138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 763138568Ssam phyParam[ic->ic_curmode].logcwmin; 764138568Ssam chanp->wmep_logcwmax = bssp->wmep_logcwmax = 765138568Ssam phyParam[ic->ic_curmode].logcwmax; 766138568Ssam chanp->wmep_txopLimit = bssp->wmep_txopLimit = 767153421Ssam (ic->ic_flags & IEEE80211_F_BURST) ? 768138568Ssam phyParam[ic->ic_curmode].txopLimit : 0; 769138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 770138568Ssam "%s: %s [acm %u aifsn %u log2(cwmin) %u " 771138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 772138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 773138568Ssam , chanp->wmep_acm 774138568Ssam , chanp->wmep_aifsn 775138568Ssam , chanp->wmep_logcwmin 776138568Ssam , chanp->wmep_logcwmax 777138568Ssam , chanp->wmep_txopLimit 778138568Ssam ); 779138568Ssam } 780138568Ssam 781138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && 782156524Ssam ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { 783138568Ssam static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = { 784138568Ssam 3, /* IEEE80211_MODE_AUTO */ 785138568Ssam 3, /* IEEE80211_MODE_11A */ 786138568Ssam 4, /* IEEE80211_MODE_11B */ 787138568Ssam 3, /* IEEE80211_MODE_11G */ 788138568Ssam 4, /* IEEE80211_MODE_FH */ 789138568Ssam 3, /* IEEE80211_MODE_TURBO_A */ 790138568Ssam 3, /* IEEE80211_MODE_TURBO_G */ 791138568Ssam }; 792138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 793138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 794138568Ssam 795138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 796138568Ssam logCwMin[ic->ic_curmode]; 797138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 798138568Ssam "%s: %s log2(cwmin) %u\n", __func__ 799138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 800138568Ssam , chanp->wmep_logcwmin 801138568Ssam ); 802138568Ssam } 803138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 804138568Ssam /* 805138568Ssam * Arrange for a beacon update and bump the parameter 806138568Ssam * set number so associated stations load the new values. 807138568Ssam */ 808138568Ssam wme->wme_bssChanParams.cap_info = 809138568Ssam (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 810138568Ssam ic->ic_flags |= IEEE80211_F_WMEUPDATE; 811138568Ssam } 812138568Ssam 813138568Ssam wme->wme_update(ic); 814138568Ssam 815138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 816138568Ssam "%s: WME params updated, cap_info 0x%x\n", __func__, 817138568Ssam ic->ic_opmode == IEEE80211_M_STA ? 818138568Ssam wme->wme_wmeChanParams.cap_info : 819138568Ssam wme->wme_bssChanParams.cap_info); 820138568Ssam} 821138568Ssam 822138568Ssamvoid 823138568Ssamieee80211_wme_updateparams(struct ieee80211com *ic) 824138568Ssam{ 825138568Ssam 826138568Ssam if (ic->ic_caps & IEEE80211_C_WME) { 827138568Ssam IEEE80211_BEACON_LOCK(ic); 828138568Ssam ieee80211_wme_updateparams_locked(ic); 829138568Ssam IEEE80211_BEACON_UNLOCK(ic); 830138568Ssam } 831138568Ssam} 832138568Ssam 833153349Ssamvoid 834153349Ssamieee80211_beacon_miss(struct ieee80211com *ic) 835153349Ssam{ 836153349Ssam 837153349Ssam if (ic->ic_flags & IEEE80211_F_SCAN) { 838153349Ssam /* XXX check ic_curchan != ic_bsschan? */ 839153349Ssam return; 840153349Ssam } 841153349Ssam IEEE80211_DPRINTF(ic, 842153349Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 843153349Ssam "%s\n", "beacon miss"); 844153349Ssam 845153349Ssam /* 846153349Ssam * Our handling is only meaningful for stations that are 847153349Ssam * associated; any other conditions else will be handled 848153349Ssam * through different means (e.g. the tx timeout on mgt frames). 849153349Ssam */ 850153349Ssam if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_RUN) 851153349Ssam return; 852153349Ssam 853153349Ssam if (++ic->ic_bmiss_count < ic->ic_bmiss_max) { 854153349Ssam /* 855153349Ssam * Send a directed probe req before falling back to a scan; 856153349Ssam * if we receive a response ic_bmiss_count will be reset. 857153349Ssam * Some cards mistakenly report beacon miss so this avoids 858153349Ssam * the expensive scan if the ap is still there. 859153349Ssam */ 860153349Ssam ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr, 861153349Ssam ic->ic_bss->ni_bssid, ic->ic_bss->ni_bssid, 862153349Ssam ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen, 863153349Ssam ic->ic_opt_ie, ic->ic_opt_ie_len); 864153349Ssam return; 865153349Ssam } 866153349Ssam ic->ic_bmiss_count = 0; 867153349Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 868153349Ssam} 869153349Ssam 870154736Ssam/* 871154736Ssam * Software beacon miss handling. Check if any beacons 872154736Ssam * were received in the last period. If not post a 873154736Ssam * beacon miss; otherwise reset the counter. 874154736Ssam */ 875147765Ssamstatic void 876154736Ssamieee80211_swbmiss(void *arg) 877154736Ssam{ 878154736Ssam struct ieee80211com *ic = arg; 879154736Ssam 880154736Ssam if (ic->ic_swbmiss_count == 0) { 881154736Ssam ieee80211_beacon_miss(ic); 882154736Ssam if (ic->ic_bmiss_count == 0) /* don't re-arm timer */ 883154736Ssam return; 884154736Ssam } else 885154736Ssam ic->ic_swbmiss_count = 0; 886154736Ssam callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 887154736Ssam ieee80211_swbmiss, ic); 888154736Ssam} 889154736Ssam 890154736Ssamstatic void 891147765Ssamsta_disassoc(void *arg, struct ieee80211_node *ni) 892147765Ssam{ 893147765Ssam struct ieee80211com *ic = arg; 894147765Ssam 895147765Ssam if (ni->ni_associd != 0) { 896147765Ssam IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, 897147765Ssam IEEE80211_REASON_ASSOC_LEAVE); 898147765Ssam ieee80211_node_leave(ic, ni); 899147765Ssam } 900147765Ssam} 901147765Ssam 902147765Ssamstatic void 903147765Ssamsta_deauth(void *arg, struct ieee80211_node *ni) 904147765Ssam{ 905147765Ssam struct ieee80211com *ic = arg; 906147765Ssam 907147765Ssam IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 908147765Ssam IEEE80211_REASON_ASSOC_LEAVE); 909147765Ssam} 910147765Ssam 911117811Ssamstatic int 912138568Ssamieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 913116742Ssam{ 914138568Ssam struct ifnet *ifp = ic->ic_ifp; 915116742Ssam struct ieee80211_node *ni; 916117811Ssam enum ieee80211_state ostate; 917116742Ssam 918116742Ssam ostate = ic->ic_state; 919138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, 920138568Ssam ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 921117811Ssam ic->ic_state = nstate; /* state transition */ 922116742Ssam ni = ic->ic_bss; /* NB: no reference held */ 923154736Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS) 924154736Ssam callout_stop(&ic->ic_swbmiss); 925116742Ssam switch (nstate) { 926116742Ssam case IEEE80211_S_INIT: 927116742Ssam switch (ostate) { 928116742Ssam case IEEE80211_S_INIT: 929116742Ssam break; 930116742Ssam case IEEE80211_S_RUN: 931116742Ssam switch (ic->ic_opmode) { 932116742Ssam case IEEE80211_M_STA: 933116742Ssam IEEE80211_SEND_MGMT(ic, ni, 934116742Ssam IEEE80211_FC0_SUBTYPE_DISASSOC, 935116742Ssam IEEE80211_REASON_ASSOC_LEAVE); 936138568Ssam ieee80211_sta_leave(ic, ni); 937116742Ssam break; 938116742Ssam case IEEE80211_M_HOSTAP: 939147765Ssam ieee80211_iterate_nodes(&ic->ic_sta, 940147765Ssam sta_disassoc, ic); 941116742Ssam break; 942116742Ssam default: 943116742Ssam break; 944116742Ssam } 945165894Ssam break; 946116742Ssam case IEEE80211_S_ASSOC: 947116742Ssam switch (ic->ic_opmode) { 948116742Ssam case IEEE80211_M_STA: 949116742Ssam IEEE80211_SEND_MGMT(ic, ni, 950116742Ssam IEEE80211_FC0_SUBTYPE_DEAUTH, 951116742Ssam IEEE80211_REASON_AUTH_LEAVE); 952116742Ssam break; 953116742Ssam case IEEE80211_M_HOSTAP: 954147765Ssam ieee80211_iterate_nodes(&ic->ic_sta, 955147765Ssam sta_deauth, ic); 956116742Ssam break; 957116742Ssam default: 958116742Ssam break; 959116742Ssam } 960165894Ssam break; 961140441Ssam case IEEE80211_S_SCAN: 962140441Ssam ieee80211_cancel_scan(ic); 963165894Ssam break; 964116742Ssam case IEEE80211_S_AUTH: 965165894Ssam break; 966165894Ssam } 967165894Ssam if (ostate != IEEE80211_S_INIT) { 968165894Ssam /* NB: optimize INIT -> INIT case */ 969116742Ssam ic->ic_mgt_timer = 0; 970165894Ssam ieee80211_drain_ifq(&ic->ic_mgtq); 971138568Ssam ieee80211_reset_bss(ic); 972116742Ssam } 973138568Ssam if (ic->ic_auth->ia_detach != NULL) 974138568Ssam ic->ic_auth->ia_detach(ic); 975116742Ssam break; 976116742Ssam case IEEE80211_S_SCAN: 977116742Ssam switch (ostate) { 978116742Ssam case IEEE80211_S_INIT: 979138568Ssam if ((ic->ic_opmode == IEEE80211_M_HOSTAP || 980138568Ssam ic->ic_opmode == IEEE80211_M_IBSS || 981138568Ssam ic->ic_opmode == IEEE80211_M_AHDEMO) && 982116742Ssam ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 983116742Ssam /* 984116742Ssam * AP operation and we already have a channel; 985116742Ssam * bypass the scan and startup immediately. 986116742Ssam */ 987116742Ssam ieee80211_create_ibss(ic, ic->ic_des_chan); 988116742Ssam } else { 989138568Ssam ieee80211_begin_scan(ic, arg); 990116742Ssam } 991116742Ssam break; 992116742Ssam case IEEE80211_S_SCAN: 993138568Ssam /* 994156358Ssam * Scan next. If doing an active scan probe 995156358Ssam * for the requested ap (if any). 996138568Ssam */ 997156358Ssam if (ic->ic_flags & IEEE80211_F_ASCAN) 998156358Ssam ieee80211_probe_curchan(ic, 0); 999116742Ssam break; 1000116742Ssam case IEEE80211_S_RUN: 1001116742Ssam /* beacon miss */ 1002138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, 1003138568Ssam "no recent beacons from %s; rescanning\n", 1004138568Ssam ether_sprintf(ic->ic_bss->ni_bssid)); 1005138568Ssam ieee80211_sta_leave(ic, ni); 1006138568Ssam ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ 1007116742Ssam /* FALLTHRU */ 1008116742Ssam case IEEE80211_S_AUTH: 1009116742Ssam case IEEE80211_S_ASSOC: 1010116742Ssam /* timeout restart scan */ 1011138568Ssam ni = ieee80211_find_node(&ic->ic_scan, 1012138568Ssam ic->ic_bss->ni_macaddr); 1013116742Ssam if (ni != NULL) { 1014116742Ssam ni->ni_fails++; 1015116742Ssam ieee80211_unref_node(&ni); 1016116742Ssam } 1017147116Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 1018147116Ssam ieee80211_begin_scan(ic, arg); 1019116742Ssam break; 1020116742Ssam } 1021116742Ssam break; 1022116742Ssam case IEEE80211_S_AUTH: 1023116742Ssam switch (ostate) { 1024116742Ssam case IEEE80211_S_INIT: 1025116742Ssam case IEEE80211_S_SCAN: 1026116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1027116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1); 1028116742Ssam break; 1029116742Ssam case IEEE80211_S_AUTH: 1030116742Ssam case IEEE80211_S_ASSOC: 1031138568Ssam switch (arg) { 1032116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 1033116742Ssam /* ??? */ 1034116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1035116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 2); 1036116742Ssam break; 1037116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 1038116742Ssam /* ignore and retry scan on timeout */ 1039116742Ssam break; 1040116742Ssam } 1041116742Ssam break; 1042116742Ssam case IEEE80211_S_RUN: 1043138568Ssam switch (arg) { 1044116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 1045116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1046116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 2); 1047116742Ssam ic->ic_state = ostate; /* stay RUN */ 1048116742Ssam break; 1049116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 1050138568Ssam ieee80211_sta_leave(ic, ni); 1051147116Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 1052147116Ssam /* try to reauth */ 1053147116Ssam IEEE80211_SEND_MGMT(ic, ni, 1054147116Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1); 1055147116Ssam } 1056116742Ssam break; 1057116742Ssam } 1058116742Ssam break; 1059116742Ssam } 1060116742Ssam break; 1061116742Ssam case IEEE80211_S_ASSOC: 1062116742Ssam switch (ostate) { 1063116742Ssam case IEEE80211_S_INIT: 1064116742Ssam case IEEE80211_S_SCAN: 1065116742Ssam case IEEE80211_S_ASSOC: 1066138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 1067138568Ssam "%s: invalid transition\n", __func__); 1068116742Ssam break; 1069116742Ssam case IEEE80211_S_AUTH: 1070116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1071116742Ssam IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 1072116742Ssam break; 1073116742Ssam case IEEE80211_S_RUN: 1074138568Ssam ieee80211_sta_leave(ic, ni); 1075147116Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 1076147116Ssam IEEE80211_SEND_MGMT(ic, ni, 1077147116Ssam IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); 1078147116Ssam } 1079116742Ssam break; 1080116742Ssam } 1081116742Ssam break; 1082116742Ssam case IEEE80211_S_RUN: 1083138568Ssam if (ic->ic_flags & IEEE80211_F_WPA) { 1084138568Ssam /* XXX validate prerequisites */ 1085138568Ssam } 1086116742Ssam switch (ostate) { 1087116742Ssam case IEEE80211_S_INIT: 1088138568Ssam if (ic->ic_opmode == IEEE80211_M_MONITOR) 1089138568Ssam break; 1090138568Ssam /* fall thru... */ 1091116742Ssam case IEEE80211_S_AUTH: 1092138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 1093138568Ssam "%s: invalid transition\n", __func__); 1094140763Ssam /* fall thru... */ 1095140763Ssam case IEEE80211_S_RUN: 1096116742Ssam break; 1097116742Ssam case IEEE80211_S_SCAN: /* adhoc/hostap mode */ 1098116742Ssam case IEEE80211_S_ASSOC: /* infra mode */ 1099116742Ssam KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, 1100116742Ssam ("%s: bogus xmit rate %u setup\n", __func__, 1101116742Ssam ni->ni_txrate)); 1102138568Ssam#ifdef IEEE80211_DEBUG 1103138568Ssam if (ieee80211_msg_debug(ic)) { 1104116742Ssam if (ic->ic_opmode == IEEE80211_M_STA) 1105138568Ssam if_printf(ifp, "associated "); 1106116742Ssam else 1107138568Ssam if_printf(ifp, "synchronized "); 1108116742Ssam printf("with %s ssid ", 1109116742Ssam ether_sprintf(ni->ni_bssid)); 1110116742Ssam ieee80211_print_essid(ic->ic_bss->ni_essid, 1111116742Ssam ni->ni_esslen); 1112116742Ssam printf(" channel %d start %uMb\n", 1113148936Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), 1114116742Ssam IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); 1115116742Ssam } 1116138568Ssam#endif 1117116742Ssam ic->ic_mgt_timer = 0; 1118138568Ssam if (ic->ic_opmode == IEEE80211_M_STA) 1119138568Ssam ieee80211_notify_node_join(ic, ni, 1120138568Ssam arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 1121138568Ssam if_start(ifp); /* XXX not authorized yet */ 1122116742Ssam break; 1123116742Ssam } 1124154736Ssam if (ostate != IEEE80211_S_RUN && 1125154736Ssam ic->ic_opmode == IEEE80211_M_STA && 1126154736Ssam (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) { 1127154736Ssam /* 1128154736Ssam * Start s/w beacon miss timer for devices w/o 1129154736Ssam * hardware support. We fudge a bit here since 1130154736Ssam * we're doing this in software. 1131154736Ssam */ 1132154736Ssam ic->ic_swbmiss_period = IEEE80211_TU_TO_TICKS( 1133154736Ssam 2 * ic->ic_bmissthreshold * ni->ni_intval); 1134154736Ssam ic->ic_swbmiss_count = 0; 1135154736Ssam callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 1136154736Ssam ieee80211_swbmiss, ic); 1137154736Ssam } 1138138568Ssam /* 1139138568Ssam * Start/stop the authenticator when operating as an 1140138568Ssam * AP. We delay until here to allow configuration to 1141138568Ssam * happen out of order. 1142138568Ssam */ 1143138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ 1144138568Ssam ic->ic_auth->ia_attach != NULL) { 1145138568Ssam /* XXX check failure */ 1146138568Ssam ic->ic_auth->ia_attach(ic); 1147138568Ssam } else if (ic->ic_auth->ia_detach != NULL) { 1148138568Ssam ic->ic_auth->ia_detach(ic); 1149138568Ssam } 1150138568Ssam /* 1151138568Ssam * When 802.1x is not in use mark the port authorized 1152138568Ssam * at this point so traffic can flow. 1153138568Ssam */ 1154138568Ssam if (ni->ni_authmode != IEEE80211_AUTH_8021X) 1155148302Ssam ieee80211_node_authorize(ni); 1156138568Ssam /* 1157138568Ssam * Enable inactivity processing. 1158138568Ssam * XXX 1159138568Ssam */ 1160138568Ssam ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT; 1161140753Ssam ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT; 1162116742Ssam break; 1163116742Ssam } 1164116742Ssam return 0; 1165116742Ssam} 1166