ieee80211_sta.c revision 183247
146492Swpaul/*- 246492Swpaul * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting 346492Swpaul * All rights reserved. 446492Swpaul * 546492Swpaul * Redistribution and use in source and binary forms, with or without 646492Swpaul * modification, are permitted provided that the following conditions 746492Swpaul * are met: 846492Swpaul * 1. Redistributions of source code must retain the above copyright 946492Swpaul * notice, this list of conditions and the following disclaimer. 1046492Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1146492Swpaul * notice, this list of conditions and the following disclaimer in the 1246492Swpaul * documentation and/or other materials provided with the distribution. 1346492Swpaul * 1446492Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1546492Swpaul * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1646492Swpaul * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1746492Swpaul * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1846492Swpaul * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1946492Swpaul * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2046492Swpaul * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2146492Swpaul * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2246492Swpaul * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2346492Swpaul * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2446492Swpaul */ 2546492Swpaul 2646492Swpaul#include <sys/cdefs.h> 2746492Swpaul#ifdef __FreeBSD__ 2846492Swpaul__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_sta.c 183247 2008-09-21 23:00:19Z sam $"); 2946492Swpaul#endif 3046492Swpaul 3146492Swpaul/* 3250477Speter * IEEE 802.11 Station mode support. 3346492Swpaul */ 3446492Swpaul#include "opt_inet.h" 3546492Swpaul#include "opt_wlan.h" 3646492Swpaul 3746492Swpaul#include <sys/param.h> 3846492Swpaul#include <sys/systm.h> 3946492Swpaul#include <sys/mbuf.h> 4046492Swpaul#include <sys/malloc.h> 4146492Swpaul#include <sys/kernel.h> 4246492Swpaul 4346492Swpaul#include <sys/socket.h> 4447401Swpaul#include <sys/sockio.h> 4546492Swpaul#include <sys/endian.h> 4646492Swpaul#include <sys/errno.h> 4746492Swpaul#include <sys/proc.h> 4846492Swpaul#include <sys/sysctl.h> 4946492Swpaul 5046492Swpaul#include <net/if.h> 5146492Swpaul#include <net/if_media.h> 5247401Swpaul#include <net/if_llc.h> 5346492Swpaul#include <net/ethernet.h> 5446492Swpaul 5546492Swpaul#include <net/bpf.h> 5646492Swpaul 5746492Swpaul#include <net80211/ieee80211_var.h> 5846492Swpaul#include <net80211/ieee80211_sta.h> 5946492Swpaul#include <net80211/ieee80211_input.h> 6046492Swpaul 6146492Swpaul#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) 6246492Swpaul 6346492Swpaulstatic void sta_vattach(struct ieee80211vap *); 6446492Swpaulstatic void sta_beacon_miss(struct ieee80211vap *); 6546492Swpaulstatic int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int); 6646492Swpaulstatic int sta_input(struct ieee80211_node *, struct mbuf *, 6746492Swpaul int rssi, int noise, uint32_t rstamp); 6846492Swpaulstatic void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *, 6953702Swpaul int subtype, int rssi, int noise, uint32_t rstamp); 7046492Swpaul 7146492Swpaulvoid 7246492Swpaulieee80211_sta_attach(struct ieee80211com *ic) 7346492Swpaul{ 7446492Swpaul ic->ic_vattach[IEEE80211_M_STA] = sta_vattach; 7546492Swpaul} 7661818Sroberto 7761818Srobertovoid 7846492Swpaulieee80211_sta_detach(struct ieee80211com *ic) 7953702Swpaul{ 8053702Swpaul} 8153702Swpaul 8253702Swpaulstatic void 8346492Swpaulsta_vdetach(struct ieee80211vap *vap) 8453702Swpaul{ 8553702Swpaul} 8653702Swpaul 8753702Swpaulstatic void 8853702Swpaulsta_vattach(struct ieee80211vap *vap) 8953702Swpaul{ 9053702Swpaul vap->iv_newstate = sta_newstate; 9146492Swpaul vap->iv_input = sta_input; 9246492Swpaul vap->iv_recv_mgmt = sta_recv_mgmt; 9346492Swpaul vap->iv_opdetach = sta_vdetach; 9446492Swpaul vap->iv_bmiss = sta_beacon_miss; 9546492Swpaul} 9646492Swpaul 9746492Swpaul/* 9846492Swpaul * Handle a beacon miss event. The common code filters out 9946492Swpaul * spurious events that can happen when scanning and/or before 10046492Swpaul * reaching RUN state. 10146492Swpaul */ 10246492Swpaulstatic void 10346492Swpaulsta_beacon_miss(struct ieee80211vap *vap) 10446492Swpaul{ 10546492Swpaul struct ieee80211com *ic = vap->iv_ic; 10653702Swpaul 10746492Swpaul KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning")); 10846492Swpaul KASSERT(vap->iv_state == IEEE80211_S_RUN, 10946492Swpaul ("wrong state %d", vap->iv_state)); 11046492Swpaul 11150477Speter IEEE80211_DPRINTF(vap, 11246492Swpaul IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 11346492Swpaul "beacon miss, mode %u state %s\n", 11446492Swpaul vap->iv_opmode, ieee80211_state_name[vap->iv_state]); 11547401Swpaul 11646492Swpaul if (++vap->iv_bmiss_count < vap->iv_bmiss_max) { 11746492Swpaul /* 11853702Swpaul * Send a directed probe req before falling back to a 11946492Swpaul * scan; if we receive a response ic_bmiss_count will 12046492Swpaul * be reset. Some cards mistakenly report beacon miss 12146492Swpaul * so this avoids the expensive scan if the ap is 12246492Swpaul * still there. 12346492Swpaul */ 12446492Swpaul ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr, 12546492Swpaul vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid, 12646492Swpaul vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen); 12746492Swpaul return; 12846492Swpaul } 12946492Swpaul vap->iv_bmiss_count = 0; 13046492Swpaul vap->iv_stats.is_beacon_miss++; 13146492Swpaul if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { 13246492Swpaul /* 13346492Swpaul * If we receive a beacon miss interrupt when using 13446492Swpaul * dynamic turbo, attempt to switch modes before 13546492Swpaul * reassociating. 13646492Swpaul */ 13746492Swpaul if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP)) 13846492Swpaul ieee80211_dturbo_switch(vap, 13946492Swpaul ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO); 14046492Swpaul /* 14146492Swpaul * Try to reassociate before scanning for a new ap. 14246492Swpaul */ 14353702Swpaul ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1); 14453702Swpaul } else { 14553702Swpaul /* 14653702Swpaul * Somebody else is controlling state changes (e.g. 14753702Swpaul * a user-mode app) don't do anything that would 14853702Swpaul * confuse them; just drop into scan mode so they'll 14953702Swpaul * notified of the state change and given control. 15053702Swpaul */ 15153702Swpaul ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); 15253702Swpaul } 15353702Swpaul} 15453702Swpaul 15553702Swpaul/* 15653702Swpaul * Handle deauth with reason. We retry only for 15753702Swpaul * the cases where we might succeed. Otherwise 15853702Swpaul * we downgrade the ap and scan. 15953702Swpaul */ 16053702Swpaulstatic void 16153702Swpaulsta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason) 16253702Swpaul{ 16353702Swpaul switch (reason) { 16453702Swpaul case IEEE80211_STATUS_SUCCESS: /* NB: MLME assoc */ 16546492Swpaul case IEEE80211_STATUS_TIMEOUT: 16646492Swpaul case IEEE80211_REASON_ASSOC_EXPIRE: 16753702Swpaul case IEEE80211_REASON_NOT_AUTHED: 16846492Swpaul case IEEE80211_REASON_NOT_ASSOCED: 16953702Swpaul case IEEE80211_REASON_ASSOC_LEAVE: 17053702Swpaul case IEEE80211_REASON_ASSOC_NOT_AUTHED: 17146492Swpaul IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); 17246492Swpaul break; 17353702Swpaul default: 17446492Swpaul ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason); 17553702Swpaul if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) 17653702Swpaul ieee80211_check_scan_current(vap); 17753702Swpaul break; 17853702Swpaul } 17946492Swpaul} 18053702Swpaul 18153702Swpaul/* 18246492Swpaul * IEEE80211_M_STA vap state machine handler. 18353702Swpaul * This routine handles the main states in the 802.11 protocol. 18446492Swpaul */ 18546492Swpaulstatic int 18653702Swpaulsta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 18753702Swpaul{ 18853702Swpaul struct ieee80211com *ic = vap->iv_ic; 18953702Swpaul struct ieee80211_node *ni; 19053702Swpaul enum ieee80211_state ostate; 19153702Swpaul 19253702Swpaul IEEE80211_LOCK_ASSERT(ic); 19346492Swpaul 19446492Swpaul ostate = vap->iv_state; 19546492Swpaul IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 19646492Swpaul __func__, ieee80211_state_name[ostate], 19753702Swpaul ieee80211_state_name[nstate], arg); 19846492Swpaul vap->iv_state = nstate; /* state transition */ 19946492Swpaul callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */ 20053702Swpaul if (ostate != IEEE80211_S_SCAN) 20153702Swpaul ieee80211_cancel_scan(vap); /* background scan */ 20246492Swpaul ni = vap->iv_bss; /* NB: no reference held */ 20346492Swpaul if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) 20446492Swpaul callout_stop(&vap->iv_swbmiss); 20546492Swpaul switch (nstate) { 20653702Swpaul case IEEE80211_S_INIT: 20767092Swpaul switch (ostate) { 20846492Swpaul case IEEE80211_S_SLEEP: 20946492Swpaul /* XXX wakeup */ 21046492Swpaul case IEEE80211_S_RUN: 21153702Swpaul IEEE80211_SEND_MGMT(ni, 21267092Swpaul IEEE80211_FC0_SUBTYPE_DISASSOC, 21353702Swpaul IEEE80211_REASON_ASSOC_LEAVE); 21446492Swpaul ieee80211_sta_leave(ni); 21546492Swpaul break; 21653702Swpaul case IEEE80211_S_ASSOC: 21758274Srwatson IEEE80211_SEND_MGMT(ni, 21863090Sarchie IEEE80211_FC0_SUBTYPE_DEAUTH, 21954277Swpaul IEEE80211_REASON_AUTH_LEAVE); 22053702Swpaul break; 22146492Swpaul case IEEE80211_S_SCAN: 22246492Swpaul ieee80211_cancel_scan(vap); 22353702Swpaul break; 22467092Swpaul default: 22567092Swpaul goto invalid; 22646492Swpaul } 22746492Swpaul if (ostate != IEEE80211_S_INIT) { 22846492Swpaul /* NB: optimize INIT -> INIT case */ 22946492Swpaul ieee80211_reset_bss(vap); 23053702Swpaul } 23146492Swpaul if (vap->iv_auth->ia_detach != NULL) 23246492Swpaul vap->iv_auth->ia_detach(vap); 23346492Swpaul break; 23446563Swpaul case IEEE80211_S_SCAN: 23546492Swpaul switch (ostate) { 23653702Swpaul case IEEE80211_S_INIT: 23746492Swpaul /* 23853702Swpaul * Initiate a scan. We can come here as a result 23946492Swpaul * of an IEEE80211_IOC_SCAN_REQ too in which case 24046492Swpaul * the vap will be marked with IEEE80211_FEXT_SCANREQ 24153702Swpaul * and the scan request parameters will be present 24253702Swpaul * in iv_scanreq. Otherwise we do the default. 24353702Swpaul */ 24453702Swpaul if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 24553702Swpaul ieee80211_check_scan(vap, 24653702Swpaul vap->iv_scanreq_flags, 24753702Swpaul vap->iv_scanreq_duration, 24853702Swpaul vap->iv_scanreq_mindwell, 24953702Swpaul vap->iv_scanreq_maxdwell, 25053702Swpaul vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 25153702Swpaul vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 25253702Swpaul } else 25353702Swpaul ieee80211_check_scan_current(vap); 25453702Swpaul break; 25553702Swpaul case IEEE80211_S_SCAN: 25667092Swpaul case IEEE80211_S_AUTH: 25767092Swpaul case IEEE80211_S_ASSOC: 25867092Swpaul /* 25946492Swpaul * These can happen either because of a timeout 26046492Swpaul * on an assoc/auth response or because of a 26146492Swpaul * change in state that requires a reset. For 26246492Swpaul * the former we're called with a non-zero arg 26346492Swpaul * that is the cause for the failure; pass this 26446492Swpaul * to the scan code so it can update state. 26546492Swpaul * Otherwise trigger a new scan unless we're in 26646492Swpaul * manual roaming mode in which case an application 26746492Swpaul * must issue an explicit scan request. 26846492Swpaul */ 26953702Swpaul if (arg != 0) 27046492Swpaul ieee80211_scan_assoc_fail(vap, 27146492Swpaul vap->iv_bss->ni_macaddr, arg); 27246492Swpaul if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) 27346492Swpaul ieee80211_check_scan_current(vap); 27446492Swpaul break; 27546492Swpaul case IEEE80211_S_RUN: /* beacon miss */ 27646492Swpaul /* 27746492Swpaul * Beacon miss. Notify user space and if not 27846492Swpaul * under control of a user application (roaming 27946492Swpaul * manual) kick off a scan to re-connect. 28046492Swpaul */ 28146492Swpaul ieee80211_sta_leave(ni); 28246492Swpaul if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) 28346492Swpaul ieee80211_check_scan_current(vap); 28446492Swpaul break; 28546492Swpaul default: 28646492Swpaul goto invalid; 28746492Swpaul } 28846492Swpaul break; 28946492Swpaul case IEEE80211_S_AUTH: 29046492Swpaul switch (ostate) { 29146492Swpaul case IEEE80211_S_INIT: 29246492Swpaul case IEEE80211_S_SCAN: 29346492Swpaul IEEE80211_SEND_MGMT(ni, 29446492Swpaul IEEE80211_FC0_SUBTYPE_AUTH, 1); 29546492Swpaul break; 29646492Swpaul case IEEE80211_S_AUTH: 29746492Swpaul case IEEE80211_S_ASSOC: 29846492Swpaul switch (arg & 0xff) { 29946492Swpaul case IEEE80211_FC0_SUBTYPE_AUTH: 30046492Swpaul /* ??? */ 30146492Swpaul IEEE80211_SEND_MGMT(ni, 30246492Swpaul IEEE80211_FC0_SUBTYPE_AUTH, 2); 30346492Swpaul break; 30446611Swpaul case IEEE80211_FC0_SUBTYPE_DEAUTH: 30546611Swpaul sta_authretry(vap, ni, arg>>8); 30646492Swpaul break; 30746563Swpaul } 30846563Swpaul break; 30946563Swpaul case IEEE80211_S_RUN: 31046563Swpaul switch (arg & 0xff) { 31146563Swpaul case IEEE80211_FC0_SUBTYPE_AUTH: 31246563Swpaul IEEE80211_SEND_MGMT(ni, 31346563Swpaul IEEE80211_FC0_SUBTYPE_AUTH, 2); 31446563Swpaul vap->iv_state = ostate; /* stay RUN */ 31546563Swpaul break; 31646563Swpaul case IEEE80211_FC0_SUBTYPE_DEAUTH: 31746563Swpaul ieee80211_sta_leave(ni); 31856965Swpaul if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { 31956965Swpaul /* try to reauth */ 32056965Swpaul IEEE80211_SEND_MGMT(ni, 32156965Swpaul IEEE80211_FC0_SUBTYPE_AUTH, 1); 32256965Swpaul } 32356965Swpaul break; 32456965Swpaul } 32556965Swpaul break; 32646492Swpaul default: 32746492Swpaul goto invalid; 32846492Swpaul } 32946492Swpaul break; 33046492Swpaul case IEEE80211_S_ASSOC: 33146492Swpaul switch (ostate) { 33263090Sarchie case IEEE80211_S_AUTH: 33346492Swpaul case IEEE80211_S_ASSOC: 33463090Sarchie IEEE80211_SEND_MGMT(ni, 33553702Swpaul IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 33667092Swpaul break; 33746492Swpaul case IEEE80211_S_SLEEP: /* cannot happen */ 33846492Swpaul case IEEE80211_S_RUN: 33946492Swpaul ieee80211_sta_leave(ni); 34046492Swpaul if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { 34146492Swpaul IEEE80211_SEND_MGMT(ni, arg ? 34246492Swpaul IEEE80211_FC0_SUBTYPE_REASSOC_REQ : 34346492Swpaul IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 34446492Swpaul } 34546492Swpaul break; 34646492Swpaul default: 34746492Swpaul goto invalid; 34846492Swpaul } 34946492Swpaul break; 35046492Swpaul case IEEE80211_S_RUN: 35146492Swpaul if (vap->iv_flags & IEEE80211_F_WPA) { 35246492Swpaul /* XXX validate prerequisites */ 35346492Swpaul } 35446492Swpaul switch (ostate) { 35546492Swpaul case IEEE80211_S_RUN: 35646492Swpaul break; 35746492Swpaul case IEEE80211_S_AUTH: /* when join is done in fw */ 35846492Swpaul case IEEE80211_S_ASSOC: 35946492Swpaul#ifdef IEEE80211_DEBUG 36046492Swpaul if (ieee80211_msg_debug(vap)) { 36146492Swpaul ieee80211_note(vap, "%s with %s ssid ", 36246492Swpaul (vap->iv_opmode == IEEE80211_M_STA ? 36346492Swpaul "associated" : "synchronized"), 36446492Swpaul ether_sprintf(ni->ni_bssid)); 36546492Swpaul ieee80211_print_essid(vap->iv_bss->ni_essid, 36646492Swpaul ni->ni_esslen); 36746492Swpaul /* XXX MCS/HT */ 36846492Swpaul printf(" channel %d start %uMb\n", 36946492Swpaul ieee80211_chan2ieee(ic, ic->ic_curchan), 37046492Swpaul IEEE80211_RATE2MBS(ni->ni_txrate)); 37146492Swpaul } 37246492Swpaul#endif 37346492Swpaul ieee80211_scan_assoc_success(vap, ni->ni_macaddr); 37446492Swpaul ieee80211_notify_node_join(ni, 37546492Swpaul arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 37646492Swpaul break; 37746492Swpaul case IEEE80211_S_SLEEP: 37846492Swpaul ieee80211_sta_pwrsave(vap, 0); 37946492Swpaul break; 38046492Swpaul default: 38146492Swpaul goto invalid; 38246492Swpaul } 38348553Swpaul ieee80211_sync_curchan(ic); 38453702Swpaul if (ostate != IEEE80211_S_RUN && 38553702Swpaul (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) { 38648553Swpaul /* 38748553Swpaul * Start s/w beacon miss timer for devices w/o 38848553Swpaul * hardware support. We fudge a bit here since 38948553Swpaul * we're doing this in software. 39048553Swpaul */ 39146492Swpaul vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( 39246492Swpaul 2 * vap->iv_bmissthreshold * ni->ni_intval); 39346492Swpaul vap->iv_swbmiss_count = 0; 39446492Swpaul callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, 39546492Swpaul ieee80211_swbmiss, vap); 39659328Swpaul } 39759328Swpaul /* 39859328Swpaul * When 802.1x is not in use mark the port authorized 39959328Swpaul * at this point so traffic can flow. 40059328Swpaul */ 40159328Swpaul if (ni->ni_authmode != IEEE80211_AUTH_8021X) 40259328Swpaul ieee80211_node_authorize(ni); 40346492Swpaul break; 40446492Swpaul case IEEE80211_S_SLEEP: 40546492Swpaul ieee80211_sta_pwrsave(vap, 0); 40646492Swpaul break; 40746492Swpaul default: 40846492Swpaul invalid: 40946492Swpaul IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, 41046492Swpaul "%s: invalid state transition %s -> %s\n", __func__, 41146492Swpaul ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 41246492Swpaul break; 41346492Swpaul } 41448553Swpaul return 0; 41548553Swpaul} 41653702Swpaul 41753702Swpaul/* 41848553Swpaul * Return non-zero if the frame is an echo of a multicast 41948553Swpaul * frame sent by ourself. The dir is known to be DSTODS. 42048553Swpaul */ 42148553Swpaulstatic __inline int 42248553Swpaulisdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh) 42346492Swpaul{ 42446492Swpaul#define QWH4(wh) ((const struct ieee80211_qosframe_addr4 *)wh) 42546492Swpaul#define WH4(wh) ((const struct ieee80211_frame_addr4 *)wh) 42646492Swpaul const uint8_t *sa; 42746492Swpaul 42846492Swpaul KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode")); 42946492Swpaul 43046492Swpaul if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) 43146492Swpaul return 0; 43246492Swpaul sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4; 43346492Swpaul return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr); 43446492Swpaul#undef WH4 43546492Swpaul#undef QWH4 43646492Swpaul} 43746492Swpaul 43853702Swpaul/* 43953702Swpaul * Return non-zero if the frame is an echo of a multicast 44053702Swpaul * frame sent by ourself. The dir is known to be FROMDS. 44146492Swpaul */ 44246492Swpaulstatic __inline int 44346492Swpaulisfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh) 44446492Swpaul{ 44546492Swpaul KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode")); 44646492Swpaul 44746492Swpaul if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) 44846492Swpaul return 0; 44946492Swpaul return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr); 45046492Swpaul} 45146492Swpaul 45246492Swpaul/* 45346492Swpaul * Decide if a received management frame should be 45446492Swpaul * printed when debugging is enabled. This filters some 45546492Swpaul * of the less interesting frames that come frequently 45646492Swpaul * (e.g. beacons). 45746492Swpaul */ 45846492Swpaulstatic __inline int 45946492Swpauldoprint(struct ieee80211vap *vap, int subtype) 46046492Swpaul{ 46146492Swpaul switch (subtype) { 46246492Swpaul case IEEE80211_FC0_SUBTYPE_BEACON: 46346492Swpaul return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); 46446492Swpaul case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 46546492Swpaul return 0; 46646492Swpaul } 46746492Swpaul return 1; 46846492Swpaul} 46946492Swpaul 47046492Swpaul/* 47146492Swpaul * Process a received frame. The node associated with the sender 47246492Swpaul * should be supplied. If nothing was found in the node table then 47346492Swpaul * the caller is assumed to supply a reference to iv_bss instead. 47446492Swpaul * The RSSI and a timestamp are also supplied. The RSSI data is used 47546492Swpaul * during AP scanning to select a AP to associate with; it can have 47646492Swpaul * any units so long as values have consistent units and higher values 47746492Swpaul * mean ``better signal''. The receive timestamp is currently not used 47846492Swpaul * by the 802.11 layer. 47946492Swpaul */ 48046492Swpaulstatic int 48146492Swpaulsta_input(struct ieee80211_node *ni, struct mbuf *m, 48246492Swpaul int rssi, int noise, uint32_t rstamp) 48346492Swpaul{ 48446492Swpaul#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) 48546492Swpaul#define HAS_SEQ(type) ((type & 0x4) == 0) 48646492Swpaul struct ieee80211vap *vap = ni->ni_vap; 48746492Swpaul struct ieee80211com *ic = ni->ni_ic; 48846492Swpaul struct ifnet *ifp = vap->iv_ifp; 48946492Swpaul struct ieee80211_frame *wh; 49046492Swpaul struct ieee80211_key *key; 49146492Swpaul struct ether_header *eh; 49246492Swpaul int hdrspace, need_tap; 49346492Swpaul uint8_t dir, type, subtype, qos; 49446492Swpaul uint8_t *bssid; 49546492Swpaul uint16_t rxseq; 49646492Swpaul 49746492Swpaul if (m->m_flags & M_AMPDU_MPDU) { 49846492Swpaul /* 49946492Swpaul * Fastpath for A-MPDU reorder q resubmission. Frames 50046492Swpaul * w/ M_AMPDU_MPDU marked have already passed through 50146492Swpaul * here but were received out of order and been held on 50246492Swpaul * the reorder queue. When resubmitted they are marked 50346492Swpaul * with the M_AMPDU_MPDU flag and we can bypass most of 50446492Swpaul * the normal processing. 50546492Swpaul */ 50646492Swpaul wh = mtod(m, struct ieee80211_frame *); 50746492Swpaul type = IEEE80211_FC0_TYPE_DATA; 50846492Swpaul dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 50946492Swpaul subtype = IEEE80211_FC0_SUBTYPE_QOS; 51046492Swpaul hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ 51146492Swpaul goto resubmit_ampdu; 51246492Swpaul } 51346492Swpaul 51446492Swpaul KASSERT(ni != NULL, ("null node")); 51546492Swpaul ni->ni_inact = ni->ni_inact_reload; 51646492Swpaul 51746492Swpaul need_tap = 1; /* mbuf need to be tapped. */ 51846492Swpaul type = -1; /* undefined */ 51946492Swpaul 52046492Swpaul if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { 52153702Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 52253702Swpaul ni->ni_macaddr, NULL, 52346492Swpaul "too short (1): len %u", m->m_pkthdr.len); 52453702Swpaul vap->iv_stats.is_rx_tooshort++; 52546492Swpaul goto out; 52646492Swpaul } 52746492Swpaul /* 52867092Swpaul * Bit of a cheat here, we use a pointer for a 3-address 52967092Swpaul * frame format but don't reference fields past outside 53046492Swpaul * ieee80211_frame_min w/o first validating the data is 53146492Swpaul * present. 53246492Swpaul */ 53346492Swpaul wh = mtod(m, struct ieee80211_frame *); 53446492Swpaul 53567092Swpaul if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 53646492Swpaul IEEE80211_FC0_VERSION_0) { 53746492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 53846492Swpaul ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); 53946492Swpaul vap->iv_stats.is_rx_badversion++; 54046492Swpaul goto err; 54146492Swpaul } 54246492Swpaul 54346492Swpaul dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 54446492Swpaul type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 54546492Swpaul subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 54646492Swpaul if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 54746492Swpaul bssid = wh->i_addr2; 54846492Swpaul if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { 54946492Swpaul /* not interested in */ 55046492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 55146492Swpaul bssid, NULL, "%s", "not to bss"); 55246492Swpaul vap->iv_stats.is_rx_wrongbss++; 55346492Swpaul goto out; 55446492Swpaul } 55546492Swpaul IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 55646492Swpaul ni->ni_noise = noise; 55746492Swpaul ni->ni_rstamp = rstamp; 55846492Swpaul if (HAS_SEQ(type)) { 55946492Swpaul uint8_t tid = ieee80211_gettid(wh); 56046492Swpaul if (IEEE80211_QOS_HAS_SEQ(wh) && 56146492Swpaul TID_TO_WME_AC(tid) >= WME_AC_VI) 56246492Swpaul ic->ic_wme.wme_hipri_traffic++; 56346492Swpaul rxseq = le16toh(*(uint16_t *)wh->i_seq); 56446492Swpaul if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 && 56546492Swpaul (wh->i_fc[1] & IEEE80211_FC1_RETRY) && 56646492Swpaul SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { 56746492Swpaul /* duplicate, discard */ 56846492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 56946492Swpaul bssid, "duplicate", 57046492Swpaul "seqno <%u,%u> fragno <%u,%u> tid %u", 57146492Swpaul rxseq >> IEEE80211_SEQ_SEQ_SHIFT, 57246492Swpaul ni->ni_rxseqs[tid] >> 57346492Swpaul IEEE80211_SEQ_SEQ_SHIFT, 57446492Swpaul rxseq & IEEE80211_SEQ_FRAG_MASK, 57546492Swpaul ni->ni_rxseqs[tid] & 57646492Swpaul IEEE80211_SEQ_FRAG_MASK, 57746492Swpaul tid); 57846492Swpaul vap->iv_stats.is_rx_dup++; 57946492Swpaul IEEE80211_NODE_STAT(ni, rx_dup); 58046492Swpaul goto out; 58146492Swpaul } 58246492Swpaul ni->ni_rxseqs[tid] = rxseq; 58367092Swpaul } 58467092Swpaul } 58546492Swpaul 58646492Swpaul switch (type) { 58746492Swpaul case IEEE80211_FC0_TYPE_DATA: 58846492Swpaul hdrspace = ieee80211_hdrspace(ic, wh); 58946492Swpaul if (m->m_len < hdrspace && 59046492Swpaul (m = m_pullup(m, hdrspace)) == NULL) { 59146492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 59246492Swpaul ni->ni_macaddr, NULL, 59346492Swpaul "data too short: expecting %u", hdrspace); 59446492Swpaul vap->iv_stats.is_rx_tooshort++; 59546492Swpaul goto out; /* XXX */ 59646492Swpaul } 59746492Swpaul /* 59846492Swpaul * Handle A-MPDU re-ordering. If the frame is to be 59946492Swpaul * processed directly then ieee80211_ampdu_reorder 60046492Swpaul * will return 0; otherwise it has consumed the mbuf 60146492Swpaul * and we should do nothing more with it. 60246492Swpaul */ 60346492Swpaul if ((m->m_flags & M_AMPDU) && 60446492Swpaul (dir == IEEE80211_FC1_DIR_FROMDS || 60546492Swpaul dir == IEEE80211_FC1_DIR_DSTODS) && 60646492Swpaul ieee80211_ampdu_reorder(ni, m) != 0) { 60746492Swpaul m = NULL; 60846492Swpaul goto out; 60946492Swpaul } 61046492Swpaul resubmit_ampdu: 61146492Swpaul if (dir == IEEE80211_FC1_DIR_FROMDS) { 61246492Swpaul if ((ifp->if_flags & IFF_SIMPLEX) && 61346492Swpaul isfromds_mcastecho(vap, wh)) { 61446492Swpaul /* 61546492Swpaul * In IEEE802.11 network, multicast 61646492Swpaul * packets sent from "me" are broadcast 61746492Swpaul * from the AP; silently discard for 61846492Swpaul * SIMPLEX interface. 61946492Swpaul */ 62046492Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 62146492Swpaul wh, "data", "%s", "multicast echo"); 62246492Swpaul vap->iv_stats.is_rx_mcastecho++; 62346492Swpaul goto out; 62446492Swpaul } 62546492Swpaul if ((vap->iv_flags & IEEE80211_F_DWDS) && 62646492Swpaul IEEE80211_IS_MULTICAST(wh->i_addr1)) { 62753702Swpaul /* 62853702Swpaul * DWDS sta's must drop 3-address mcast frames 62953702Swpaul * as they will be sent separately as a 4-addr 63053702Swpaul * frame. Accepting the 3-addr frame will 63153702Swpaul * confuse the bridge into thinking the sending 63246492Swpaul * sta is located at the end of WDS link. 63353702Swpaul */ 63446492Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, 63546492Swpaul "3-address data", "%s", "DWDS enabled"); 63646492Swpaul vap->iv_stats.is_rx_mcastecho++; 63746492Swpaul goto out; 63846492Swpaul } 63953702Swpaul } else if (dir == IEEE80211_FC1_DIR_DSTODS) { 64046492Swpaul if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) { 64146492Swpaul IEEE80211_DISCARD(vap, 64246492Swpaul IEEE80211_MSG_INPUT, wh, "4-address data", 64346492Swpaul "%s", "DWDS not enabled"); 64446492Swpaul vap->iv_stats.is_rx_wrongdir++; 64546492Swpaul goto out; 64646492Swpaul } 64746492Swpaul if ((ifp->if_flags & IFF_SIMPLEX) && 64846492Swpaul isdstods_mcastecho(vap, wh)) { 64946492Swpaul /* 65046492Swpaul * In IEEE802.11 network, multicast 65146492Swpaul * packets sent from "me" are broadcast 65246492Swpaul * from the AP; silently discard for 65346492Swpaul * SIMPLEX interface. 65446492Swpaul */ 65546492Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, 65646492Swpaul "4-address data", "%s", "multicast echo"); 65747789Swpaul vap->iv_stats.is_rx_mcastecho++; 65847789Swpaul goto out; 65947789Swpaul } 66046492Swpaul } else { 66146492Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, 66246492Swpaul "data", "incorrect dir 0x%x", dir); 66346492Swpaul vap->iv_stats.is_rx_wrongdir++; 66447401Swpaul goto out; 66546492Swpaul } 66646492Swpaul 66746492Swpaul /* 66846492Swpaul * Handle privacy requirements. Note that we 66946492Swpaul * must not be preempted from here until after 67046492Swpaul * we (potentially) call ieee80211_crypto_demic; 67146492Swpaul * otherwise we may violate assumptions in the 67246492Swpaul * crypto cipher modules used to do delayed update 67346492Swpaul * of replay sequence numbers. 67446492Swpaul */ 67546492Swpaul if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 67646492Swpaul if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 67746492Swpaul /* 67846492Swpaul * Discard encrypted frames when privacy is off. 67946492Swpaul */ 68046492Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 68146492Swpaul wh, "WEP", "%s", "PRIVACY off"); 68246492Swpaul vap->iv_stats.is_rx_noprivacy++; 68346492Swpaul IEEE80211_NODE_STAT(ni, rx_noprivacy); 68446492Swpaul goto out; 68546492Swpaul } 68646492Swpaul key = ieee80211_crypto_decap(ni, m, hdrspace); 68746492Swpaul if (key == NULL) { 68846492Swpaul /* NB: stats+msgs handled in crypto_decap */ 68946492Swpaul IEEE80211_NODE_STAT(ni, rx_wepfail); 69046492Swpaul goto out; 69146492Swpaul } 69246492Swpaul wh = mtod(m, struct ieee80211_frame *); 69346492Swpaul wh->i_fc[1] &= ~IEEE80211_FC1_WEP; 69447789Swpaul } else { 69547789Swpaul /* XXX M_WEP and IEEE80211_F_PRIVACY */ 69646492Swpaul key = NULL; 69746492Swpaul } 69846492Swpaul 69946492Swpaul /* 70046492Swpaul * Save QoS bits for use below--before we strip the header. 70146492Swpaul */ 70246492Swpaul if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { 70346492Swpaul qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? 70446492Swpaul ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : 70546492Swpaul ((struct ieee80211_qosframe *)wh)->i_qos[0]; 70646492Swpaul } else 70746492Swpaul qos = 0; 70846492Swpaul 70946492Swpaul /* 71046492Swpaul * Next up, any fragmentation. 71146492Swpaul */ 71246492Swpaul if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 71346492Swpaul m = ieee80211_defrag(ni, m, hdrspace); 71446492Swpaul if (m == NULL) { 71546492Swpaul /* Fragment dropped or frame not complete yet */ 71646492Swpaul goto out; 71746492Swpaul } 71846492Swpaul } 71946492Swpaul wh = NULL; /* no longer valid, catch any uses */ 72046492Swpaul 72146492Swpaul /* 72246492Swpaul * Next strip any MSDU crypto bits. 72346492Swpaul */ 72446492Swpaul if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) { 72546492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 72646492Swpaul ni->ni_macaddr, "data", "%s", "demic error"); 72753702Swpaul vap->iv_stats.is_rx_demicfail++; 72846492Swpaul IEEE80211_NODE_STAT(ni, rx_demicfail); 72946492Swpaul goto out; 73046492Swpaul } 73146492Swpaul 73246492Swpaul /* copy to listener after decrypt */ 73346492Swpaul if (bpf_peers_present(vap->iv_rawbpf)) 73446492Swpaul bpf_mtap(vap->iv_rawbpf, m); 73546492Swpaul need_tap = 0; 73646492Swpaul 73746492Swpaul /* 73846492Swpaul * Finally, strip the 802.11 header. 73946492Swpaul */ 74046492Swpaul m = ieee80211_decap(vap, m, hdrspace); 74146492Swpaul if (m == NULL) { 74246492Swpaul /* XXX mask bit to check for both */ 74346492Swpaul /* don't count Null data frames as errors */ 74446492Swpaul if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || 74546492Swpaul subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) 74646492Swpaul goto out; 74746492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 74846492Swpaul ni->ni_macaddr, "data", "%s", "decap error"); 74946492Swpaul vap->iv_stats.is_rx_decap++; 75046492Swpaul IEEE80211_NODE_STAT(ni, rx_decap); 75146492Swpaul goto err; 75246492Swpaul } 75346492Swpaul eh = mtod(m, struct ether_header *); 75446492Swpaul if (!ieee80211_node_is_authorized(ni)) { 75546492Swpaul /* 75646492Swpaul * Deny any non-PAE frames received prior to 75746492Swpaul * authorization. For open/shared-key 75846492Swpaul * authentication the port is mark authorized 75946492Swpaul * after authentication completes. For 802.1x 76046492Swpaul * the port is not marked authorized by the 76146492Swpaul * authenticator until the handshake has completed. 76246492Swpaul */ 76346492Swpaul if (eh->ether_type != htons(ETHERTYPE_PAE)) { 76446492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 76546492Swpaul eh->ether_shost, "data", 76646492Swpaul "unauthorized port: ether type 0x%x len %u", 76746492Swpaul eh->ether_type, m->m_pkthdr.len); 76846492Swpaul vap->iv_stats.is_rx_unauth++; 76946492Swpaul IEEE80211_NODE_STAT(ni, rx_unauth); 77046492Swpaul goto err; 77146492Swpaul } 77246492Swpaul } else { 77346492Swpaul /* 77446492Swpaul * When denying unencrypted frames, discard 77546492Swpaul * any non-PAE frames received without encryption. 77646492Swpaul */ 77746492Swpaul if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && 77846492Swpaul (key == NULL && (m->m_flags & M_WEP) == 0) && 77946492Swpaul eh->ether_type != htons(ETHERTYPE_PAE)) { 78046492Swpaul /* 78146492Swpaul * Drop unencrypted frames. 78246492Swpaul */ 78346492Swpaul vap->iv_stats.is_rx_unencrypted++; 78446492Swpaul IEEE80211_NODE_STAT(ni, rx_unencrypted); 78546492Swpaul goto out; 78646492Swpaul } 78746492Swpaul } 78846492Swpaul /* XXX require HT? */ 78946492Swpaul if (qos & IEEE80211_QOS_AMSDU) { 79046492Swpaul m = ieee80211_decap_amsdu(ni, m); 79146492Swpaul if (m == NULL) 79246492Swpaul return IEEE80211_FC0_TYPE_DATA; 79346492Swpaul } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) && 79446492Swpaul#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) 79546492Swpaul m->m_pkthdr.len >= 3*FF_LLC_SIZE) { 79646492Swpaul struct llc *llc; 79746492Swpaul 79846492Swpaul /* 79946492Swpaul * Check for fast-frame tunnel encapsulation. 80046492Swpaul */ 80146492Swpaul if (m->m_len < FF_LLC_SIZE && 80246492Swpaul (m = m_pullup(m, FF_LLC_SIZE)) == NULL) { 80346492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 80446492Swpaul ni->ni_macaddr, "fast-frame", 80546492Swpaul "%s", "m_pullup(llc) failed"); 80646492Swpaul vap->iv_stats.is_rx_tooshort++; 80746492Swpaul return IEEE80211_FC0_TYPE_DATA; 80846492Swpaul } 80946492Swpaul llc = (struct llc *)(mtod(m, uint8_t *) + 81046492Swpaul sizeof(struct ether_header)); 81146492Swpaul if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) { 81246492Swpaul m_adj(m, FF_LLC_SIZE); 81346492Swpaul m = ieee80211_decap_fastframe(ni, m); 81446492Swpaul if (m == NULL) 81546492Swpaul return IEEE80211_FC0_TYPE_DATA; 81646492Swpaul } 81746492Swpaul } 81846492Swpaul#undef FF_LLC_SIZE 81946492Swpaul ieee80211_deliver_data(vap, ni, m); 82046492Swpaul return IEEE80211_FC0_TYPE_DATA; 82146492Swpaul 82246492Swpaul case IEEE80211_FC0_TYPE_MGT: 82353702Swpaul vap->iv_stats.is_rx_mgmt++; 82446492Swpaul IEEE80211_NODE_STAT(ni, rx_mgmt); 82546492Swpaul if (dir != IEEE80211_FC1_DIR_NODS) { 82646492Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 82746492Swpaul wh, "data", "incorrect dir 0x%x", dir); 82846492Swpaul vap->iv_stats.is_rx_wrongdir++; 82946492Swpaul goto err; 83046492Swpaul } 83146492Swpaul if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 83246492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 83346492Swpaul ni->ni_macaddr, "mgt", "too short: len %u", 83446492Swpaul m->m_pkthdr.len); 83546492Swpaul vap->iv_stats.is_rx_tooshort++; 83646492Swpaul goto out; 83746492Swpaul } 83847789Swpaul#ifdef IEEE80211_DEBUG 83947789Swpaul if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || 84046492Swpaul ieee80211_msg_dumppkts(vap)) { 84146492Swpaul if_printf(ifp, "received %s from %s rssi %d\n", 84246492Swpaul ieee80211_mgt_subtype_name[subtype >> 84346492Swpaul IEEE80211_FC0_SUBTYPE_SHIFT], 84446492Swpaul ether_sprintf(wh->i_addr2), rssi); 84546492Swpaul } 84646492Swpaul#endif 84746492Swpaul if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 84846492Swpaul if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { 84946492Swpaul /* 85046492Swpaul * Only shared key auth frames with a challenge 85146492Swpaul * should be encrypted, discard all others. 85246492Swpaul */ 85346492Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 85446492Swpaul wh, ieee80211_mgt_subtype_name[subtype >> 85546492Swpaul IEEE80211_FC0_SUBTYPE_SHIFT], 85646492Swpaul "%s", "WEP set but not permitted"); 85746492Swpaul vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ 85846492Swpaul goto out; 85946492Swpaul } 86046492Swpaul if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 86146492Swpaul /* 86246492Swpaul * Discard encrypted frames when privacy is off. 86346492Swpaul */ 86446492Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 86546492Swpaul wh, "mgt", "%s", "WEP set but PRIVACY off"); 86646492Swpaul vap->iv_stats.is_rx_noprivacy++; 86746492Swpaul goto out; 86846492Swpaul } 86946492Swpaul hdrspace = ieee80211_hdrspace(ic, wh); 87046492Swpaul key = ieee80211_crypto_decap(ni, m, hdrspace); 87146492Swpaul if (key == NULL) { 87246492Swpaul /* NB: stats+msgs handled in crypto_decap */ 87346492Swpaul goto out; 87446492Swpaul } 87546492Swpaul wh = mtod(m, struct ieee80211_frame *); 87646492Swpaul wh->i_fc[1] &= ~IEEE80211_FC1_WEP; 87746492Swpaul } 87846492Swpaul if (bpf_peers_present(vap->iv_rawbpf)) 87946492Swpaul bpf_mtap(vap->iv_rawbpf, m); 88046492Swpaul vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); 88146492Swpaul m_freem(m); 88246492Swpaul return IEEE80211_FC0_TYPE_MGT; 88346492Swpaul 88446492Swpaul case IEEE80211_FC0_TYPE_CTL: 88546492Swpaul vap->iv_stats.is_rx_ctl++; 88646492Swpaul IEEE80211_NODE_STAT(ni, rx_ctrl); 88746492Swpaul goto out; 88846492Swpaul default: 88946492Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 89046492Swpaul wh, NULL, "bad frame type 0x%x", type); 89146492Swpaul /* should not come here */ 89246492Swpaul break; 89346492Swpaul } 89446492Swpaulerr: 89546492Swpaul ifp->if_ierrors++; 89646492Swpaulout: 89746492Swpaul if (m != NULL) { 89846492Swpaul if (bpf_peers_present(vap->iv_rawbpf) && need_tap) 89946492Swpaul bpf_mtap(vap->iv_rawbpf, m); 90046492Swpaul m_freem(m); 90146492Swpaul } 90246492Swpaul return type; 90346492Swpaul#undef SEQ_LEQ 90446492Swpaul} 90546492Swpaul 90646492Swpaulstatic void 90746492Swpaulsta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh, 90846492Swpaul int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status) 90946492Swpaul{ 91046492Swpaul struct ieee80211vap *vap = ni->ni_vap; 91146492Swpaul 91246492Swpaul if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { 91346492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 91446492Swpaul ni->ni_macaddr, "open auth", 91546492Swpaul "bad sta auth mode %u", ni->ni_authmode); 91646492Swpaul vap->iv_stats.is_rx_bad_auth++; /* XXX */ 91746492Swpaul return; 91846492Swpaul } 91946492Swpaul if (vap->iv_state != IEEE80211_S_AUTH || 92046492Swpaul seq != IEEE80211_AUTH_OPEN_RESPONSE) { 92146492Swpaul vap->iv_stats.is_rx_bad_auth++; 92246492Swpaul return; 92346563Swpaul } 92446563Swpaul if (status != 0) { 92546563Swpaul IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 92646492Swpaul ni, "open auth failed (reason %d)", status); 92746492Swpaul vap->iv_stats.is_rx_auth_fail++; 92846492Swpaul vap->iv_stats.is_rx_authfail_code = status; 92946492Swpaul ieee80211_new_state(vap, IEEE80211_S_SCAN, 93046492Swpaul IEEE80211_SCAN_FAIL_STATUS); 93146492Swpaul } else 93246492Swpaul ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); 93346492Swpaul} 93446492Swpaul 93546492Swpaulstatic void 93646492Swpaulsta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh, 93746492Swpaul uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp, 93846611Swpaul uint16_t seq, uint16_t status) 93946611Swpaul{ 94046611Swpaul struct ieee80211vap *vap = ni->ni_vap; 94146611Swpaul uint8_t *challenge; 94246611Swpaul int estatus; 94346611Swpaul 94456965Swpaul /* 94556965Swpaul * NB: this can happen as we allow pre-shared key 94656965Swpaul * authentication to be enabled w/o wep being turned 94756965Swpaul * on so that configuration of these can be done 94856965Swpaul * in any order. It may be better to enforce the 94956965Swpaul * ordering in which case this check would just be 95056965Swpaul * for sanity/consistency. 95156965Swpaul */ 95256965Swpaul if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 95356965Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 95446492Swpaul ni->ni_macaddr, "shared key auth", 95546492Swpaul "%s", " PRIVACY is disabled"); 95646492Swpaul estatus = IEEE80211_STATUS_ALG; 95746492Swpaul goto bad; 95846563Swpaul } 95946563Swpaul /* 96046563Swpaul * Pre-shared key authentication is evil; accept 96146492Swpaul * it only if explicitly configured (it is supported 96246492Swpaul * mainly for compatibility with clients like OS X). 96346492Swpaul */ 96446492Swpaul if (ni->ni_authmode != IEEE80211_AUTH_AUTO && 96546492Swpaul ni->ni_authmode != IEEE80211_AUTH_SHARED) { 96646492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 96746492Swpaul ni->ni_macaddr, "shared key auth", 96846492Swpaul "bad sta auth mode %u", ni->ni_authmode); 96967092Swpaul vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ 97046492Swpaul estatus = IEEE80211_STATUS_ALG; 97146492Swpaul goto bad; 97246492Swpaul } 97361818Sroberto 97446492Swpaul challenge = NULL; 97546492Swpaul if (frm + 1 < efrm) { 97667092Swpaul if ((frm[1] + 2) > (efrm - frm)) { 97746492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 97846492Swpaul ni->ni_macaddr, "shared key auth", 97961818Sroberto "ie %d/%d too long", 98061818Sroberto frm[0], (frm[1] + 2) - (efrm - frm)); 98161818Sroberto vap->iv_stats.is_rx_bad_auth++; 98261818Sroberto estatus = IEEE80211_STATUS_CHALLENGE; 98346492Swpaul goto bad; 98446492Swpaul } 98546492Swpaul if (*frm == IEEE80211_ELEMID_CHALLENGE) 98646492Swpaul challenge = frm; 98746492Swpaul frm += frm[1] + 2; 98846492Swpaul } 98946492Swpaul switch (seq) { 99046492Swpaul case IEEE80211_AUTH_SHARED_CHALLENGE: 99146492Swpaul case IEEE80211_AUTH_SHARED_RESPONSE: 99246492Swpaul if (challenge == NULL) { 99346492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 99446492Swpaul ni->ni_macaddr, "shared key auth", 99546492Swpaul "%s", "no challenge"); 99646492Swpaul vap->iv_stats.is_rx_bad_auth++; 99746492Swpaul estatus = IEEE80211_STATUS_CHALLENGE; 99846492Swpaul goto bad; 99946492Swpaul } 100046492Swpaul if (challenge[1] != IEEE80211_CHALLENGE_LEN) { 100146492Swpaul IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 100246492Swpaul ni->ni_macaddr, "shared key auth", 100346492Swpaul "bad challenge len %d", challenge[1]); 100446492Swpaul vap->iv_stats.is_rx_bad_auth++; 100546492Swpaul estatus = IEEE80211_STATUS_CHALLENGE; 100646492Swpaul goto bad; 100746492Swpaul } 100846492Swpaul default: 100946492Swpaul break; 101046492Swpaul } 101146492Swpaul if (vap->iv_state != IEEE80211_S_AUTH) 101246492Swpaul return; 101346492Swpaul switch (seq) { 101446492Swpaul case IEEE80211_AUTH_SHARED_PASS: 101546492Swpaul if (ni->ni_challenge != NULL) { 101646492Swpaul FREE(ni->ni_challenge, M_80211_NODE); 101746492Swpaul ni->ni_challenge = NULL; 101846492Swpaul } 101965581Swpaul if (status != 0) { 102065581Swpaul IEEE80211_NOTE_FRAME(vap, 102165581Swpaul IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh, 102246492Swpaul "shared key auth failed (reason %d)", status); 102346492Swpaul vap->iv_stats.is_rx_auth_fail++; 102446492Swpaul vap->iv_stats.is_rx_authfail_code = status; 102546492Swpaul return; 102656965Swpaul } 102756965Swpaul ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); 102856965Swpaul break; 102953702Swpaul case IEEE80211_AUTH_SHARED_CHALLENGE: 103053702Swpaul if (!ieee80211_alloc_challenge(ni)) 103153702Swpaul return; 103253702Swpaul /* XXX could optimize by passing recvd challenge */ 103353702Swpaul memcpy(ni->ni_challenge, &challenge[2], challenge[1]); 103453702Swpaul IEEE80211_SEND_MGMT(ni, 103553702Swpaul IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 103653702Swpaul break; 103753702Swpaul default: 103853702Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH, 103953702Swpaul wh, "shared key auth", "bad seq %d", seq); 104053702Swpaul vap->iv_stats.is_rx_bad_auth++; 104153702Swpaul return; 104253702Swpaul } 104353702Swpaul return; 104453702Swpaulbad: 104553702Swpaul /* 104646492Swpaul * Kick the state machine. This short-circuits 104746492Swpaul * using the mgt frame timeout to trigger the 104846492Swpaul * state transition. 104946492Swpaul */ 105046492Swpaul if (vap->iv_state == IEEE80211_S_AUTH) 105146492Swpaul ieee80211_new_state(vap, IEEE80211_S_SCAN, 105246492Swpaul IEEE80211_SCAN_FAIL_STATUS); 105346492Swpaul} 105461818Sroberto 105561818Srobertostatic int 105646492Swpaulieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm, 105746492Swpaul const struct ieee80211_frame *wh) 105846492Swpaul{ 105946492Swpaul#define MS(_v, _f) (((_v) & _f) >> _f##_S) 106046492Swpaul struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme; 106146492Swpaul u_int len = frm[1], qosinfo; 106246492Swpaul int i; 106346492Swpaul 106446492Swpaul if (len < sizeof(struct ieee80211_wme_param)-2) { 106546492Swpaul IEEE80211_DISCARD_IE(vap, 106646492Swpaul IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME, 106746492Swpaul wh, "WME", "too short, len %u", len); 106846492Swpaul return -1; 106946492Swpaul } 107046492Swpaul qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)]; 107146492Swpaul qosinfo &= WME_QOSINFO_COUNT; 107246492Swpaul /* XXX do proper check for wraparound */ 107346492Swpaul if (qosinfo == wme->wme_wmeChanParams.cap_info) 107446492Swpaul return 0; 107561818Sroberto frm += __offsetof(struct ieee80211_wme_param, params_acParams); 107667092Swpaul for (i = 0; i < WME_NUM_AC; i++) { 107746492Swpaul struct wmeParams *wmep = 107846492Swpaul &wme->wme_wmeChanParams.cap_wmeParams[i]; 107946492Swpaul /* NB: ACI not used */ 108046492Swpaul wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM); 108146492Swpaul wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN); 108246492Swpaul wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN); 108346492Swpaul wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX); 108446492Swpaul wmep->wmep_txopLimit = LE_READ_2(frm+2); 108546492Swpaul frm += 4; 108646492Swpaul } 108746492Swpaul wme->wme_wmeChanParams.cap_info = qosinfo; 108846492Swpaul return 1; 108967092Swpaul#undef MS 109067092Swpaul} 109167092Swpaul 109267092Swpaulstatic int 109346492Swpaulieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm, 109467092Swpaul const struct ieee80211_frame *wh) 109546492Swpaul{ 109646492Swpaul struct ieee80211vap *vap = ni->ni_vap; 109746492Swpaul const struct ieee80211_ath_ie *ath; 109846492Swpaul u_int len = frm[1]; 109946492Swpaul int capschanged; 110046492Swpaul uint16_t defkeyix; 110146492Swpaul 110246492Swpaul if (len < sizeof(struct ieee80211_ath_ie)-2) { 110346492Swpaul IEEE80211_DISCARD_IE(vap, 110447401Swpaul IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG, 110546492Swpaul wh, "Atheros", "too short, len %u", len); 110646492Swpaul return -1; 110746492Swpaul } 110846492Swpaul ath = (const struct ieee80211_ath_ie *)frm; 110946492Swpaul capschanged = (ni->ni_ath_flags != ath->ath_capability); 111046492Swpaul defkeyix = LE_READ_2(ath->ath_defkeyix); 111146492Swpaul if (capschanged || defkeyix != ni->ni_ath_defkeyix) { 111246492Swpaul ni->ni_ath_flags = ath->ath_capability; 111346492Swpaul ni->ni_ath_defkeyix = defkeyix; 111446492Swpaul IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni, 111546492Swpaul "ath ie change: new caps 0x%x defkeyix 0x%x", 111646492Swpaul ni->ni_ath_flags, ni->ni_ath_defkeyix); 111746492Swpaul } 111846492Swpaul if (IEEE80211_ATH_CAP(vap, ni, ATHEROS_CAP_TURBO_PRIME)) { 111946611Swpaul uint16_t curflags, newflags; 112046611Swpaul 112146611Swpaul /* 112246611Swpaul * Check for turbo mode switch. Calculate flags 112346611Swpaul * for the new mode and effect the switch. 112446611Swpaul */ 112546492Swpaul newflags = curflags = vap->iv_ic->ic_bsschan->ic_flags; 112646492Swpaul /* NB: BOOST is not in ic_flags, so get it from the ie */ 112746492Swpaul if (ath->ath_capability & ATHEROS_CAP_BOOST) 112846492Swpaul newflags |= IEEE80211_CHAN_TURBO; 112946492Swpaul else 113046492Swpaul newflags &= ~IEEE80211_CHAN_TURBO; 113146563Swpaul if (newflags != curflags) 113246563Swpaul ieee80211_dturbo_switch(vap, newflags); 113346563Swpaul } 113446492Swpaul return capschanged; 113546492Swpaul} 113646492Swpaul 113746492Swpaul/* 113846492Swpaul * Return non-zero if a background scan may be continued: 113946492Swpaul * o bg scan is active 114046492Swpaul * o no channel switch is pending 114146492Swpaul * o there has not been any traffic recently 114246492Swpaul * 114346492Swpaul * Note we do not check if there is an administrative enable; 114456965Swpaul * this is only done to start the scan. We assume that any 114556965Swpaul * change in state will be accompanied by a request to cancel 114656965Swpaul * active scans which will otherwise cause this test to fail. 114756965Swpaul */ 114856965Swpaulstatic __inline int 114956965Swpaulcontbgscan(struct ieee80211vap *vap) 115056965Swpaul{ 115156965Swpaul struct ieee80211com *ic = vap->iv_ic; 115256965Swpaul 115346492Swpaul return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) && 115446492Swpaul (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && 115546492Swpaul vap->iv_state == IEEE80211_S_RUN && /* XXX? */ 115646492Swpaul time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); 115746492Swpaul} 115846492Swpaul 115946492Swpaul/* 116046492Swpaul * Return non-zero if a backgrond scan may be started: 116146492Swpaul * o bg scanning is administratively enabled 116246492Swpaul * o no channel switch is pending 116346492Swpaul * o we are not boosted on a dynamic turbo channel 116446492Swpaul * o there has not been a scan recently 116546492Swpaul * o there has not been any traffic recently 116646492Swpaul */ 116753702Swpaulstatic __inline int 116846492Swpaulstartbgscan(struct ieee80211vap *vap) 116946492Swpaul{ 117046492Swpaul struct ieee80211com *ic = vap->iv_ic; 117153702Swpaul 117246492Swpaul return ((vap->iv_flags & IEEE80211_F_BGSCAN) && 117346492Swpaul (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && 117446492Swpaul !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && 117546492Swpaul time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) && 117646492Swpaul time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); 117746492Swpaul} 117846492Swpaul 117946492Swpaulstatic void 118046492Swpaulsta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 118167092Swpaul int subtype, int rssi, int noise, uint32_t rstamp) 118246492Swpaul{ 118346492Swpaul#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 118446492Swpaul#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) 118546492Swpaul struct ieee80211vap *vap = ni->ni_vap; 118646492Swpaul struct ieee80211com *ic = ni->ni_ic; 118746492Swpaul struct ieee80211_frame *wh; 118846492Swpaul uint8_t *frm, *efrm; 118946492Swpaul uint8_t *rates, *xrates, *wme, *htcap, *htinfo; 119046492Swpaul uint8_t rate; 119146492Swpaul 119246492Swpaul wh = mtod(m0, struct ieee80211_frame *); 119346492Swpaul frm = (uint8_t *)&wh[1]; 119446492Swpaul efrm = mtod(m0, uint8_t *) + m0->m_len; 119546492Swpaul switch (subtype) { 119667092Swpaul case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 119746492Swpaul case IEEE80211_FC0_SUBTYPE_BEACON: { 119867092Swpaul struct ieee80211_scanparams scan; 119967092Swpaul /* 120046492Swpaul * We process beacon/probe response frames: 120167092Swpaul * o when scanning, or 120246492Swpaul * o station mode when associated (to collect state 120367092Swpaul * updates such as 802.11g slot time), or 120467092Swpaul * Frames otherwise received are discarded. 120546492Swpaul */ 120667092Swpaul if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) { 120746492Swpaul vap->iv_stats.is_rx_mgtdiscard++; 120846492Swpaul return; 120967092Swpaul } 121067092Swpaul /* XXX probe response in sta mode when !scanning? */ 121146492Swpaul if (ieee80211_parse_beacon(ni, m0, &scan) != 0) 121267092Swpaul return; 121346492Swpaul /* 121446492Swpaul * Count frame now that we know it's to be processed. 121546492Swpaul */ 121646492Swpaul if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { 121746492Swpaul vap->iv_stats.is_rx_beacon++; /* XXX remove */ 121846492Swpaul IEEE80211_NODE_STAT(ni, rx_beacons); 121947401Swpaul } else 122046492Swpaul IEEE80211_NODE_STAT(ni, rx_proberesp); 122146492Swpaul /* 122255831Swpaul * When operating in station mode, check for state updates. 122346492Swpaul * Be careful to ignore beacons received while doing a 122446492Swpaul * background scan. We consider only 11g/WMM stuff right now. 122546492Swpaul */ 122646492Swpaul if (ni->ni_associd != 0 && 122746492Swpaul ((ic->ic_flags & IEEE80211_F_SCAN) == 0 || 122846492Swpaul IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) { 122946492Swpaul /* record tsf of last beacon */ 123046492Swpaul memcpy(ni->ni_tstamp.data, scan.tstamp, 123146492Swpaul sizeof(ni->ni_tstamp)); 123246492Swpaul /* count beacon frame for s/w bmiss handling */ 123346492Swpaul vap->iv_swbmiss_count++; 123446492Swpaul vap->iv_bmiss_count = 0; 123546492Swpaul if (ni->ni_erp != scan.erp) { 123646492Swpaul IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, 123746492Swpaul wh->i_addr2, 123846492Swpaul "erp change: was 0x%x, now 0x%x", 123946492Swpaul ni->ni_erp, scan.erp); 124046492Swpaul if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 124146492Swpaul (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) 124246492Swpaul ic->ic_flags |= IEEE80211_F_USEPROT; 124346492Swpaul else 124446492Swpaul ic->ic_flags &= ~IEEE80211_F_USEPROT; 124546492Swpaul ni->ni_erp = scan.erp; 124646492Swpaul /* XXX statistic */ 124746492Swpaul /* XXX driver notification */ 124846492Swpaul } 124946492Swpaul if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) { 125055831Swpaul IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, 125146492Swpaul wh->i_addr2, 125246492Swpaul "capabilities change: was 0x%x, now 0x%x", 125346492Swpaul ni->ni_capinfo, scan.capinfo); 125446492Swpaul /* 125546492Swpaul * NB: we assume short preamble doesn't 125646492Swpaul * change dynamically 125746492Swpaul */ 125846492Swpaul ieee80211_set_shortslottime(ic, 125946492Swpaul IEEE80211_IS_CHAN_A(ic->ic_bsschan) || 126046492Swpaul (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); 126146492Swpaul ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME) 126246492Swpaul | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME); 126346492Swpaul /* XXX statistic */ 126446492Swpaul } 126546492Swpaul if (scan.wme != NULL && 126646492Swpaul (ni->ni_flags & IEEE80211_NODE_QOS) && 126746492Swpaul ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0) 126846492Swpaul ieee80211_wme_updateparams(vap); 126953702Swpaul if (scan.ath != NULL) 127046492Swpaul ieee80211_parse_athparams(ni, scan.ath, wh); 127146492Swpaul if (scan.htcap != NULL && scan.htinfo != NULL) { 127246492Swpaul ieee80211_parse_htcap(ni, scan.htcap); 127346492Swpaul ieee80211_parse_htinfo(ni, scan.htinfo); 127446492Swpaul /* XXX state changes? */ 127546492Swpaul } 127646492Swpaul if (scan.tim != NULL) { 127746492Swpaul struct ieee80211_tim_ie *tim = 127867092Swpaul (struct ieee80211_tim_ie *) scan.tim; 127946492Swpaul#if 0 128046492Swpaul int aid = IEEE80211_AID(ni->ni_associd); 128146492Swpaul int ix = aid / NBBY; 128246492Swpaul int min = tim->tim_bitctl &~ 1; 128346492Swpaul int max = tim->tim_len + min - 4; 128446492Swpaul if ((tim->tim_bitctl&1) || 128546492Swpaul (min <= ix && ix <= max && 128646492Swpaul isset(tim->tim_bitmap - min, aid))) { 128746492Swpaul /* 128846492Swpaul * XXX Do not let bg scan kick off 128946492Swpaul * we are expecting data. 129046492Swpaul */ 129146492Swpaul ic->ic_lastdata = ticks; 129246492Swpaul ieee80211_sta_pwrsave(vap, 0); 129346492Swpaul } 129446492Swpaul#endif 129546492Swpaul ni->ni_dtim_count = tim->tim_count; 129646492Swpaul ni->ni_dtim_period = tim->tim_period; 129746492Swpaul } 129846492Swpaul /* 129946492Swpaul * If scanning, pass the info to the scan module. 130046492Swpaul * Otherwise, check if it's the right time to do 130146492Swpaul * a background scan. Background scanning must 130246492Swpaul * be enabled and we must not be operating in the 130346492Swpaul * turbo phase of dynamic turbo mode. Then, 130446492Swpaul * it's been a while since the last background 130546492Swpaul * scan and if no data frames have come through 130646492Swpaul * recently, kick off a scan. Note that this 130746492Swpaul * is the mechanism by which a background scan 130846492Swpaul * is started _and_ continued each time we 130946492Swpaul * return on-channel to receive a beacon from 131046492Swpaul * our ap. 131146492Swpaul */ 131253702Swpaul if (ic->ic_flags & IEEE80211_F_SCAN) { 131346492Swpaul ieee80211_add_scan(vap, &scan, wh, 131446492Swpaul subtype, rssi, noise, rstamp); 131546492Swpaul } else if (contbgscan(vap)) { 131646492Swpaul ieee80211_bg_scan(vap, 0); 131746492Swpaul } else if (startbgscan(vap)) { 131846492Swpaul vap->iv_stats.is_scan_bg++; 131946492Swpaul#if 0 132046492Swpaul /* wakeup if we are sleeing */ 132146492Swpaul ieee80211_set_pwrsave(vap, 0); 132246492Swpaul#endif 132346492Swpaul ieee80211_bg_scan(vap, 0); 132467092Swpaul } 132567092Swpaul return; 132667092Swpaul } 132767092Swpaul /* 132846492Swpaul * If scanning, just pass information to the scan module. 132967092Swpaul */ 133046492Swpaul if (ic->ic_flags & IEEE80211_F_SCAN) { 133146492Swpaul if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { 133246492Swpaul /* 133346492Swpaul * Actively scanning a channel marked passive; 133446492Swpaul * send a probe request now that we know there 133546492Swpaul * is 802.11 traffic present. 133646492Swpaul * 133746492Swpaul * XXX check if the beacon we recv'd gives 133846492Swpaul * us what we need and suppress the probe req 133946492Swpaul */ 134067092Swpaul ieee80211_probe_curchan(vap, 1); 134146492Swpaul ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; 134246492Swpaul } 134346492Swpaul ieee80211_add_scan(vap, &scan, wh, 134446492Swpaul subtype, rssi, noise, rstamp); 134546492Swpaul return; 134646492Swpaul } 134746492Swpaul break; 134846492Swpaul } 134946492Swpaul 135046492Swpaul case IEEE80211_FC0_SUBTYPE_AUTH: { 135153702Swpaul uint16_t algo, seq, status; 135246492Swpaul /* 135346492Swpaul * auth frame format 135446492Swpaul * [2] algorithm 135546492Swpaul * [2] sequence 135646492Swpaul * [2] status 135746492Swpaul * [tlv*] challenge 135846492Swpaul */ 135946492Swpaul IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); 136053702Swpaul algo = le16toh(*(uint16_t *)frm); 136153702Swpaul seq = le16toh(*(uint16_t *)(frm + 2)); 136246492Swpaul status = le16toh(*(uint16_t *)(frm + 4)); 136353702Swpaul IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2, 136453702Swpaul "recv auth frame with algorithm %d seq %d", algo, seq); 136553702Swpaul 136653702Swpaul if (vap->iv_flags & IEEE80211_F_COUNTERM) { 136753702Swpaul IEEE80211_DISCARD(vap, 136853702Swpaul IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, 136953702Swpaul wh, "auth", "%s", "TKIP countermeasures enabled"); 137053702Swpaul vap->iv_stats.is_rx_auth_countermeasures++; 137153702Swpaul if (vap->iv_opmode == IEEE80211_M_HOSTAP) { 137253702Swpaul ieee80211_send_error(ni, wh->i_addr2, 137353702Swpaul IEEE80211_FC0_SUBTYPE_AUTH, 137453702Swpaul IEEE80211_REASON_MIC_FAILURE); 137553702Swpaul } 137653702Swpaul return; 137753702Swpaul } 137853702Swpaul if (algo == IEEE80211_AUTH_ALG_SHARED) 137953702Swpaul sta_auth_shared(ni, wh, frm + 6, efrm, rssi, 138053702Swpaul noise, rstamp, seq, status); 138153702Swpaul else if (algo == IEEE80211_AUTH_ALG_OPEN) 138253702Swpaul sta_auth_open(ni, wh, rssi, noise, rstamp, 138353702Swpaul seq, status); 138453702Swpaul else { 138553702Swpaul IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 138653702Swpaul wh, "auth", "unsupported alg %d", algo); 138753702Swpaul vap->iv_stats.is_rx_auth_unsupported++; 138853702Swpaul return; 138953702Swpaul } 139053702Swpaul break; 139153702Swpaul } 139253702Swpaul 139353702Swpaul case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 139453702Swpaul case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { 139553702Swpaul uint16_t capinfo, associd; 139653702Swpaul uint16_t status; 139753702Swpaul 139853702Swpaul if (vap->iv_state != IEEE80211_S_ASSOC) { 139953702Swpaul vap->iv_stats.is_rx_mgtdiscard++; 140053702Swpaul return; 140153702Swpaul } 140253702Swpaul 140353702Swpaul /* 140453702Swpaul * asresp frame format 140553702Swpaul * [2] capability information 140653702Swpaul * [2] status 140746492Swpaul * [2] association ID 140846492Swpaul * [tlv] supported rates 140953702Swpaul * [tlv] extended supported rates 141046492Swpaul * [tlv] WME 141146492Swpaul * [tlv] HT capabilities 141246492Swpaul * [tlv] HT info 141346492Swpaul */ 141453702Swpaul IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); 141553702Swpaul ni = vap->iv_bss; 141653702Swpaul capinfo = le16toh(*(uint16_t *)frm); 141753702Swpaul frm += 2; 141853702Swpaul status = le16toh(*(uint16_t *)frm); 141953702Swpaul frm += 2; 142053702Swpaul if (status != 0) { 142153702Swpaul IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, 142253702Swpaul wh->i_addr2, "%sassoc failed (reason %d)", 142353702Swpaul ISREASSOC(subtype) ? "re" : "", status); 142453702Swpaul vap->iv_stats.is_rx_auth_fail++; /* XXX */ 142553702Swpaul return; 142653702Swpaul } 142753702Swpaul associd = le16toh(*(uint16_t *)frm); 142853702Swpaul frm += 2; 142953702Swpaul 143053702Swpaul rates = xrates = wme = htcap = htinfo = NULL; 143153702Swpaul while (efrm - frm > 1) { 143253702Swpaul IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 143353702Swpaul switch (*frm) { 143453702Swpaul case IEEE80211_ELEMID_RATES: 143553702Swpaul rates = frm; 143653702Swpaul break; 143753702Swpaul case IEEE80211_ELEMID_XRATES: 143853702Swpaul xrates = frm; 143953702Swpaul break; 144053702Swpaul case IEEE80211_ELEMID_HTCAP: 144153702Swpaul htcap = frm; 144253702Swpaul break; 144353702Swpaul case IEEE80211_ELEMID_HTINFO: 144453702Swpaul htinfo = frm; 144553702Swpaul break; 144653702Swpaul case IEEE80211_ELEMID_VENDOR: 144753702Swpaul if (iswmeoui(frm)) 144853702Swpaul wme = frm; 144953702Swpaul else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) { 145053702Swpaul /* 145153702Swpaul * Accept pre-draft HT ie's if the 145253702Swpaul * standard ones have not been seen. 145353702Swpaul */ 145453702Swpaul if (ishtcapoui(frm)) { 145553702Swpaul if (htcap == NULL) 145653702Swpaul htcap = frm; 145753702Swpaul } else if (ishtinfooui(frm)) { 145853702Swpaul if (htinfo == NULL) 145953702Swpaul htcap = frm; 146053702Swpaul } 146153702Swpaul } 146253702Swpaul /* XXX Atheros OUI support */ 146353702Swpaul break; 146453702Swpaul } 146553702Swpaul frm += frm[1] + 2; 146653702Swpaul } 146753702Swpaul 146853702Swpaul IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); 146953702Swpaul if (xrates != NULL) 147053702Swpaul IEEE80211_VERIFY_ELEMENT(xrates, 147153702Swpaul IEEE80211_RATE_MAXSIZE - rates[1], return); 147253702Swpaul rate = ieee80211_setup_rates(ni, rates, xrates, 147353702Swpaul IEEE80211_F_JOIN | 147453702Swpaul IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | 147553702Swpaul IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 147653702Swpaul if (rate & IEEE80211_RATE_BASIC) { 147753702Swpaul IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, 147853702Swpaul wh->i_addr2, 147953702Swpaul "%sassoc failed (rate set mismatch)", 148053702Swpaul ISREASSOC(subtype) ? "re" : ""); 148153702Swpaul vap->iv_stats.is_rx_assoc_norate++; 148253702Swpaul ieee80211_new_state(vap, IEEE80211_S_SCAN, 148353702Swpaul IEEE80211_SCAN_FAIL_STATUS); 148453702Swpaul return; 148553702Swpaul } 148653702Swpaul 148753702Swpaul ni->ni_capinfo = capinfo; 148853702Swpaul ni->ni_associd = associd; 148953702Swpaul if (ni->ni_jointime == 0) 149053702Swpaul ni->ni_jointime = time_uptime; 149153702Swpaul if (wme != NULL && 149253702Swpaul ieee80211_parse_wmeparams(vap, wme, wh) >= 0) { 149353702Swpaul ni->ni_flags |= IEEE80211_NODE_QOS; 149453702Swpaul ieee80211_wme_updateparams(vap); 149553702Swpaul } else 149653702Swpaul ni->ni_flags &= ~IEEE80211_NODE_QOS; 149753702Swpaul /* 149853702Swpaul * Setup HT state according to the negotiation. 149953702Swpaul * 150053702Swpaul * NB: shouldn't need to check if HT use is enabled but some 150153702Swpaul * ap's send back HT ie's even when we don't indicate we 150253702Swpaul * are HT capable in our AssocReq. 150353702Swpaul */ 150453702Swpaul if (htcap != NULL && htinfo != NULL && 150553702Swpaul (vap->iv_flags_ext & IEEE80211_FEXT_HT)) { 150653702Swpaul ieee80211_ht_node_init(ni, htcap); 150753702Swpaul ieee80211_parse_htinfo(ni, htinfo); 150853702Swpaul ieee80211_setup_htrates(ni, htcap, 150953702Swpaul IEEE80211_F_JOIN | IEEE80211_F_DOBRS); 151053702Swpaul ieee80211_setup_basic_htrates(ni, htinfo); 151153702Swpaul } 151253702Swpaul /* 151353702Swpaul * Configure state now that we are associated. 151453702Swpaul * 151553702Swpaul * XXX may need different/additional driver callbacks? 151653702Swpaul */ 151753702Swpaul if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || 151853702Swpaul (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { 151953702Swpaul ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 152053702Swpaul ic->ic_flags &= ~IEEE80211_F_USEBARKER; 152153702Swpaul } else { 152253702Swpaul ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 152353702Swpaul ic->ic_flags |= IEEE80211_F_USEBARKER; 152453702Swpaul } 152553702Swpaul ieee80211_set_shortslottime(ic, 152653702Swpaul IEEE80211_IS_CHAN_A(ic->ic_curchan) || 152753702Swpaul (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); 152853702Swpaul /* 152953702Swpaul * Honor ERP protection. 153053702Swpaul * 153153702Swpaul * NB: ni_erp should zero for non-11g operation. 153253702Swpaul */ 153353702Swpaul if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 153453702Swpaul (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) 153553702Swpaul ic->ic_flags |= IEEE80211_F_USEPROT; 153653702Swpaul else 153753702Swpaul ic->ic_flags &= ~IEEE80211_F_USEPROT; 153853702Swpaul IEEE80211_NOTE_MAC(vap, 153953702Swpaul IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2, 154053702Swpaul "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s", 154153702Swpaul ISREASSOC(subtype) ? "re" : "", 154253702Swpaul IEEE80211_NODE_AID(ni), 154353702Swpaul ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", 154453702Swpaul ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", 154553702Swpaul ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "", 154653702Swpaul ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", 154753702Swpaul ni->ni_flags & IEEE80211_NODE_HT ? 154853702Swpaul (ni->ni_chw == 40 ? ", HT40" : ", HT20") : "", 154953702Swpaul ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", 155053702Swpaul IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ? 155153702Swpaul ", fast-frames" : "", 155253702Swpaul IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ? 155353702Swpaul ", turbo" : "" 155453702Swpaul ); 155553702Swpaul ieee80211_new_state(vap, IEEE80211_S_RUN, subtype); 155653702Swpaul break; 155753702Swpaul } 155853702Swpaul 155953702Swpaul case IEEE80211_FC0_SUBTYPE_DEAUTH: { 156053702Swpaul uint16_t reason; 156153702Swpaul 156253702Swpaul if (vap->iv_state == IEEE80211_S_SCAN) { 156353702Swpaul vap->iv_stats.is_rx_mgtdiscard++; 156453702Swpaul return; 156553702Swpaul } 156653702Swpaul if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { 156753702Swpaul /* NB: can happen when in promiscuous mode */ 156853702Swpaul vap->iv_stats.is_rx_mgtdiscard++; 156953702Swpaul break; 157053702Swpaul } 157153702Swpaul 157253702Swpaul /* 157353702Swpaul * deauth frame format 157453702Swpaul * [2] reason 157553702Swpaul */ 157653702Swpaul IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); 157753702Swpaul reason = le16toh(*(uint16_t *)frm); 157853702Swpaul 157953702Swpaul vap->iv_stats.is_rx_deauth++; 158053702Swpaul vap->iv_stats.is_rx_deauth_code = reason; 158153702Swpaul IEEE80211_NODE_STAT(ni, rx_deauth); 158253702Swpaul 158353702Swpaul IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, 158453702Swpaul "recv deauthenticate (reason %d)", reason); 158553702Swpaul ieee80211_new_state(vap, IEEE80211_S_AUTH, 158653702Swpaul (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH); 158753702Swpaul break; 158853702Swpaul } 158953702Swpaul 159053702Swpaul case IEEE80211_FC0_SUBTYPE_DISASSOC: { 159153702Swpaul uint16_t reason; 159253702Swpaul 159353702Swpaul if (vap->iv_state != IEEE80211_S_RUN && 159453702Swpaul vap->iv_state != IEEE80211_S_ASSOC && 159553702Swpaul vap->iv_state != IEEE80211_S_AUTH) { 159653702Swpaul vap->iv_stats.is_rx_mgtdiscard++; 159753702Swpaul return; 159853702Swpaul } 159953702Swpaul if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { 160053702Swpaul /* NB: can happen when in promiscuous mode */ 160153702Swpaul vap->iv_stats.is_rx_mgtdiscard++; 160253702Swpaul break; 160353702Swpaul } 160453702Swpaul 160553702Swpaul /* 160653702Swpaul * disassoc frame format 160753702Swpaul * [2] reason 160853702Swpaul */ 160953702Swpaul IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); 161053702Swpaul reason = le16toh(*(uint16_t *)frm); 161153702Swpaul 161253702Swpaul vap->iv_stats.is_rx_disassoc++; 161353702Swpaul vap->iv_stats.is_rx_disassoc_code = reason; 161453702Swpaul IEEE80211_NODE_STAT(ni, rx_disassoc); 161553702Swpaul 161653702Swpaul IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, 161753702Swpaul "recv disassociate (reason %d)", reason); 161853702Swpaul ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); 161953702Swpaul break; 162053702Swpaul } 1621 1622 case IEEE80211_FC0_SUBTYPE_ACTION: 1623 if (vap->iv_state == IEEE80211_S_RUN) { 1624 if (ieee80211_parse_action(ni, m0) == 0) 1625 ic->ic_recv_action(ni, frm, efrm); 1626 } else 1627 vap->iv_stats.is_rx_mgtdiscard++; 1628 break; 1629 1630 case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 1631 case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 1632 case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 1633 vap->iv_stats.is_rx_mgtdiscard++; 1634 return; 1635 default: 1636 IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 1637 wh, "mgt", "subtype 0x%x not handled", subtype); 1638 vap->iv_stats.is_rx_badsubtype++; 1639 break; 1640 } 1641#undef ISREASSOC 1642#undef ISPROBE 1643} 1644