132176cfdSRui Paulo /*- 232176cfdSRui Paulo * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting 332176cfdSRui Paulo * All rights reserved. 432176cfdSRui Paulo * 532176cfdSRui Paulo * Redistribution and use in source and binary forms, with or without 632176cfdSRui Paulo * modification, are permitted provided that the following conditions 732176cfdSRui Paulo * are met: 832176cfdSRui Paulo * 1. Redistributions of source code must retain the above copyright 932176cfdSRui Paulo * notice, this list of conditions and the following disclaimer. 1032176cfdSRui Paulo * 2. Redistributions in binary form must reproduce the above copyright 1132176cfdSRui Paulo * notice, this list of conditions and the following disclaimer in the 1232176cfdSRui Paulo * documentation and/or other materials provided with the distribution. 1332176cfdSRui Paulo * 1432176cfdSRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1532176cfdSRui Paulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1632176cfdSRui Paulo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1732176cfdSRui Paulo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1832176cfdSRui Paulo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1932176cfdSRui Paulo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2032176cfdSRui Paulo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2132176cfdSRui Paulo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2232176cfdSRui Paulo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2332176cfdSRui Paulo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2432176cfdSRui Paulo */ 2532176cfdSRui Paulo 26085ff963SMatthew Dillon #include <sys/cdefs.h> 27085ff963SMatthew Dillon #ifdef __FreeBSD__ 28085ff963SMatthew Dillon __FBSDID("$FreeBSD$"); 29085ff963SMatthew Dillon #endif 30085ff963SMatthew Dillon 3132176cfdSRui Paulo /* 3232176cfdSRui Paulo * IEEE 802.11 Station mode support. 3332176cfdSRui Paulo */ 3432176cfdSRui Paulo #include "opt_inet.h" 3532176cfdSRui Paulo #include "opt_wlan.h" 3632176cfdSRui Paulo 3732176cfdSRui Paulo #include <sys/param.h> 3832176cfdSRui Paulo #include <sys/systm.h> 3932176cfdSRui Paulo #include <sys/mbuf.h> 4032176cfdSRui Paulo #include <sys/malloc.h> 4132176cfdSRui Paulo #include <sys/kernel.h> 4232176cfdSRui Paulo 4332176cfdSRui Paulo #include <sys/socket.h> 4432176cfdSRui Paulo #include <sys/sockio.h> 4532176cfdSRui Paulo #include <sys/endian.h> 4632176cfdSRui Paulo #include <sys/errno.h> 4732176cfdSRui Paulo #include <sys/proc.h> 4832176cfdSRui Paulo #include <sys/sysctl.h> 4932176cfdSRui Paulo 5032176cfdSRui Paulo #include <net/if.h> 5132176cfdSRui Paulo #include <net/if_media.h> 5232176cfdSRui Paulo #include <net/if_llc.h> 53085ff963SMatthew Dillon #include <net/if_dl.h> 54085ff963SMatthew Dillon #include <net/if_var.h> 5532176cfdSRui Paulo #include <net/ethernet.h> 5632176cfdSRui Paulo 5732176cfdSRui Paulo #include <net/bpf.h> 5832176cfdSRui Paulo 5932176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h> 6032176cfdSRui Paulo #include <netproto/802_11/ieee80211_sta.h> 6132176cfdSRui Paulo #include <netproto/802_11/ieee80211_input.h> 6232176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 6332176cfdSRui Paulo #include <netproto/802_11/ieee80211_superg.h> 6432176cfdSRui Paulo #endif 65085ff963SMatthew Dillon #include <netproto/802_11/ieee80211_ratectl.h> 66085ff963SMatthew Dillon #include <netproto/802_11/ieee80211_sta.h> 6732176cfdSRui Paulo 6832176cfdSRui Paulo #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) 6932176cfdSRui Paulo 7032176cfdSRui Paulo static void sta_vattach(struct ieee80211vap *); 7132176cfdSRui Paulo static void sta_beacon_miss(struct ieee80211vap *); 7232176cfdSRui Paulo static int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int); 7332176cfdSRui Paulo static int sta_input(struct ieee80211_node *, struct mbuf *, int, int); 7432176cfdSRui Paulo static void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *, 7532176cfdSRui Paulo int subtype, int rssi, int nf); 7632176cfdSRui Paulo static void sta_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype); 7732176cfdSRui Paulo 7832176cfdSRui Paulo void 7932176cfdSRui Paulo ieee80211_sta_attach(struct ieee80211com *ic) 8032176cfdSRui Paulo { 8132176cfdSRui Paulo ic->ic_vattach[IEEE80211_M_STA] = sta_vattach; 8232176cfdSRui Paulo } 8332176cfdSRui Paulo 8432176cfdSRui Paulo void 8532176cfdSRui Paulo ieee80211_sta_detach(struct ieee80211com *ic) 8632176cfdSRui Paulo { 8732176cfdSRui Paulo } 8832176cfdSRui Paulo 8932176cfdSRui Paulo static void 9032176cfdSRui Paulo sta_vdetach(struct ieee80211vap *vap) 9132176cfdSRui Paulo { 9232176cfdSRui Paulo } 9332176cfdSRui Paulo 9432176cfdSRui Paulo static void 9532176cfdSRui Paulo sta_vattach(struct ieee80211vap *vap) 9632176cfdSRui Paulo { 9732176cfdSRui Paulo vap->iv_newstate = sta_newstate; 9832176cfdSRui Paulo vap->iv_input = sta_input; 9932176cfdSRui Paulo vap->iv_recv_mgmt = sta_recv_mgmt; 10032176cfdSRui Paulo vap->iv_recv_ctl = sta_recv_ctl; 10132176cfdSRui Paulo vap->iv_opdetach = sta_vdetach; 10232176cfdSRui Paulo vap->iv_bmiss = sta_beacon_miss; 10332176cfdSRui Paulo } 10432176cfdSRui Paulo 10532176cfdSRui Paulo /* 10632176cfdSRui Paulo * Handle a beacon miss event. The common code filters out 10732176cfdSRui Paulo * spurious events that can happen when scanning and/or before 10832176cfdSRui Paulo * reaching RUN state. 10932176cfdSRui Paulo */ 11032176cfdSRui Paulo static void 11132176cfdSRui Paulo sta_beacon_miss(struct ieee80211vap *vap) 11232176cfdSRui Paulo { 11332176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 11432176cfdSRui Paulo 115085ff963SMatthew Dillon IEEE80211_LOCK_ASSERT(ic); 116085ff963SMatthew Dillon 11732176cfdSRui Paulo KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning")); 11832176cfdSRui Paulo KASSERT(vap->iv_state >= IEEE80211_S_RUN, 11932176cfdSRui Paulo ("wrong state %s", ieee80211_state_name[vap->iv_state])); 12032176cfdSRui Paulo 12132176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 12232176cfdSRui Paulo "beacon miss, mode %s state %s\n", 12332176cfdSRui Paulo ieee80211_opmode_name[vap->iv_opmode], 12432176cfdSRui Paulo ieee80211_state_name[vap->iv_state]); 12532176cfdSRui Paulo 12632176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_CSA) { 12732176cfdSRui Paulo /* 12832176cfdSRui Paulo * A Channel Switch is pending; assume we missed the 12932176cfdSRui Paulo * beacon that would've completed the process and just 13032176cfdSRui Paulo * force the switch. If we made a mistake we'll not 13132176cfdSRui Paulo * find the AP on the new channel and fall back to a 13232176cfdSRui Paulo * normal scan. 13332176cfdSRui Paulo */ 13432176cfdSRui Paulo ieee80211_csa_completeswitch(ic); 13532176cfdSRui Paulo return; 13632176cfdSRui Paulo } 13732176cfdSRui Paulo if (++vap->iv_bmiss_count < vap->iv_bmiss_max) { 13832176cfdSRui Paulo /* 13932176cfdSRui Paulo * Send a directed probe req before falling back to a 14032176cfdSRui Paulo * scan; if we receive a response ic_bmiss_count will 14132176cfdSRui Paulo * be reset. Some cards mistakenly report beacon miss 14232176cfdSRui Paulo * so this avoids the expensive scan if the ap is 14332176cfdSRui Paulo * still there. 14432176cfdSRui Paulo */ 14532176cfdSRui Paulo ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr, 14632176cfdSRui Paulo vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid, 14732176cfdSRui Paulo vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen); 14832176cfdSRui Paulo return; 14932176cfdSRui Paulo } 150085ff963SMatthew Dillon 151085ff963SMatthew Dillon callout_stop(&vap->iv_swbmiss); 15232176cfdSRui Paulo vap->iv_bmiss_count = 0; 15332176cfdSRui Paulo vap->iv_stats.is_beacon_miss++; 15432176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { 15532176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 15632176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 15732176cfdSRui Paulo 15832176cfdSRui Paulo /* 15932176cfdSRui Paulo * If we receive a beacon miss interrupt when using 16032176cfdSRui Paulo * dynamic turbo, attempt to switch modes before 16132176cfdSRui Paulo * reassociating. 16232176cfdSRui Paulo */ 16332176cfdSRui Paulo if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP)) 16432176cfdSRui Paulo ieee80211_dturbo_switch(vap, 16532176cfdSRui Paulo ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO); 16632176cfdSRui Paulo #endif 16732176cfdSRui Paulo /* 16832176cfdSRui Paulo * Try to reassociate before scanning for a new ap. 16932176cfdSRui Paulo */ 17032176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1); 17132176cfdSRui Paulo } else { 17232176cfdSRui Paulo /* 17332176cfdSRui Paulo * Somebody else is controlling state changes (e.g. 17432176cfdSRui Paulo * a user-mode app) don't do anything that would 17532176cfdSRui Paulo * confuse them; just drop into scan mode so they'll 17632176cfdSRui Paulo * notified of the state change and given control. 17732176cfdSRui Paulo */ 17832176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); 17932176cfdSRui Paulo } 18032176cfdSRui Paulo } 18132176cfdSRui Paulo 18232176cfdSRui Paulo /* 18332176cfdSRui Paulo * Handle deauth with reason. We retry only for 18432176cfdSRui Paulo * the cases where we might succeed. Otherwise 18532176cfdSRui Paulo * we downgrade the ap and scan. 18632176cfdSRui Paulo */ 18732176cfdSRui Paulo static void 18832176cfdSRui Paulo sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason) 18932176cfdSRui Paulo { 19032176cfdSRui Paulo switch (reason) { 19132176cfdSRui Paulo case IEEE80211_STATUS_SUCCESS: /* NB: MLME assoc */ 19232176cfdSRui Paulo case IEEE80211_STATUS_TIMEOUT: 19332176cfdSRui Paulo case IEEE80211_REASON_ASSOC_EXPIRE: 19432176cfdSRui Paulo case IEEE80211_REASON_NOT_AUTHED: 19532176cfdSRui Paulo case IEEE80211_REASON_NOT_ASSOCED: 19632176cfdSRui Paulo case IEEE80211_REASON_ASSOC_LEAVE: 19732176cfdSRui Paulo case IEEE80211_REASON_ASSOC_NOT_AUTHED: 19832176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); 19932176cfdSRui Paulo break; 20032176cfdSRui Paulo default: 20132176cfdSRui Paulo ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason); 20232176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) 20332176cfdSRui Paulo ieee80211_check_scan_current(vap); 20432176cfdSRui Paulo break; 20532176cfdSRui Paulo } 20632176cfdSRui Paulo } 20732176cfdSRui Paulo 20832176cfdSRui Paulo /* 20932176cfdSRui Paulo * IEEE80211_M_STA vap state machine handler. 21032176cfdSRui Paulo * This routine handles the main states in the 802.11 protocol. 21132176cfdSRui Paulo */ 21232176cfdSRui Paulo static int 21332176cfdSRui Paulo sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 21432176cfdSRui Paulo { 21532176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 21632176cfdSRui Paulo struct ieee80211_node *ni; 21732176cfdSRui Paulo enum ieee80211_state ostate; 218085ff963SMatthew Dillon 219085ff963SMatthew Dillon IEEE80211_LOCK_ASSERT(ic); 220085ff963SMatthew Dillon 22132176cfdSRui Paulo ostate = vap->iv_state; 22232176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 22332176cfdSRui Paulo __func__, ieee80211_state_name[ostate], 22432176cfdSRui Paulo ieee80211_state_name[nstate], arg); 22532176cfdSRui Paulo vap->iv_state = nstate; /* state transition */ 226085ff963SMatthew Dillon callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */ 22732176cfdSRui Paulo if (ostate != IEEE80211_S_SCAN) 22832176cfdSRui Paulo ieee80211_cancel_scan(vap); /* background scan */ 22932176cfdSRui Paulo ni = vap->iv_bss; /* NB: no reference held */ 23032176cfdSRui Paulo if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) 23132176cfdSRui Paulo callout_stop(&vap->iv_swbmiss); 23232176cfdSRui Paulo switch (nstate) { 23332176cfdSRui Paulo case IEEE80211_S_INIT: 23432176cfdSRui Paulo switch (ostate) { 23532176cfdSRui Paulo case IEEE80211_S_SLEEP: 23632176cfdSRui Paulo /* XXX wakeup */ 237d98a0bcfSMatthew Dillon /* XXX driver hook to wakeup the hardware? */ 23832176cfdSRui Paulo case IEEE80211_S_RUN: 23932176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, 24032176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_DISASSOC, 24132176cfdSRui Paulo IEEE80211_REASON_ASSOC_LEAVE); 24232176cfdSRui Paulo ieee80211_sta_leave(ni); 24332176cfdSRui Paulo break; 24432176cfdSRui Paulo case IEEE80211_S_ASSOC: 24532176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, 24632176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_DEAUTH, 24732176cfdSRui Paulo IEEE80211_REASON_AUTH_LEAVE); 24832176cfdSRui Paulo break; 24932176cfdSRui Paulo case IEEE80211_S_SCAN: 25032176cfdSRui Paulo ieee80211_cancel_scan(vap); 25132176cfdSRui Paulo break; 25232176cfdSRui Paulo default: 25332176cfdSRui Paulo goto invalid; 25432176cfdSRui Paulo } 25532176cfdSRui Paulo if (ostate != IEEE80211_S_INIT) { 25632176cfdSRui Paulo /* NB: optimize INIT -> INIT case */ 25732176cfdSRui Paulo ieee80211_reset_bss(vap); 25832176cfdSRui Paulo } 25932176cfdSRui Paulo if (vap->iv_auth->ia_detach != NULL) 26032176cfdSRui Paulo vap->iv_auth->ia_detach(vap); 26132176cfdSRui Paulo break; 26232176cfdSRui Paulo case IEEE80211_S_SCAN: 26332176cfdSRui Paulo switch (ostate) { 26432176cfdSRui Paulo case IEEE80211_S_INIT: 26532176cfdSRui Paulo /* 26632176cfdSRui Paulo * Initiate a scan. We can come here as a result 26732176cfdSRui Paulo * of an IEEE80211_IOC_SCAN_REQ too in which case 26832176cfdSRui Paulo * the vap will be marked with IEEE80211_FEXT_SCANREQ 26932176cfdSRui Paulo * and the scan request parameters will be present 27032176cfdSRui Paulo * in iv_scanreq. Otherwise we do the default. 27132176cfdSRui Paulo */ 27232176cfdSRui Paulo if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 27332176cfdSRui Paulo ieee80211_check_scan(vap, 27432176cfdSRui Paulo vap->iv_scanreq_flags, 27532176cfdSRui Paulo vap->iv_scanreq_duration, 27632176cfdSRui Paulo vap->iv_scanreq_mindwell, 27732176cfdSRui Paulo vap->iv_scanreq_maxdwell, 27832176cfdSRui Paulo vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 27932176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 28032176cfdSRui Paulo } else 28132176cfdSRui Paulo ieee80211_check_scan_current(vap); 28232176cfdSRui Paulo break; 28332176cfdSRui Paulo case IEEE80211_S_SCAN: 28432176cfdSRui Paulo case IEEE80211_S_AUTH: 28532176cfdSRui Paulo case IEEE80211_S_ASSOC: 28632176cfdSRui Paulo /* 28732176cfdSRui Paulo * These can happen either because of a timeout 28832176cfdSRui Paulo * on an assoc/auth response or because of a 28932176cfdSRui Paulo * change in state that requires a reset. For 29032176cfdSRui Paulo * the former we're called with a non-zero arg 29132176cfdSRui Paulo * that is the cause for the failure; pass this 29232176cfdSRui Paulo * to the scan code so it can update state. 29332176cfdSRui Paulo * Otherwise trigger a new scan unless we're in 29432176cfdSRui Paulo * manual roaming mode in which case an application 29532176cfdSRui Paulo * must issue an explicit scan request. 29632176cfdSRui Paulo */ 29732176cfdSRui Paulo if (arg != 0) 29832176cfdSRui Paulo ieee80211_scan_assoc_fail(vap, 29932176cfdSRui Paulo vap->iv_bss->ni_macaddr, arg); 30032176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) 30132176cfdSRui Paulo ieee80211_check_scan_current(vap); 30232176cfdSRui Paulo break; 303d98a0bcfSMatthew Dillon case IEEE80211_S_SLEEP: /* beacon miss */ 304d98a0bcfSMatthew Dillon /* 305d98a0bcfSMatthew Dillon * XXX if in sleep we need to wakeup the hardware. 306d98a0bcfSMatthew Dillon */ 307d98a0bcfSMatthew Dillon /* FALLTHROUGH */ 30832176cfdSRui Paulo case IEEE80211_S_RUN: /* beacon miss */ 30932176cfdSRui Paulo /* 31032176cfdSRui Paulo * Beacon miss. Notify user space and if not 31132176cfdSRui Paulo * under control of a user application (roaming 31232176cfdSRui Paulo * manual) kick off a scan to re-connect. 31332176cfdSRui Paulo */ 314d98a0bcfSMatthew Dillon 31532176cfdSRui Paulo ieee80211_sta_leave(ni); 31632176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) 31732176cfdSRui Paulo ieee80211_check_scan_current(vap); 31832176cfdSRui Paulo break; 31932176cfdSRui Paulo default: 32032176cfdSRui Paulo goto invalid; 32132176cfdSRui Paulo } 32232176cfdSRui Paulo break; 32332176cfdSRui Paulo case IEEE80211_S_AUTH: 32432176cfdSRui Paulo switch (ostate) { 32532176cfdSRui Paulo case IEEE80211_S_INIT: 32632176cfdSRui Paulo case IEEE80211_S_SCAN: 32732176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, 32832176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 1); 32932176cfdSRui Paulo break; 33032176cfdSRui Paulo case IEEE80211_S_AUTH: 33132176cfdSRui Paulo case IEEE80211_S_ASSOC: 33232176cfdSRui Paulo switch (arg & 0xff) { 33332176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_AUTH: 33432176cfdSRui Paulo /* ??? */ 33532176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, 33632176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 2); 33732176cfdSRui Paulo break; 33832176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_DEAUTH: 33932176cfdSRui Paulo sta_authretry(vap, ni, arg>>8); 34032176cfdSRui Paulo break; 34132176cfdSRui Paulo } 34232176cfdSRui Paulo break; 34332176cfdSRui Paulo case IEEE80211_S_RUN: 34432176cfdSRui Paulo switch (arg & 0xff) { 34532176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_AUTH: 34632176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, 34732176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 2); 34832176cfdSRui Paulo vap->iv_state = ostate; /* stay RUN */ 34932176cfdSRui Paulo break; 35032176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_DEAUTH: 35132176cfdSRui Paulo ieee80211_sta_leave(ni); 35232176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { 35332176cfdSRui Paulo /* try to reauth */ 35432176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, 35532176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 1); 35632176cfdSRui Paulo } 35732176cfdSRui Paulo break; 35832176cfdSRui Paulo } 35932176cfdSRui Paulo break; 36032176cfdSRui Paulo default: 36132176cfdSRui Paulo goto invalid; 36232176cfdSRui Paulo } 36332176cfdSRui Paulo break; 36432176cfdSRui Paulo case IEEE80211_S_ASSOC: 36532176cfdSRui Paulo switch (ostate) { 36632176cfdSRui Paulo case IEEE80211_S_AUTH: 36732176cfdSRui Paulo case IEEE80211_S_ASSOC: 36832176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, 36932176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 37032176cfdSRui Paulo break; 37132176cfdSRui Paulo case IEEE80211_S_SLEEP: /* cannot happen */ 37232176cfdSRui Paulo case IEEE80211_S_RUN: 37332176cfdSRui Paulo ieee80211_sta_leave(ni); 37432176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { 37532176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, arg ? 37632176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_REASSOC_REQ : 37732176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 37832176cfdSRui Paulo } 37932176cfdSRui Paulo break; 38032176cfdSRui Paulo default: 38132176cfdSRui Paulo goto invalid; 38232176cfdSRui Paulo } 38332176cfdSRui Paulo break; 38432176cfdSRui Paulo case IEEE80211_S_RUN: 38532176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_WPA) { 38632176cfdSRui Paulo /* XXX validate prerequisites */ 38732176cfdSRui Paulo } 38832176cfdSRui Paulo switch (ostate) { 38932176cfdSRui Paulo case IEEE80211_S_RUN: 39032176cfdSRui Paulo case IEEE80211_S_CSA: 39132176cfdSRui Paulo break; 39232176cfdSRui Paulo case IEEE80211_S_AUTH: /* when join is done in fw */ 39332176cfdSRui Paulo case IEEE80211_S_ASSOC: 39432176cfdSRui Paulo #ifdef IEEE80211_DEBUG 39532176cfdSRui Paulo if (ieee80211_msg_debug(vap)) { 3961e290df3SAntonio Huete Jimenez ieee80211_note(vap, "%s with %s ssid ", 39732176cfdSRui Paulo (vap->iv_opmode == IEEE80211_M_STA ? 39832176cfdSRui Paulo "associated" : "synchronized"), 399085ff963SMatthew Dillon ether_sprintf(ni->ni_bssid)); 40032176cfdSRui Paulo ieee80211_print_essid(vap->iv_bss->ni_essid, 40132176cfdSRui Paulo ni->ni_esslen); 40232176cfdSRui Paulo /* XXX MCS/HT */ 4036168f72eSRui Paulo kprintf(" channel %d start %uMb\n", 40432176cfdSRui Paulo ieee80211_chan2ieee(ic, ic->ic_curchan), 40532176cfdSRui Paulo IEEE80211_RATE2MBS(ni->ni_txrate)); 40632176cfdSRui Paulo } 40732176cfdSRui Paulo #endif 40832176cfdSRui Paulo ieee80211_scan_assoc_success(vap, ni->ni_macaddr); 40932176cfdSRui Paulo ieee80211_notify_node_join(ni, 41032176cfdSRui Paulo arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 41132176cfdSRui Paulo break; 41232176cfdSRui Paulo case IEEE80211_S_SLEEP: 413d98a0bcfSMatthew Dillon /* Wake up from sleep */ 414d98a0bcfSMatthew Dillon vap->iv_sta_ps(vap, 0); 41532176cfdSRui Paulo break; 41632176cfdSRui Paulo default: 41732176cfdSRui Paulo goto invalid; 41832176cfdSRui Paulo } 41932176cfdSRui Paulo ieee80211_sync_curchan(ic); 42032176cfdSRui Paulo if (ostate != IEEE80211_S_RUN && 42132176cfdSRui Paulo (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) { 42232176cfdSRui Paulo /* 42332176cfdSRui Paulo * Start s/w beacon miss timer for devices w/o 42432176cfdSRui Paulo * hardware support. We fudge a bit here since 42532176cfdSRui Paulo * we're doing this in software. 42632176cfdSRui Paulo */ 42732176cfdSRui Paulo vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( 42832176cfdSRui Paulo 2 * vap->iv_bmissthreshold * ni->ni_intval); 42932176cfdSRui Paulo vap->iv_swbmiss_count = 0; 43032176cfdSRui Paulo callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, 431085ff963SMatthew Dillon ieee80211_swbmiss, vap); 43232176cfdSRui Paulo } 43332176cfdSRui Paulo /* 43432176cfdSRui Paulo * When 802.1x is not in use mark the port authorized 43532176cfdSRui Paulo * at this point so traffic can flow. 43632176cfdSRui Paulo */ 43732176cfdSRui Paulo if (ni->ni_authmode != IEEE80211_AUTH_8021X) 43832176cfdSRui Paulo ieee80211_node_authorize(ni); 43932176cfdSRui Paulo /* 44032176cfdSRui Paulo * Fake association when joining an existing bss. 441d98a0bcfSMatthew Dillon * 442d98a0bcfSMatthew Dillon * Don't do this if we're doing SLEEP->RUN. 44332176cfdSRui Paulo */ 444d98a0bcfSMatthew Dillon if (ic->ic_newassoc != NULL && ostate != IEEE80211_S_SLEEP) 445d98a0bcfSMatthew Dillon ic->ic_newassoc(vap->iv_bss, (ostate != IEEE80211_S_RUN)); 44632176cfdSRui Paulo break; 44732176cfdSRui Paulo case IEEE80211_S_CSA: 44832176cfdSRui Paulo if (ostate != IEEE80211_S_RUN) 44932176cfdSRui Paulo goto invalid; 45032176cfdSRui Paulo break; 45132176cfdSRui Paulo case IEEE80211_S_SLEEP: 452085ff963SMatthew Dillon vap->iv_sta_ps(vap, 1); 45332176cfdSRui Paulo break; 45432176cfdSRui Paulo default: 45532176cfdSRui Paulo invalid: 45632176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 45732176cfdSRui Paulo "%s: unexpected state transition %s -> %s\n", __func__, 45832176cfdSRui Paulo ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 45932176cfdSRui Paulo break; 46032176cfdSRui Paulo } 46132176cfdSRui Paulo return 0; 46232176cfdSRui Paulo } 46332176cfdSRui Paulo 46432176cfdSRui Paulo /* 46532176cfdSRui Paulo * Return non-zero if the frame is an echo of a multicast 46632176cfdSRui Paulo * frame sent by ourself. The dir is known to be DSTODS. 46732176cfdSRui Paulo */ 46832176cfdSRui Paulo static __inline int 46932176cfdSRui Paulo isdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh) 47032176cfdSRui Paulo { 47132176cfdSRui Paulo #define QWH4(wh) ((const struct ieee80211_qosframe_addr4 *)wh) 47232176cfdSRui Paulo #define WH4(wh) ((const struct ieee80211_frame_addr4 *)wh) 47332176cfdSRui Paulo const uint8_t *sa; 47432176cfdSRui Paulo 47532176cfdSRui Paulo KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode")); 47632176cfdSRui Paulo 47732176cfdSRui Paulo if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) 47832176cfdSRui Paulo return 0; 47932176cfdSRui Paulo sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4; 48032176cfdSRui Paulo return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr); 48132176cfdSRui Paulo #undef WH4 48232176cfdSRui Paulo #undef QWH4 48332176cfdSRui Paulo } 48432176cfdSRui Paulo 48532176cfdSRui Paulo /* 48632176cfdSRui Paulo * Return non-zero if the frame is an echo of a multicast 48732176cfdSRui Paulo * frame sent by ourself. The dir is known to be FROMDS. 48832176cfdSRui Paulo */ 48932176cfdSRui Paulo static __inline int 49032176cfdSRui Paulo isfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh) 49132176cfdSRui Paulo { 49232176cfdSRui Paulo KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode")); 49332176cfdSRui Paulo 49432176cfdSRui Paulo if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) 49532176cfdSRui Paulo return 0; 49632176cfdSRui Paulo return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr); 49732176cfdSRui Paulo } 49832176cfdSRui Paulo 49932176cfdSRui Paulo /* 50032176cfdSRui Paulo * Decide if a received management frame should be 50132176cfdSRui Paulo * printed when debugging is enabled. This filters some 50232176cfdSRui Paulo * of the less interesting frames that come frequently 50332176cfdSRui Paulo * (e.g. beacons). 50432176cfdSRui Paulo */ 50532176cfdSRui Paulo static __inline int 50632176cfdSRui Paulo doprint(struct ieee80211vap *vap, int subtype) 50732176cfdSRui Paulo { 50832176cfdSRui Paulo switch (subtype) { 50932176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_BEACON: 51032176cfdSRui Paulo return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); 51132176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 51232176cfdSRui Paulo return 0; 51332176cfdSRui Paulo } 51432176cfdSRui Paulo return 1; 51532176cfdSRui Paulo } 51632176cfdSRui Paulo 51732176cfdSRui Paulo /* 51832176cfdSRui Paulo * Process a received frame. The node associated with the sender 51932176cfdSRui Paulo * should be supplied. If nothing was found in the node table then 52032176cfdSRui Paulo * the caller is assumed to supply a reference to iv_bss instead. 52132176cfdSRui Paulo * The RSSI and a timestamp are also supplied. The RSSI data is used 52232176cfdSRui Paulo * during AP scanning to select a AP to associate with; it can have 52332176cfdSRui Paulo * any units so long as values have consistent units and higher values 52432176cfdSRui Paulo * mean ``better signal''. The receive timestamp is currently not used 52532176cfdSRui Paulo * by the 802.11 layer. 52632176cfdSRui Paulo */ 52732176cfdSRui Paulo static int 52832176cfdSRui Paulo sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) 52932176cfdSRui Paulo { 53032176cfdSRui Paulo #define HAS_SEQ(type) ((type & 0x4) == 0) 53132176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 53232176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic; 53332176cfdSRui Paulo struct ifnet *ifp = vap->iv_ifp; 53432176cfdSRui Paulo struct ieee80211_frame *wh; 53532176cfdSRui Paulo struct ieee80211_key *key; 53632176cfdSRui Paulo struct ether_header *eh; 53732176cfdSRui Paulo int hdrspace, need_tap = 1; /* mbuf need to be tapped. */ 53832176cfdSRui Paulo uint8_t dir, type, subtype, qos; 53932176cfdSRui Paulo uint8_t *bssid; 54032176cfdSRui Paulo uint16_t rxseq; 54132176cfdSRui Paulo 54232176cfdSRui Paulo if (m->m_flags & M_AMPDU_MPDU) { 54332176cfdSRui Paulo /* 54432176cfdSRui Paulo * Fastpath for A-MPDU reorder q resubmission. Frames 54532176cfdSRui Paulo * w/ M_AMPDU_MPDU marked have already passed through 54632176cfdSRui Paulo * here but were received out of order and been held on 54732176cfdSRui Paulo * the reorder queue. When resubmitted they are marked 54832176cfdSRui Paulo * with the M_AMPDU_MPDU flag and we can bypass most of 54932176cfdSRui Paulo * the normal processing. 55032176cfdSRui Paulo */ 55132176cfdSRui Paulo wh = mtod(m, struct ieee80211_frame *); 55232176cfdSRui Paulo type = IEEE80211_FC0_TYPE_DATA; 55332176cfdSRui Paulo dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 55432176cfdSRui Paulo subtype = IEEE80211_FC0_SUBTYPE_QOS; 55532176cfdSRui Paulo hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ 55632176cfdSRui Paulo goto resubmit_ampdu; 55732176cfdSRui Paulo } 55832176cfdSRui Paulo 55932176cfdSRui Paulo KASSERT(ni != NULL, ("null node")); 56032176cfdSRui Paulo ni->ni_inact = ni->ni_inact_reload; 56132176cfdSRui Paulo 56232176cfdSRui Paulo type = -1; /* undefined */ 56332176cfdSRui Paulo 56432176cfdSRui Paulo if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { 56532176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 56632176cfdSRui Paulo ni->ni_macaddr, NULL, 56732176cfdSRui Paulo "too short (1): len %u", m->m_pkthdr.len); 56832176cfdSRui Paulo vap->iv_stats.is_rx_tooshort++; 56932176cfdSRui Paulo goto out; 57032176cfdSRui Paulo } 57132176cfdSRui Paulo /* 57232176cfdSRui Paulo * Bit of a cheat here, we use a pointer for a 3-address 57332176cfdSRui Paulo * frame format but don't reference fields past outside 57432176cfdSRui Paulo * ieee80211_frame_min w/o first validating the data is 57532176cfdSRui Paulo * present. 57632176cfdSRui Paulo */ 57732176cfdSRui Paulo wh = mtod(m, struct ieee80211_frame *); 57832176cfdSRui Paulo 57932176cfdSRui Paulo if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 58032176cfdSRui Paulo IEEE80211_FC0_VERSION_0) { 58132176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 58232176cfdSRui Paulo ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x", 58332176cfdSRui Paulo wh->i_fc[0], wh->i_fc[1]); 58432176cfdSRui Paulo vap->iv_stats.is_rx_badversion++; 58532176cfdSRui Paulo goto err; 58632176cfdSRui Paulo } 58732176cfdSRui Paulo 58832176cfdSRui Paulo dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 58932176cfdSRui Paulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 59032176cfdSRui Paulo subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 59132176cfdSRui Paulo if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 59232176cfdSRui Paulo bssid = wh->i_addr2; 59332176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { 59432176cfdSRui Paulo /* not interested in */ 59532176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 59632176cfdSRui Paulo bssid, NULL, "%s", "not to bss"); 59732176cfdSRui Paulo vap->iv_stats.is_rx_wrongbss++; 59832176cfdSRui Paulo goto out; 59932176cfdSRui Paulo } 600085ff963SMatthew Dillon 601085ff963SMatthew Dillon /* 602085ff963SMatthew Dillon * Some devices may be in a promiscuous mode 603085ff963SMatthew Dillon * where they receive frames for multiple station 604085ff963SMatthew Dillon * addresses. 605085ff963SMatthew Dillon * 606085ff963SMatthew Dillon * If we receive a data frame that isn't 607085ff963SMatthew Dillon * destined to our VAP MAC, drop it. 608085ff963SMatthew Dillon * 609085ff963SMatthew Dillon * XXX TODO: This is only enforced when not scanning; 610085ff963SMatthew Dillon * XXX it assumes a software-driven scan will put the NIC 611085ff963SMatthew Dillon * XXX into a "no data frames" mode before setting this 612085ff963SMatthew Dillon * XXX flag. Otherwise it may be possible that we'll still 613085ff963SMatthew Dillon * XXX process data frames whilst scanning. 614085ff963SMatthew Dillon */ 615085ff963SMatthew Dillon if ((! IEEE80211_IS_MULTICAST(wh->i_addr1)) 616085ff963SMatthew Dillon && (! IEEE80211_ADDR_EQ(wh->i_addr1, IF_LLADDR(ifp)))) { 617085ff963SMatthew Dillon IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 618085ff963SMatthew Dillon bssid, NULL, "not to cur sta: lladdr=%6D, addr1=%6D", 619085ff963SMatthew Dillon IF_LLADDR(ifp), ":", wh->i_addr1, ":"); 620085ff963SMatthew Dillon vap->iv_stats.is_rx_wrongbss++; 621085ff963SMatthew Dillon goto out; 622085ff963SMatthew Dillon } 623085ff963SMatthew Dillon 62432176cfdSRui Paulo IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 62532176cfdSRui Paulo ni->ni_noise = nf; 62636e4ebd1SJoe Talbott if (HAS_SEQ(type) && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { 62732176cfdSRui Paulo uint8_t tid = ieee80211_gettid(wh); 62832176cfdSRui Paulo if (IEEE80211_QOS_HAS_SEQ(wh) && 62932176cfdSRui Paulo TID_TO_WME_AC(tid) >= WME_AC_VI) 63032176cfdSRui Paulo ic->ic_wme.wme_hipri_traffic++; 63132176cfdSRui Paulo rxseq = le16toh(*(uint16_t *)wh->i_seq); 632085ff963SMatthew Dillon if (! ieee80211_check_rxseq(ni, wh)) { 63332176cfdSRui Paulo /* duplicate, discard */ 63432176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 63532176cfdSRui Paulo bssid, "duplicate", 63632176cfdSRui Paulo "seqno <%u,%u> fragno <%u,%u> tid %u", 63732176cfdSRui Paulo rxseq >> IEEE80211_SEQ_SEQ_SHIFT, 63832176cfdSRui Paulo ni->ni_rxseqs[tid] >> 63932176cfdSRui Paulo IEEE80211_SEQ_SEQ_SHIFT, 64032176cfdSRui Paulo rxseq & IEEE80211_SEQ_FRAG_MASK, 64132176cfdSRui Paulo ni->ni_rxseqs[tid] & 64232176cfdSRui Paulo IEEE80211_SEQ_FRAG_MASK, 64332176cfdSRui Paulo tid); 64432176cfdSRui Paulo vap->iv_stats.is_rx_dup++; 64532176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_dup); 64632176cfdSRui Paulo goto out; 64732176cfdSRui Paulo } 64832176cfdSRui Paulo ni->ni_rxseqs[tid] = rxseq; 64932176cfdSRui Paulo } 65032176cfdSRui Paulo } 65132176cfdSRui Paulo 65232176cfdSRui Paulo switch (type) { 65332176cfdSRui Paulo case IEEE80211_FC0_TYPE_DATA: 65432176cfdSRui Paulo hdrspace = ieee80211_hdrspace(ic, wh); 65532176cfdSRui Paulo if (m->m_len < hdrspace && 65632176cfdSRui Paulo (m = m_pullup(m, hdrspace)) == NULL) { 65732176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 65832176cfdSRui Paulo ni->ni_macaddr, NULL, 65932176cfdSRui Paulo "data too short: expecting %u", hdrspace); 66032176cfdSRui Paulo vap->iv_stats.is_rx_tooshort++; 66132176cfdSRui Paulo goto out; /* XXX */ 66232176cfdSRui Paulo } 66332176cfdSRui Paulo /* 66432176cfdSRui Paulo * Handle A-MPDU re-ordering. If the frame is to be 66532176cfdSRui Paulo * processed directly then ieee80211_ampdu_reorder 66632176cfdSRui Paulo * will return 0; otherwise it has consumed the mbuf 66732176cfdSRui Paulo * and we should do nothing more with it. 66832176cfdSRui Paulo */ 66932176cfdSRui Paulo if ((m->m_flags & M_AMPDU) && 67032176cfdSRui Paulo (dir == IEEE80211_FC1_DIR_FROMDS || 67132176cfdSRui Paulo dir == IEEE80211_FC1_DIR_DSTODS) && 67232176cfdSRui Paulo ieee80211_ampdu_reorder(ni, m) != 0) { 67332176cfdSRui Paulo m = NULL; 67432176cfdSRui Paulo goto out; 67532176cfdSRui Paulo } 67632176cfdSRui Paulo resubmit_ampdu: 67732176cfdSRui Paulo if (dir == IEEE80211_FC1_DIR_FROMDS) { 67832176cfdSRui Paulo if ((ifp->if_flags & IFF_SIMPLEX) && 67932176cfdSRui Paulo isfromds_mcastecho(vap, wh)) { 68032176cfdSRui Paulo /* 68132176cfdSRui Paulo * In IEEE802.11 network, multicast 68232176cfdSRui Paulo * packets sent from "me" are broadcast 68332176cfdSRui Paulo * from the AP; silently discard for 68432176cfdSRui Paulo * SIMPLEX interface. 68532176cfdSRui Paulo */ 68632176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 68732176cfdSRui Paulo wh, "data", "%s", "multicast echo"); 68832176cfdSRui Paulo vap->iv_stats.is_rx_mcastecho++; 68932176cfdSRui Paulo goto out; 69032176cfdSRui Paulo } 69132176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_DWDS) && 69232176cfdSRui Paulo IEEE80211_IS_MULTICAST(wh->i_addr1)) { 69332176cfdSRui Paulo /* 69432176cfdSRui Paulo * DWDS sta's must drop 3-address mcast frames 69532176cfdSRui Paulo * as they will be sent separately as a 4-addr 69632176cfdSRui Paulo * frame. Accepting the 3-addr frame will 69732176cfdSRui Paulo * confuse the bridge into thinking the sending 69832176cfdSRui Paulo * sta is located at the end of WDS link. 69932176cfdSRui Paulo */ 70032176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, 70132176cfdSRui Paulo "3-address data", "%s", "DWDS enabled"); 70232176cfdSRui Paulo vap->iv_stats.is_rx_mcastecho++; 70332176cfdSRui Paulo goto out; 70432176cfdSRui Paulo } 70532176cfdSRui Paulo } else if (dir == IEEE80211_FC1_DIR_DSTODS) { 70632176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) { 70732176cfdSRui Paulo IEEE80211_DISCARD(vap, 70832176cfdSRui Paulo IEEE80211_MSG_INPUT, wh, "4-address data", 70932176cfdSRui Paulo "%s", "DWDS not enabled"); 71032176cfdSRui Paulo vap->iv_stats.is_rx_wrongdir++; 71132176cfdSRui Paulo goto out; 71232176cfdSRui Paulo } 71332176cfdSRui Paulo if ((ifp->if_flags & IFF_SIMPLEX) && 71432176cfdSRui Paulo isdstods_mcastecho(vap, wh)) { 71532176cfdSRui Paulo /* 71632176cfdSRui Paulo * In IEEE802.11 network, multicast 71732176cfdSRui Paulo * packets sent from "me" are broadcast 71832176cfdSRui Paulo * from the AP; silently discard for 71932176cfdSRui Paulo * SIMPLEX interface. 72032176cfdSRui Paulo */ 72132176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, 72232176cfdSRui Paulo "4-address data", "%s", "multicast echo"); 72332176cfdSRui Paulo vap->iv_stats.is_rx_mcastecho++; 72432176cfdSRui Paulo goto out; 72532176cfdSRui Paulo } 72632176cfdSRui Paulo } else { 72732176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, 72832176cfdSRui Paulo "data", "incorrect dir 0x%x", dir); 72932176cfdSRui Paulo vap->iv_stats.is_rx_wrongdir++; 73032176cfdSRui Paulo goto out; 73132176cfdSRui Paulo } 73232176cfdSRui Paulo 73332176cfdSRui Paulo /* 73432176cfdSRui Paulo * Handle privacy requirements. Note that we 73532176cfdSRui Paulo * must not be preempted from here until after 73632176cfdSRui Paulo * we (potentially) call ieee80211_crypto_demic; 73732176cfdSRui Paulo * otherwise we may violate assumptions in the 73832176cfdSRui Paulo * crypto cipher modules used to do delayed update 73932176cfdSRui Paulo * of replay sequence numbers. 74032176cfdSRui Paulo */ 741085ff963SMatthew Dillon if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 74232176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 74332176cfdSRui Paulo /* 74432176cfdSRui Paulo * Discard encrypted frames when privacy is off. 74532176cfdSRui Paulo */ 74632176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 74732176cfdSRui Paulo wh, "WEP", "%s", "PRIVACY off"); 74832176cfdSRui Paulo vap->iv_stats.is_rx_noprivacy++; 74932176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_noprivacy); 75032176cfdSRui Paulo goto out; 75132176cfdSRui Paulo } 75232176cfdSRui Paulo key = ieee80211_crypto_decap(ni, m, hdrspace); 75332176cfdSRui Paulo if (key == NULL) { 75432176cfdSRui Paulo /* NB: stats+msgs handled in crypto_decap */ 75532176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_wepfail); 75632176cfdSRui Paulo goto out; 75732176cfdSRui Paulo } 75832176cfdSRui Paulo wh = mtod(m, struct ieee80211_frame *); 759085ff963SMatthew Dillon wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; 76032176cfdSRui Paulo } else { 76132176cfdSRui Paulo /* XXX M_WEP and IEEE80211_F_PRIVACY */ 76232176cfdSRui Paulo key = NULL; 76332176cfdSRui Paulo } 76432176cfdSRui Paulo 76532176cfdSRui Paulo /* 76632176cfdSRui Paulo * Save QoS bits for use below--before we strip the header. 76732176cfdSRui Paulo */ 76832176cfdSRui Paulo if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { 76932176cfdSRui Paulo qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? 77032176cfdSRui Paulo ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : 77132176cfdSRui Paulo ((struct ieee80211_qosframe *)wh)->i_qos[0]; 77232176cfdSRui Paulo } else 77332176cfdSRui Paulo qos = 0; 77432176cfdSRui Paulo 77532176cfdSRui Paulo /* 77632176cfdSRui Paulo * Next up, any fragmentation. 77732176cfdSRui Paulo */ 77832176cfdSRui Paulo if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 77932176cfdSRui Paulo m = ieee80211_defrag(ni, m, hdrspace); 78032176cfdSRui Paulo if (m == NULL) { 78132176cfdSRui Paulo /* Fragment dropped or frame not complete yet */ 78232176cfdSRui Paulo goto out; 78332176cfdSRui Paulo } 78432176cfdSRui Paulo } 78532176cfdSRui Paulo wh = NULL; /* no longer valid, catch any uses */ 78632176cfdSRui Paulo 78732176cfdSRui Paulo /* 78832176cfdSRui Paulo * Next strip any MSDU crypto bits. 78932176cfdSRui Paulo */ 79032176cfdSRui Paulo if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) { 79132176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 79232176cfdSRui Paulo ni->ni_macaddr, "data", "%s", "demic error"); 79332176cfdSRui Paulo vap->iv_stats.is_rx_demicfail++; 79432176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_demicfail); 79532176cfdSRui Paulo goto out; 79632176cfdSRui Paulo } 79732176cfdSRui Paulo 79832176cfdSRui Paulo /* copy to listener after decrypt */ 79932176cfdSRui Paulo if (ieee80211_radiotap_active_vap(vap)) 80032176cfdSRui Paulo ieee80211_radiotap_rx(vap, m); 80132176cfdSRui Paulo need_tap = 0; 80232176cfdSRui Paulo 80332176cfdSRui Paulo /* 80432176cfdSRui Paulo * Finally, strip the 802.11 header. 80532176cfdSRui Paulo */ 80632176cfdSRui Paulo m = ieee80211_decap(vap, m, hdrspace); 80732176cfdSRui Paulo if (m == NULL) { 80832176cfdSRui Paulo /* XXX mask bit to check for both */ 80932176cfdSRui Paulo /* don't count Null data frames as errors */ 81032176cfdSRui Paulo if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || 81132176cfdSRui Paulo subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) 81232176cfdSRui Paulo goto out; 81332176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 81432176cfdSRui Paulo ni->ni_macaddr, "data", "%s", "decap error"); 81532176cfdSRui Paulo vap->iv_stats.is_rx_decap++; 81632176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_decap); 81732176cfdSRui Paulo goto err; 81832176cfdSRui Paulo } 81932176cfdSRui Paulo eh = mtod(m, struct ether_header *); 82032176cfdSRui Paulo if (!ieee80211_node_is_authorized(ni)) { 82132176cfdSRui Paulo /* 82232176cfdSRui Paulo * Deny any non-PAE frames received prior to 82332176cfdSRui Paulo * authorization. For open/shared-key 82432176cfdSRui Paulo * authentication the port is mark authorized 82532176cfdSRui Paulo * after authentication completes. For 802.1x 82632176cfdSRui Paulo * the port is not marked authorized by the 82732176cfdSRui Paulo * authenticator until the handshake has completed. 82832176cfdSRui Paulo */ 82932176cfdSRui Paulo if (eh->ether_type != htons(ETHERTYPE_PAE)) { 83032176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 83132176cfdSRui Paulo eh->ether_shost, "data", 83232176cfdSRui Paulo "unauthorized port: ether type 0x%x len %u", 83332176cfdSRui Paulo eh->ether_type, m->m_pkthdr.len); 83432176cfdSRui Paulo vap->iv_stats.is_rx_unauth++; 83532176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_unauth); 83632176cfdSRui Paulo goto err; 83732176cfdSRui Paulo } 83832176cfdSRui Paulo } else { 83932176cfdSRui Paulo /* 84032176cfdSRui Paulo * When denying unencrypted frames, discard 84132176cfdSRui Paulo * any non-PAE frames received without encryption. 84232176cfdSRui Paulo */ 84332176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && 84432176cfdSRui Paulo (key == NULL && (m->m_flags & M_WEP) == 0) && 84532176cfdSRui Paulo eh->ether_type != htons(ETHERTYPE_PAE)) { 84632176cfdSRui Paulo /* 84732176cfdSRui Paulo * Drop unencrypted frames. 84832176cfdSRui Paulo */ 84932176cfdSRui Paulo vap->iv_stats.is_rx_unencrypted++; 85032176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_unencrypted); 85132176cfdSRui Paulo goto out; 85232176cfdSRui Paulo } 85332176cfdSRui Paulo } 85432176cfdSRui Paulo /* XXX require HT? */ 85532176cfdSRui Paulo if (qos & IEEE80211_QOS_AMSDU) { 85632176cfdSRui Paulo m = ieee80211_decap_amsdu(ni, m); 85732176cfdSRui Paulo if (m == NULL) 85832176cfdSRui Paulo return IEEE80211_FC0_TYPE_DATA; 85932176cfdSRui Paulo } else { 86032176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 86132176cfdSRui Paulo m = ieee80211_decap_fastframe(vap, ni, m); 86232176cfdSRui Paulo if (m == NULL) 86332176cfdSRui Paulo return IEEE80211_FC0_TYPE_DATA; 86432176cfdSRui Paulo #endif 86532176cfdSRui Paulo } 86632176cfdSRui Paulo ieee80211_deliver_data(vap, ni, m); 86732176cfdSRui Paulo return IEEE80211_FC0_TYPE_DATA; 86832176cfdSRui Paulo 86932176cfdSRui Paulo case IEEE80211_FC0_TYPE_MGT: 87032176cfdSRui Paulo vap->iv_stats.is_rx_mgmt++; 87132176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_mgmt); 87232176cfdSRui Paulo if (dir != IEEE80211_FC1_DIR_NODS) { 87332176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 87432176cfdSRui Paulo wh, "data", "incorrect dir 0x%x", dir); 87532176cfdSRui Paulo vap->iv_stats.is_rx_wrongdir++; 87632176cfdSRui Paulo goto err; 87732176cfdSRui Paulo } 87832176cfdSRui Paulo if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 87932176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 88032176cfdSRui Paulo ni->ni_macaddr, "mgt", "too short: len %u", 88132176cfdSRui Paulo m->m_pkthdr.len); 88232176cfdSRui Paulo vap->iv_stats.is_rx_tooshort++; 88332176cfdSRui Paulo goto out; 88432176cfdSRui Paulo } 88532176cfdSRui Paulo #ifdef IEEE80211_DEBUG 88632176cfdSRui Paulo if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || 88732176cfdSRui Paulo ieee80211_msg_dumppkts(vap)) { 8881e290df3SAntonio Huete Jimenez if_printf(ifp, "received %s from %s rssi %d\n", 88932176cfdSRui Paulo ieee80211_mgt_subtype_name[subtype >> 89032176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_SHIFT], 891085ff963SMatthew Dillon ether_sprintf(wh->i_addr2), rssi); 89232176cfdSRui Paulo } 89332176cfdSRui Paulo #endif 894085ff963SMatthew Dillon if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 89532176cfdSRui Paulo if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { 89632176cfdSRui Paulo /* 89732176cfdSRui Paulo * Only shared key auth frames with a challenge 89832176cfdSRui Paulo * should be encrypted, discard all others. 89932176cfdSRui Paulo */ 90032176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 90132176cfdSRui Paulo wh, ieee80211_mgt_subtype_name[subtype >> 90232176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_SHIFT], 90332176cfdSRui Paulo "%s", "WEP set but not permitted"); 90432176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ 90532176cfdSRui Paulo goto out; 90632176cfdSRui Paulo } 90732176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 90832176cfdSRui Paulo /* 90932176cfdSRui Paulo * Discard encrypted frames when privacy is off. 91032176cfdSRui Paulo */ 91132176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 91232176cfdSRui Paulo wh, "mgt", "%s", "WEP set but PRIVACY off"); 91332176cfdSRui Paulo vap->iv_stats.is_rx_noprivacy++; 91432176cfdSRui Paulo goto out; 91532176cfdSRui Paulo } 91632176cfdSRui Paulo hdrspace = ieee80211_hdrspace(ic, wh); 91732176cfdSRui Paulo key = ieee80211_crypto_decap(ni, m, hdrspace); 91832176cfdSRui Paulo if (key == NULL) { 91932176cfdSRui Paulo /* NB: stats+msgs handled in crypto_decap */ 92032176cfdSRui Paulo goto out; 92132176cfdSRui Paulo } 92232176cfdSRui Paulo wh = mtod(m, struct ieee80211_frame *); 923085ff963SMatthew Dillon wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; 92432176cfdSRui Paulo } 92532176cfdSRui Paulo vap->iv_recv_mgmt(ni, m, subtype, rssi, nf); 92632176cfdSRui Paulo goto out; 92732176cfdSRui Paulo 92832176cfdSRui Paulo case IEEE80211_FC0_TYPE_CTL: 92932176cfdSRui Paulo vap->iv_stats.is_rx_ctl++; 93032176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_ctrl); 93132176cfdSRui Paulo vap->iv_recv_ctl(ni, m, subtype); 93232176cfdSRui Paulo goto out; 93332176cfdSRui Paulo 93432176cfdSRui Paulo default: 93532176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 93632176cfdSRui Paulo wh, NULL, "bad frame type 0x%x", type); 93732176cfdSRui Paulo /* should not come here */ 93832176cfdSRui Paulo break; 93932176cfdSRui Paulo } 94032176cfdSRui Paulo err: 941d40991efSSepherosa Ziehau IFNET_STAT_INC(ifp, ierrors, 1); 94232176cfdSRui Paulo out: 94332176cfdSRui Paulo if (m != NULL) { 94432176cfdSRui Paulo if (need_tap && ieee80211_radiotap_active_vap(vap)) 94532176cfdSRui Paulo ieee80211_radiotap_rx(vap, m); 94632176cfdSRui Paulo m_freem(m); 94732176cfdSRui Paulo } 94832176cfdSRui Paulo return type; 94932176cfdSRui Paulo } 95032176cfdSRui Paulo 95132176cfdSRui Paulo static void 95232176cfdSRui Paulo sta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh, 95332176cfdSRui Paulo int rssi, int nf, uint16_t seq, uint16_t status) 95432176cfdSRui Paulo { 95532176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 95632176cfdSRui Paulo 95732176cfdSRui Paulo if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { 95832176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 95932176cfdSRui Paulo ni->ni_macaddr, "open auth", 96032176cfdSRui Paulo "bad sta auth mode %u", ni->ni_authmode); 96132176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++; /* XXX */ 96232176cfdSRui Paulo return; 96332176cfdSRui Paulo } 96432176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_AUTH || 96532176cfdSRui Paulo seq != IEEE80211_AUTH_OPEN_RESPONSE) { 96632176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++; 96732176cfdSRui Paulo return; 96832176cfdSRui Paulo } 96932176cfdSRui Paulo if (status != 0) { 97032176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 97132176cfdSRui Paulo ni, "open auth failed (reason %d)", status); 97232176cfdSRui Paulo vap->iv_stats.is_rx_auth_fail++; 97332176cfdSRui Paulo vap->iv_stats.is_rx_authfail_code = status; 97432176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, 97532176cfdSRui Paulo IEEE80211_SCAN_FAIL_STATUS); 97632176cfdSRui Paulo } else 97732176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); 97832176cfdSRui Paulo } 97932176cfdSRui Paulo 98032176cfdSRui Paulo static void 98132176cfdSRui Paulo sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh, 98232176cfdSRui Paulo uint8_t *frm, uint8_t *efrm, int rssi, int nf, 98332176cfdSRui Paulo uint16_t seq, uint16_t status) 98432176cfdSRui Paulo { 98532176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 98632176cfdSRui Paulo uint8_t *challenge; 987085ff963SMatthew Dillon int estatus; 98832176cfdSRui Paulo 98932176cfdSRui Paulo /* 99032176cfdSRui Paulo * NB: this can happen as we allow pre-shared key 99132176cfdSRui Paulo * authentication to be enabled w/o wep being turned 99232176cfdSRui Paulo * on so that configuration of these can be done 99332176cfdSRui Paulo * in any order. It may be better to enforce the 99432176cfdSRui Paulo * ordering in which case this check would just be 99532176cfdSRui Paulo * for sanity/consistency. 99632176cfdSRui Paulo */ 99732176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 99832176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 99932176cfdSRui Paulo ni->ni_macaddr, "shared key auth", 100032176cfdSRui Paulo "%s", " PRIVACY is disabled"); 1001085ff963SMatthew Dillon estatus = IEEE80211_STATUS_ALG; 100232176cfdSRui Paulo goto bad; 100332176cfdSRui Paulo } 100432176cfdSRui Paulo /* 100532176cfdSRui Paulo * Pre-shared key authentication is evil; accept 100632176cfdSRui Paulo * it only if explicitly configured (it is supported 100732176cfdSRui Paulo * mainly for compatibility with clients like OS X). 100832176cfdSRui Paulo */ 100932176cfdSRui Paulo if (ni->ni_authmode != IEEE80211_AUTH_AUTO && 101032176cfdSRui Paulo ni->ni_authmode != IEEE80211_AUTH_SHARED) { 101132176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 101232176cfdSRui Paulo ni->ni_macaddr, "shared key auth", 101332176cfdSRui Paulo "bad sta auth mode %u", ni->ni_authmode); 101432176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ 1015085ff963SMatthew Dillon estatus = IEEE80211_STATUS_ALG; 101632176cfdSRui Paulo goto bad; 101732176cfdSRui Paulo } 101832176cfdSRui Paulo 101932176cfdSRui Paulo challenge = NULL; 102032176cfdSRui Paulo if (frm + 1 < efrm) { 102132176cfdSRui Paulo if ((frm[1] + 2) > (efrm - frm)) { 102232176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 102332176cfdSRui Paulo ni->ni_macaddr, "shared key auth", 1024*2c7ccc4aSSascha Wildner "ie %d/%ld too long", 1025085ff963SMatthew Dillon frm[0], (frm[1] + 2) - (efrm - frm)); 102632176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++; 1027085ff963SMatthew Dillon estatus = IEEE80211_STATUS_CHALLENGE; 102832176cfdSRui Paulo goto bad; 102932176cfdSRui Paulo } 103032176cfdSRui Paulo if (*frm == IEEE80211_ELEMID_CHALLENGE) 103132176cfdSRui Paulo challenge = frm; 103232176cfdSRui Paulo frm += frm[1] + 2; 103332176cfdSRui Paulo } 103432176cfdSRui Paulo switch (seq) { 103532176cfdSRui Paulo case IEEE80211_AUTH_SHARED_CHALLENGE: 103632176cfdSRui Paulo case IEEE80211_AUTH_SHARED_RESPONSE: 103732176cfdSRui Paulo if (challenge == NULL) { 103832176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 103932176cfdSRui Paulo ni->ni_macaddr, "shared key auth", 104032176cfdSRui Paulo "%s", "no challenge"); 104132176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++; 1042085ff963SMatthew Dillon estatus = IEEE80211_STATUS_CHALLENGE; 104332176cfdSRui Paulo goto bad; 104432176cfdSRui Paulo } 104532176cfdSRui Paulo if (challenge[1] != IEEE80211_CHALLENGE_LEN) { 104632176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 104732176cfdSRui Paulo ni->ni_macaddr, "shared key auth", 104832176cfdSRui Paulo "bad challenge len %d", challenge[1]); 104932176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++; 1050085ff963SMatthew Dillon estatus = IEEE80211_STATUS_CHALLENGE; 105132176cfdSRui Paulo goto bad; 105232176cfdSRui Paulo } 105332176cfdSRui Paulo default: 105432176cfdSRui Paulo break; 105532176cfdSRui Paulo } 105632176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_AUTH) 105732176cfdSRui Paulo return; 105832176cfdSRui Paulo switch (seq) { 105932176cfdSRui Paulo case IEEE80211_AUTH_SHARED_PASS: 106032176cfdSRui Paulo if (ni->ni_challenge != NULL) { 106132176cfdSRui Paulo kfree(ni->ni_challenge, M_80211_NODE); 106232176cfdSRui Paulo ni->ni_challenge = NULL; 106332176cfdSRui Paulo } 106432176cfdSRui Paulo if (status != 0) { 106532176cfdSRui Paulo IEEE80211_NOTE_FRAME(vap, 106632176cfdSRui Paulo IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh, 106732176cfdSRui Paulo "shared key auth failed (reason %d)", status); 106832176cfdSRui Paulo vap->iv_stats.is_rx_auth_fail++; 106932176cfdSRui Paulo vap->iv_stats.is_rx_authfail_code = status; 107032176cfdSRui Paulo return; 107132176cfdSRui Paulo } 107232176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); 107332176cfdSRui Paulo break; 107432176cfdSRui Paulo case IEEE80211_AUTH_SHARED_CHALLENGE: 107532176cfdSRui Paulo if (!ieee80211_alloc_challenge(ni)) 107632176cfdSRui Paulo return; 107732176cfdSRui Paulo /* XXX could optimize by passing recvd challenge */ 107832176cfdSRui Paulo memcpy(ni->ni_challenge, &challenge[2], challenge[1]); 107932176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, 108032176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 108132176cfdSRui Paulo break; 108232176cfdSRui Paulo default: 108332176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH, 108432176cfdSRui Paulo wh, "shared key auth", "bad seq %d", seq); 108532176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++; 108632176cfdSRui Paulo return; 108732176cfdSRui Paulo } 108832176cfdSRui Paulo return; 108932176cfdSRui Paulo bad: 109032176cfdSRui Paulo /* 109132176cfdSRui Paulo * Kick the state machine. This short-circuits 109232176cfdSRui Paulo * using the mgt frame timeout to trigger the 109332176cfdSRui Paulo * state transition. 109432176cfdSRui Paulo */ 109532176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_AUTH) 109632176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, 109732176cfdSRui Paulo IEEE80211_SCAN_FAIL_STATUS); 109832176cfdSRui Paulo } 109932176cfdSRui Paulo 1100085ff963SMatthew Dillon int 110132176cfdSRui Paulo ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm, 110232176cfdSRui Paulo const struct ieee80211_frame *wh) 110332176cfdSRui Paulo { 110432176cfdSRui Paulo #define MS(_v, _f) (((_v) & _f) >> _f##_S) 110532176cfdSRui Paulo struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme; 110632176cfdSRui Paulo u_int len = frm[1], qosinfo; 110732176cfdSRui Paulo int i; 110832176cfdSRui Paulo 110932176cfdSRui Paulo if (len < sizeof(struct ieee80211_wme_param)-2) { 111032176cfdSRui Paulo IEEE80211_DISCARD_IE(vap, 111132176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME, 111232176cfdSRui Paulo wh, "WME", "too short, len %u", len); 111332176cfdSRui Paulo return -1; 111432176cfdSRui Paulo } 111532176cfdSRui Paulo qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)]; 111632176cfdSRui Paulo qosinfo &= WME_QOSINFO_COUNT; 111732176cfdSRui Paulo /* XXX do proper check for wraparound */ 111832176cfdSRui Paulo if (qosinfo == wme->wme_wmeChanParams.cap_info) 111932176cfdSRui Paulo return 0; 112032176cfdSRui Paulo frm += __offsetof(struct ieee80211_wme_param, params_acParams); 112132176cfdSRui Paulo for (i = 0; i < WME_NUM_AC; i++) { 112232176cfdSRui Paulo struct wmeParams *wmep = 112332176cfdSRui Paulo &wme->wme_wmeChanParams.cap_wmeParams[i]; 112432176cfdSRui Paulo /* NB: ACI not used */ 112532176cfdSRui Paulo wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM); 112632176cfdSRui Paulo wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN); 112732176cfdSRui Paulo wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN); 112832176cfdSRui Paulo wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX); 112932176cfdSRui Paulo wmep->wmep_txopLimit = LE_READ_2(frm+2); 113032176cfdSRui Paulo frm += 4; 113132176cfdSRui Paulo } 113232176cfdSRui Paulo wme->wme_wmeChanParams.cap_info = qosinfo; 113332176cfdSRui Paulo return 1; 113432176cfdSRui Paulo #undef MS 113532176cfdSRui Paulo } 113632176cfdSRui Paulo 113732176cfdSRui Paulo /* 113832176cfdSRui Paulo * Process 11h Channel Switch Announcement (CSA) ie. If this 113932176cfdSRui Paulo * is the first CSA then initiate the switch. Otherwise we 114032176cfdSRui Paulo * track state and trigger completion and/or cancel of the switch. 114132176cfdSRui Paulo * XXX should be public for IBSS use 114232176cfdSRui Paulo */ 114332176cfdSRui Paulo static void 114432176cfdSRui Paulo ieee80211_parse_csaparams(struct ieee80211vap *vap, uint8_t *frm, 114532176cfdSRui Paulo const struct ieee80211_frame *wh) 114632176cfdSRui Paulo { 114732176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 114832176cfdSRui Paulo const struct ieee80211_csa_ie *csa = 114932176cfdSRui Paulo (const struct ieee80211_csa_ie *) frm; 115032176cfdSRui Paulo 115132176cfdSRui Paulo KASSERT(vap->iv_state >= IEEE80211_S_RUN, 115232176cfdSRui Paulo ("state %s", ieee80211_state_name[vap->iv_state])); 115332176cfdSRui Paulo 115432176cfdSRui Paulo if (csa->csa_mode > 1) { 115532176cfdSRui Paulo IEEE80211_DISCARD_IE(vap, 115632176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, 115732176cfdSRui Paulo wh, "CSA", "invalid mode %u", csa->csa_mode); 115832176cfdSRui Paulo return; 115932176cfdSRui Paulo } 1160085ff963SMatthew Dillon IEEE80211_LOCK(ic); 116132176cfdSRui Paulo if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) { 116232176cfdSRui Paulo /* 116332176cfdSRui Paulo * Convert the channel number to a channel reference. We 116432176cfdSRui Paulo * try first to preserve turbo attribute of the current 116532176cfdSRui Paulo * channel then fallback. Note this will not work if the 116632176cfdSRui Paulo * CSA specifies a channel that requires a band switch (e.g. 116732176cfdSRui Paulo * 11a => 11g). This is intentional as 11h is defined only 116832176cfdSRui Paulo * for 5GHz/11a and because the switch does not involve a 116932176cfdSRui Paulo * reassociation, protocol state (capabilities, negotated 117032176cfdSRui Paulo * rates, etc) may/will be wrong. 117132176cfdSRui Paulo */ 117232176cfdSRui Paulo struct ieee80211_channel *c = 117332176cfdSRui Paulo ieee80211_find_channel_byieee(ic, csa->csa_newchan, 117432176cfdSRui Paulo (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_ALLTURBO)); 117532176cfdSRui Paulo if (c == NULL) { 117632176cfdSRui Paulo c = ieee80211_find_channel_byieee(ic, 117732176cfdSRui Paulo csa->csa_newchan, 117832176cfdSRui Paulo (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_ALL)); 117932176cfdSRui Paulo if (c == NULL) { 118032176cfdSRui Paulo IEEE80211_DISCARD_IE(vap, 118132176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, 118232176cfdSRui Paulo wh, "CSA", "invalid channel %u", 118332176cfdSRui Paulo csa->csa_newchan); 118432176cfdSRui Paulo goto done; 118532176cfdSRui Paulo } 118632176cfdSRui Paulo } 118732176cfdSRui Paulo #if IEEE80211_CSA_COUNT_MIN > 0 118832176cfdSRui Paulo if (csa->csa_count < IEEE80211_CSA_COUNT_MIN) { 118932176cfdSRui Paulo /* 119032176cfdSRui Paulo * Require at least IEEE80211_CSA_COUNT_MIN count to 119132176cfdSRui Paulo * reduce the risk of being redirected by a fabricated 119232176cfdSRui Paulo * CSA. If a valid CSA is dropped we'll still get a 119332176cfdSRui Paulo * beacon miss when the AP leaves the channel so we'll 119432176cfdSRui Paulo * eventually follow to the new channel. 119532176cfdSRui Paulo * 119632176cfdSRui Paulo * NOTE: this violates the 11h spec that states that 119732176cfdSRui Paulo * count may be any value and if 0 then a switch 119832176cfdSRui Paulo * should happen asap. 119932176cfdSRui Paulo */ 120032176cfdSRui Paulo IEEE80211_DISCARD_IE(vap, 120132176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, 120232176cfdSRui Paulo wh, "CSA", "count %u too small, must be >= %u", 120332176cfdSRui Paulo csa->csa_count, IEEE80211_CSA_COUNT_MIN); 120432176cfdSRui Paulo goto done; 120532176cfdSRui Paulo } 120632176cfdSRui Paulo #endif 120732176cfdSRui Paulo ieee80211_csa_startswitch(ic, c, csa->csa_mode, csa->csa_count); 120832176cfdSRui Paulo } else { 120932176cfdSRui Paulo /* 121032176cfdSRui Paulo * Validate this ie against the initial CSA. We require 121132176cfdSRui Paulo * mode and channel not change and the count must be 121232176cfdSRui Paulo * monotonically decreasing. This may be pointless and 121332176cfdSRui Paulo * canceling the switch as a result may be too paranoid but 121432176cfdSRui Paulo * in the worst case if we drop out of CSA because of this 121532176cfdSRui Paulo * and the AP does move then we'll just end up taking a 121632176cfdSRui Paulo * beacon miss and scan to find the AP. 121732176cfdSRui Paulo * 121832176cfdSRui Paulo * XXX may want <= on count as we also process ProbeResp 121932176cfdSRui Paulo * frames and those may come in w/ the same count as the 122032176cfdSRui Paulo * previous beacon; but doing so leaves us open to a stuck 122132176cfdSRui Paulo * count until we add a dead-man timer 122232176cfdSRui Paulo */ 122332176cfdSRui Paulo if (!(csa->csa_count < ic->ic_csa_count && 122432176cfdSRui Paulo csa->csa_mode == ic->ic_csa_mode && 122532176cfdSRui Paulo csa->csa_newchan == ieee80211_chan2ieee(ic, ic->ic_csa_newchan))) { 122632176cfdSRui Paulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_DOTH, wh, 122732176cfdSRui Paulo "CSA ie mismatch, initial ie <%d,%d,%d>, " 122832176cfdSRui Paulo "this ie <%d,%d,%d>", ic->ic_csa_mode, 1229*2c7ccc4aSSascha Wildner ic->ic_csa_newchan->ic_ieee, ic->ic_csa_count, 123032176cfdSRui Paulo csa->csa_mode, csa->csa_newchan, csa->csa_count); 123132176cfdSRui Paulo ieee80211_csa_cancelswitch(ic); 123232176cfdSRui Paulo } else { 123332176cfdSRui Paulo if (csa->csa_count <= 1) 123432176cfdSRui Paulo ieee80211_csa_completeswitch(ic); 123532176cfdSRui Paulo else 123632176cfdSRui Paulo ic->ic_csa_count = csa->csa_count; 123732176cfdSRui Paulo } 123832176cfdSRui Paulo } 123932176cfdSRui Paulo done: 1240085ff963SMatthew Dillon IEEE80211_UNLOCK(ic); 124132176cfdSRui Paulo } 124232176cfdSRui Paulo 124332176cfdSRui Paulo /* 124432176cfdSRui Paulo * Return non-zero if a background scan may be continued: 124532176cfdSRui Paulo * o bg scan is active 124632176cfdSRui Paulo * o no channel switch is pending 124732176cfdSRui Paulo * o there has not been any traffic recently 124832176cfdSRui Paulo * 124932176cfdSRui Paulo * Note we do not check if there is an administrative enable; 125032176cfdSRui Paulo * this is only done to start the scan. We assume that any 125132176cfdSRui Paulo * change in state will be accompanied by a request to cancel 125232176cfdSRui Paulo * active scans which will otherwise cause this test to fail. 125332176cfdSRui Paulo */ 125432176cfdSRui Paulo static __inline int 125532176cfdSRui Paulo contbgscan(struct ieee80211vap *vap) 125632176cfdSRui Paulo { 125732176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 125832176cfdSRui Paulo 125932176cfdSRui Paulo return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) && 126032176cfdSRui Paulo (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && 126132176cfdSRui Paulo vap->iv_state == IEEE80211_S_RUN && /* XXX? */ 126232176cfdSRui Paulo time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); 126332176cfdSRui Paulo } 126432176cfdSRui Paulo 126532176cfdSRui Paulo /* 126632176cfdSRui Paulo * Return non-zero if a backgrond scan may be started: 126732176cfdSRui Paulo * o bg scanning is administratively enabled 126832176cfdSRui Paulo * o no channel switch is pending 126932176cfdSRui Paulo * o we are not boosted on a dynamic turbo channel 127032176cfdSRui Paulo * o there has not been a scan recently 127132176cfdSRui Paulo * o there has not been any traffic recently 127232176cfdSRui Paulo */ 127332176cfdSRui Paulo static __inline int 127432176cfdSRui Paulo startbgscan(struct ieee80211vap *vap) 127532176cfdSRui Paulo { 127632176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 127732176cfdSRui Paulo 127832176cfdSRui Paulo return ((vap->iv_flags & IEEE80211_F_BGSCAN) && 127932176cfdSRui Paulo (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && 128032176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 128132176cfdSRui Paulo !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && 128232176cfdSRui Paulo #endif 128332176cfdSRui Paulo time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) && 128432176cfdSRui Paulo time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); 128532176cfdSRui Paulo } 128632176cfdSRui Paulo 128732176cfdSRui Paulo static void 128832176cfdSRui Paulo sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 128932176cfdSRui Paulo int subtype, int rssi, int nf) 129032176cfdSRui Paulo { 129132176cfdSRui Paulo #define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 129232176cfdSRui Paulo #define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) 129332176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 129432176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic; 129532176cfdSRui Paulo struct ieee80211_frame *wh; 129632176cfdSRui Paulo uint8_t *frm, *efrm; 129732176cfdSRui Paulo uint8_t *rates, *xrates, *wme, *htcap, *htinfo; 129832176cfdSRui Paulo uint8_t rate; 1299085ff963SMatthew Dillon int ht_state_change = 0; 130032176cfdSRui Paulo 130132176cfdSRui Paulo wh = mtod(m0, struct ieee80211_frame *); 130232176cfdSRui Paulo frm = (uint8_t *)&wh[1]; 130332176cfdSRui Paulo efrm = mtod(m0, uint8_t *) + m0->m_len; 130432176cfdSRui Paulo switch (subtype) { 130532176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 130632176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_BEACON: { 130732176cfdSRui Paulo struct ieee80211_scanparams scan; 130832176cfdSRui Paulo /* 130932176cfdSRui Paulo * We process beacon/probe response frames: 131032176cfdSRui Paulo * o when scanning, or 131132176cfdSRui Paulo * o station mode when associated (to collect state 131232176cfdSRui Paulo * updates such as 802.11g slot time) 131332176cfdSRui Paulo * Frames otherwise received are discarded. 131432176cfdSRui Paulo */ 131532176cfdSRui Paulo if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) { 131632176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 131732176cfdSRui Paulo return; 131832176cfdSRui Paulo } 131932176cfdSRui Paulo /* XXX probe response in sta mode when !scanning? */ 1320085ff963SMatthew Dillon if (ieee80211_parse_beacon(ni, m0, &scan) != 0) { 1321085ff963SMatthew Dillon if (! (ic->ic_flags & IEEE80211_F_SCAN)) 1322085ff963SMatthew Dillon vap->iv_stats.is_beacon_bad++; 132332176cfdSRui Paulo return; 1324085ff963SMatthew Dillon } 1325085ff963SMatthew Dillon 132632176cfdSRui Paulo /* 132732176cfdSRui Paulo * Count frame now that we know it's to be processed. 132832176cfdSRui Paulo */ 132932176cfdSRui Paulo if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { 133032176cfdSRui Paulo vap->iv_stats.is_rx_beacon++; /* XXX remove */ 133132176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_beacons); 133232176cfdSRui Paulo } else 133332176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_proberesp); 133432176cfdSRui Paulo /* 133532176cfdSRui Paulo * When operating in station mode, check for state updates. 133632176cfdSRui Paulo * Be careful to ignore beacons received while doing a 133732176cfdSRui Paulo * background scan. We consider only 11g/WMM stuff right now. 133832176cfdSRui Paulo */ 133932176cfdSRui Paulo if (ni->ni_associd != 0 && 134032176cfdSRui Paulo ((ic->ic_flags & IEEE80211_F_SCAN) == 0 || 134132176cfdSRui Paulo IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) { 134232176cfdSRui Paulo /* record tsf of last beacon */ 134332176cfdSRui Paulo memcpy(ni->ni_tstamp.data, scan.tstamp, 134432176cfdSRui Paulo sizeof(ni->ni_tstamp)); 134532176cfdSRui Paulo /* count beacon frame for s/w bmiss handling */ 134632176cfdSRui Paulo vap->iv_swbmiss_count++; 134732176cfdSRui Paulo vap->iv_bmiss_count = 0; 134832176cfdSRui Paulo if (ni->ni_erp != scan.erp) { 134932176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, 135032176cfdSRui Paulo wh->i_addr2, 135132176cfdSRui Paulo "erp change: was 0x%x, now 0x%x", 135232176cfdSRui Paulo ni->ni_erp, scan.erp); 135332176cfdSRui Paulo if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 135432176cfdSRui Paulo (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) 135532176cfdSRui Paulo ic->ic_flags |= IEEE80211_F_USEPROT; 135632176cfdSRui Paulo else 135732176cfdSRui Paulo ic->ic_flags &= ~IEEE80211_F_USEPROT; 135832176cfdSRui Paulo ni->ni_erp = scan.erp; 135932176cfdSRui Paulo /* XXX statistic */ 136032176cfdSRui Paulo /* XXX driver notification */ 136132176cfdSRui Paulo } 136232176cfdSRui Paulo if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) { 136332176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, 136432176cfdSRui Paulo wh->i_addr2, 136532176cfdSRui Paulo "capabilities change: was 0x%x, now 0x%x", 136632176cfdSRui Paulo ni->ni_capinfo, scan.capinfo); 136732176cfdSRui Paulo /* 136832176cfdSRui Paulo * NB: we assume short preamble doesn't 136932176cfdSRui Paulo * change dynamically 137032176cfdSRui Paulo */ 137132176cfdSRui Paulo ieee80211_set_shortslottime(ic, 137232176cfdSRui Paulo IEEE80211_IS_CHAN_A(ic->ic_bsschan) || 137332176cfdSRui Paulo (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); 137432176cfdSRui Paulo ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME) 137532176cfdSRui Paulo | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME); 137632176cfdSRui Paulo /* XXX statistic */ 137732176cfdSRui Paulo } 137832176cfdSRui Paulo if (scan.wme != NULL && 137932176cfdSRui Paulo (ni->ni_flags & IEEE80211_NODE_QOS) && 138032176cfdSRui Paulo ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0) 138132176cfdSRui Paulo ieee80211_wme_updateparams(vap); 138232176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 138332176cfdSRui Paulo if (scan.ath != NULL) 138432176cfdSRui Paulo ieee80211_parse_athparams(ni, scan.ath, wh); 138532176cfdSRui Paulo #endif 138632176cfdSRui Paulo if (scan.htcap != NULL && scan.htinfo != NULL && 138732176cfdSRui Paulo (vap->iv_flags_ht & IEEE80211_FHT_HT)) { 138832176cfdSRui Paulo /* XXX state changes? */ 1389085ff963SMatthew Dillon if (ieee80211_ht_updateparams(ni, 1390085ff963SMatthew Dillon scan.htcap, scan.htinfo)) 1391085ff963SMatthew Dillon ht_state_change = 1; 139232176cfdSRui Paulo } 1393085ff963SMatthew Dillon if (scan.quiet) 1394085ff963SMatthew Dillon ic->ic_set_quiet(ni, scan.quiet); 1395d98a0bcfSMatthew Dillon 139632176cfdSRui Paulo if (scan.tim != NULL) { 139732176cfdSRui Paulo struct ieee80211_tim_ie *tim = 139832176cfdSRui Paulo (struct ieee80211_tim_ie *) scan.tim; 1399d98a0bcfSMatthew Dillon /* 1400d98a0bcfSMatthew Dillon * XXX Check/debug this code; see if it's about 1401d98a0bcfSMatthew Dillon * the right time to force the VAP awake if we 1402d98a0bcfSMatthew Dillon * receive a frame destined for us? 1403d98a0bcfSMatthew Dillon */ 140432176cfdSRui Paulo int aid = IEEE80211_AID(ni->ni_associd); 140532176cfdSRui Paulo int ix = aid / NBBY; 140632176cfdSRui Paulo int min = tim->tim_bitctl &~ 1; 140732176cfdSRui Paulo int max = tim->tim_len + min - 4; 1408d98a0bcfSMatthew Dillon 140932176cfdSRui Paulo /* 1410d98a0bcfSMatthew Dillon * Only do this for unicast traffic in the TIM 1411d98a0bcfSMatthew Dillon * The multicast traffic notification for 1412d98a0bcfSMatthew Dillon * the scan notification stuff should occur 1413d98a0bcfSMatthew Dillon * differently. 141432176cfdSRui Paulo */ 1415d98a0bcfSMatthew Dillon if (min <= ix && ix <= max && 1416d98a0bcfSMatthew Dillon isset(tim->tim_bitmap - min, aid)) { 1417d98a0bcfSMatthew Dillon ieee80211_sta_tim_notify(vap, 1); 141832176cfdSRui Paulo ic->ic_lastdata = ticks; 1419d98a0bcfSMatthew Dillon } 1420d98a0bcfSMatthew Dillon 1421d98a0bcfSMatthew Dillon /* 1422d98a0bcfSMatthew Dillon * XXX TODO: do a separate notification 1423d98a0bcfSMatthew Dillon * for the multicast bit being set. 1424d98a0bcfSMatthew Dillon */ 1425d98a0bcfSMatthew Dillon #if 0 1426d98a0bcfSMatthew Dillon if (tim->tim_bitctl & 1) { 1427d98a0bcfSMatthew Dillon ieee80211_sta_tim_notify(vap, 1); 1428d98a0bcfSMatthew Dillon ic->ic_lastdata = ticks; 142932176cfdSRui Paulo } 143032176cfdSRui Paulo #endif 1431085ff963SMatthew Dillon 143232176cfdSRui Paulo ni->ni_dtim_count = tim->tim_count; 143332176cfdSRui Paulo ni->ni_dtim_period = tim->tim_period; 143432176cfdSRui Paulo } 143532176cfdSRui Paulo if (scan.csa != NULL && 143632176cfdSRui Paulo (vap->iv_flags & IEEE80211_F_DOTH)) 143732176cfdSRui Paulo ieee80211_parse_csaparams(vap, scan.csa, wh); 143832176cfdSRui Paulo else if (ic->ic_flags & IEEE80211_F_CSAPENDING) { 143932176cfdSRui Paulo /* 144032176cfdSRui Paulo * No CSA ie or 11h disabled, but a channel 144132176cfdSRui Paulo * switch is pending; drop out so we aren't 144232176cfdSRui Paulo * stuck in CSA state. If the AP really is 144332176cfdSRui Paulo * moving we'll get a beacon miss and scan. 144432176cfdSRui Paulo */ 1445085ff963SMatthew Dillon IEEE80211_LOCK(ic); 144632176cfdSRui Paulo ieee80211_csa_cancelswitch(ic); 1447085ff963SMatthew Dillon IEEE80211_UNLOCK(ic); 144832176cfdSRui Paulo } 144932176cfdSRui Paulo /* 145032176cfdSRui Paulo * If scanning, pass the info to the scan module. 145132176cfdSRui Paulo * Otherwise, check if it's the right time to do 145232176cfdSRui Paulo * a background scan. Background scanning must 145332176cfdSRui Paulo * be enabled and we must not be operating in the 145432176cfdSRui Paulo * turbo phase of dynamic turbo mode. Then, 145532176cfdSRui Paulo * it's been a while since the last background 145632176cfdSRui Paulo * scan and if no data frames have come through 145732176cfdSRui Paulo * recently, kick off a scan. Note that this 145832176cfdSRui Paulo * is the mechanism by which a background scan 145932176cfdSRui Paulo * is started _and_ continued each time we 146032176cfdSRui Paulo * return on-channel to receive a beacon from 146132176cfdSRui Paulo * our ap. 146232176cfdSRui Paulo */ 146332176cfdSRui Paulo if (ic->ic_flags & IEEE80211_F_SCAN) { 146432176cfdSRui Paulo ieee80211_add_scan(vap, &scan, wh, 146532176cfdSRui Paulo subtype, rssi, nf); 146632176cfdSRui Paulo } else if (contbgscan(vap)) { 146732176cfdSRui Paulo ieee80211_bg_scan(vap, 0); 146832176cfdSRui Paulo } else if (startbgscan(vap)) { 146932176cfdSRui Paulo vap->iv_stats.is_scan_bg++; 147032176cfdSRui Paulo #if 0 147132176cfdSRui Paulo /* wakeup if we are sleeing */ 147232176cfdSRui Paulo ieee80211_set_pwrsave(vap, 0); 147332176cfdSRui Paulo #endif 147432176cfdSRui Paulo ieee80211_bg_scan(vap, 0); 147532176cfdSRui Paulo } 1476d98a0bcfSMatthew Dillon 1477d98a0bcfSMatthew Dillon /* 1478d98a0bcfSMatthew Dillon * Put the station to sleep if we haven't seen 1479d98a0bcfSMatthew Dillon * traffic in a while. 1480d98a0bcfSMatthew Dillon */ 1481085ff963SMatthew Dillon IEEE80211_LOCK(ic); 1482d98a0bcfSMatthew Dillon ieee80211_sta_ps_timer_check(vap); 1483085ff963SMatthew Dillon IEEE80211_UNLOCK(ic); 1484d98a0bcfSMatthew Dillon 1485085ff963SMatthew Dillon /* 1486085ff963SMatthew Dillon * If we've had a channel width change (eg HT20<->HT40) 1487085ff963SMatthew Dillon * then schedule a delayed driver notification. 1488085ff963SMatthew Dillon */ 1489085ff963SMatthew Dillon if (ht_state_change) 1490085ff963SMatthew Dillon ieee80211_update_chw(ic); 149132176cfdSRui Paulo return; 149232176cfdSRui Paulo } 149332176cfdSRui Paulo /* 149432176cfdSRui Paulo * If scanning, just pass information to the scan module. 149532176cfdSRui Paulo */ 149632176cfdSRui Paulo if (ic->ic_flags & IEEE80211_F_SCAN) { 149732176cfdSRui Paulo if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { 149832176cfdSRui Paulo /* 149932176cfdSRui Paulo * Actively scanning a channel marked passive; 150032176cfdSRui Paulo * send a probe request now that we know there 150132176cfdSRui Paulo * is 802.11 traffic present. 150232176cfdSRui Paulo * 150332176cfdSRui Paulo * XXX check if the beacon we recv'd gives 150432176cfdSRui Paulo * us what we need and suppress the probe req 150532176cfdSRui Paulo */ 150632176cfdSRui Paulo ieee80211_probe_curchan(vap, 1); 150732176cfdSRui Paulo ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; 150832176cfdSRui Paulo } 150932176cfdSRui Paulo ieee80211_add_scan(vap, &scan, wh, subtype, rssi, nf); 151032176cfdSRui Paulo return; 151132176cfdSRui Paulo } 151232176cfdSRui Paulo break; 151332176cfdSRui Paulo } 151432176cfdSRui Paulo 151532176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_AUTH: { 151632176cfdSRui Paulo uint16_t algo, seq, status; 151732176cfdSRui Paulo /* 151832176cfdSRui Paulo * auth frame format 151932176cfdSRui Paulo * [2] algorithm 152032176cfdSRui Paulo * [2] sequence 152132176cfdSRui Paulo * [2] status 152232176cfdSRui Paulo * [tlv*] challenge 152332176cfdSRui Paulo */ 152432176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); 152532176cfdSRui Paulo algo = le16toh(*(uint16_t *)frm); 152632176cfdSRui Paulo seq = le16toh(*(uint16_t *)(frm + 2)); 152732176cfdSRui Paulo status = le16toh(*(uint16_t *)(frm + 4)); 152832176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2, 152932176cfdSRui Paulo "recv auth frame with algorithm %d seq %d", algo, seq); 153032176cfdSRui Paulo 153132176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_COUNTERM) { 153232176cfdSRui Paulo IEEE80211_DISCARD(vap, 153332176cfdSRui Paulo IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, 153432176cfdSRui Paulo wh, "auth", "%s", "TKIP countermeasures enabled"); 153532176cfdSRui Paulo vap->iv_stats.is_rx_auth_countermeasures++; 153632176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_HOSTAP) { 153732176cfdSRui Paulo ieee80211_send_error(ni, wh->i_addr2, 153832176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 153932176cfdSRui Paulo IEEE80211_REASON_MIC_FAILURE); 154032176cfdSRui Paulo } 154132176cfdSRui Paulo return; 154232176cfdSRui Paulo } 154332176cfdSRui Paulo if (algo == IEEE80211_AUTH_ALG_SHARED) 154432176cfdSRui Paulo sta_auth_shared(ni, wh, frm + 6, efrm, rssi, nf, 154532176cfdSRui Paulo seq, status); 154632176cfdSRui Paulo else if (algo == IEEE80211_AUTH_ALG_OPEN) 154732176cfdSRui Paulo sta_auth_open(ni, wh, rssi, nf, seq, status); 154832176cfdSRui Paulo else { 154932176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 155032176cfdSRui Paulo wh, "auth", "unsupported alg %d", algo); 155132176cfdSRui Paulo vap->iv_stats.is_rx_auth_unsupported++; 155232176cfdSRui Paulo return; 155332176cfdSRui Paulo } 155432176cfdSRui Paulo break; 155532176cfdSRui Paulo } 155632176cfdSRui Paulo 155732176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 155832176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { 155932176cfdSRui Paulo uint16_t capinfo, associd; 156032176cfdSRui Paulo uint16_t status; 156132176cfdSRui Paulo 156232176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_ASSOC) { 156332176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 156432176cfdSRui Paulo return; 156532176cfdSRui Paulo } 156632176cfdSRui Paulo 156732176cfdSRui Paulo /* 156832176cfdSRui Paulo * asresp frame format 156932176cfdSRui Paulo * [2] capability information 157032176cfdSRui Paulo * [2] status 157132176cfdSRui Paulo * [2] association ID 157232176cfdSRui Paulo * [tlv] supported rates 157332176cfdSRui Paulo * [tlv] extended supported rates 157432176cfdSRui Paulo * [tlv] WME 157532176cfdSRui Paulo * [tlv] HT capabilities 157632176cfdSRui Paulo * [tlv] HT info 157732176cfdSRui Paulo */ 157832176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); 157932176cfdSRui Paulo ni = vap->iv_bss; 158032176cfdSRui Paulo capinfo = le16toh(*(uint16_t *)frm); 158132176cfdSRui Paulo frm += 2; 158232176cfdSRui Paulo status = le16toh(*(uint16_t *)frm); 158332176cfdSRui Paulo frm += 2; 158432176cfdSRui Paulo if (status != 0) { 158532176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, 158632176cfdSRui Paulo wh->i_addr2, "%sassoc failed (reason %d)", 158732176cfdSRui Paulo ISREASSOC(subtype) ? "re" : "", status); 158832176cfdSRui Paulo vap->iv_stats.is_rx_auth_fail++; /* XXX */ 158932176cfdSRui Paulo return; 159032176cfdSRui Paulo } 159132176cfdSRui Paulo associd = le16toh(*(uint16_t *)frm); 159232176cfdSRui Paulo frm += 2; 159332176cfdSRui Paulo 159432176cfdSRui Paulo rates = xrates = wme = htcap = htinfo = NULL; 159532176cfdSRui Paulo while (efrm - frm > 1) { 159632176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 159732176cfdSRui Paulo switch (*frm) { 159832176cfdSRui Paulo case IEEE80211_ELEMID_RATES: 159932176cfdSRui Paulo rates = frm; 160032176cfdSRui Paulo break; 160132176cfdSRui Paulo case IEEE80211_ELEMID_XRATES: 160232176cfdSRui Paulo xrates = frm; 160332176cfdSRui Paulo break; 160432176cfdSRui Paulo case IEEE80211_ELEMID_HTCAP: 160532176cfdSRui Paulo htcap = frm; 160632176cfdSRui Paulo break; 160732176cfdSRui Paulo case IEEE80211_ELEMID_HTINFO: 160832176cfdSRui Paulo htinfo = frm; 160932176cfdSRui Paulo break; 161032176cfdSRui Paulo case IEEE80211_ELEMID_VENDOR: 161132176cfdSRui Paulo if (iswmeoui(frm)) 161232176cfdSRui Paulo wme = frm; 161332176cfdSRui Paulo else if (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) { 161432176cfdSRui Paulo /* 161532176cfdSRui Paulo * Accept pre-draft HT ie's if the 161632176cfdSRui Paulo * standard ones have not been seen. 161732176cfdSRui Paulo */ 161832176cfdSRui Paulo if (ishtcapoui(frm)) { 161932176cfdSRui Paulo if (htcap == NULL) 162032176cfdSRui Paulo htcap = frm; 162132176cfdSRui Paulo } else if (ishtinfooui(frm)) { 162232176cfdSRui Paulo if (htinfo == NULL) 1623085ff963SMatthew Dillon htinfo = frm; 162432176cfdSRui Paulo } 162532176cfdSRui Paulo } 162632176cfdSRui Paulo /* XXX Atheros OUI support */ 162732176cfdSRui Paulo break; 162832176cfdSRui Paulo } 162932176cfdSRui Paulo frm += frm[1] + 2; 163032176cfdSRui Paulo } 163132176cfdSRui Paulo 163232176cfdSRui Paulo IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); 163332176cfdSRui Paulo if (xrates != NULL) 163432176cfdSRui Paulo IEEE80211_VERIFY_ELEMENT(xrates, 163532176cfdSRui Paulo IEEE80211_RATE_MAXSIZE - rates[1], return); 163632176cfdSRui Paulo rate = ieee80211_setup_rates(ni, rates, xrates, 163732176cfdSRui Paulo IEEE80211_F_JOIN | 163832176cfdSRui Paulo IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | 163932176cfdSRui Paulo IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 164032176cfdSRui Paulo if (rate & IEEE80211_RATE_BASIC) { 164132176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, 164232176cfdSRui Paulo wh->i_addr2, 164332176cfdSRui Paulo "%sassoc failed (rate set mismatch)", 164432176cfdSRui Paulo ISREASSOC(subtype) ? "re" : ""); 164532176cfdSRui Paulo vap->iv_stats.is_rx_assoc_norate++; 164632176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, 164732176cfdSRui Paulo IEEE80211_SCAN_FAIL_STATUS); 164832176cfdSRui Paulo return; 164932176cfdSRui Paulo } 165032176cfdSRui Paulo 165132176cfdSRui Paulo ni->ni_capinfo = capinfo; 165232176cfdSRui Paulo ni->ni_associd = associd; 165332176cfdSRui Paulo if (ni->ni_jointime == 0) 1654cec73927SMatthew Dillon ni->ni_jointime = time_uptime; 165532176cfdSRui Paulo if (wme != NULL && 165632176cfdSRui Paulo ieee80211_parse_wmeparams(vap, wme, wh) >= 0) { 165732176cfdSRui Paulo ni->ni_flags |= IEEE80211_NODE_QOS; 165832176cfdSRui Paulo ieee80211_wme_updateparams(vap); 165932176cfdSRui Paulo } else 166032176cfdSRui Paulo ni->ni_flags &= ~IEEE80211_NODE_QOS; 166132176cfdSRui Paulo /* 166232176cfdSRui Paulo * Setup HT state according to the negotiation. 166332176cfdSRui Paulo * 166432176cfdSRui Paulo * NB: shouldn't need to check if HT use is enabled but some 166532176cfdSRui Paulo * ap's send back HT ie's even when we don't indicate we 166632176cfdSRui Paulo * are HT capable in our AssocReq. 166732176cfdSRui Paulo */ 166832176cfdSRui Paulo if (htcap != NULL && htinfo != NULL && 166932176cfdSRui Paulo (vap->iv_flags_ht & IEEE80211_FHT_HT)) { 167032176cfdSRui Paulo ieee80211_ht_node_init(ni); 167132176cfdSRui Paulo ieee80211_ht_updateparams(ni, htcap, htinfo); 167232176cfdSRui Paulo ieee80211_setup_htrates(ni, htcap, 167332176cfdSRui Paulo IEEE80211_F_JOIN | IEEE80211_F_DOBRS); 167432176cfdSRui Paulo ieee80211_setup_basic_htrates(ni, htinfo); 167532176cfdSRui Paulo ieee80211_node_setuptxparms(ni); 16764fbce6bdSSascha Wildner ieee80211_ratectl_node_init(ni); 167732176cfdSRui Paulo } else { 167832176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 167932176cfdSRui Paulo if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_ATH)) 168032176cfdSRui Paulo ieee80211_ff_node_init(ni); 168132176cfdSRui Paulo #endif 168232176cfdSRui Paulo } 168332176cfdSRui Paulo /* 168432176cfdSRui Paulo * Configure state now that we are associated. 168532176cfdSRui Paulo * 168632176cfdSRui Paulo * XXX may need different/additional driver callbacks? 168732176cfdSRui Paulo */ 168832176cfdSRui Paulo if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || 168932176cfdSRui Paulo (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { 169032176cfdSRui Paulo ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 169132176cfdSRui Paulo ic->ic_flags &= ~IEEE80211_F_USEBARKER; 169232176cfdSRui Paulo } else { 169332176cfdSRui Paulo ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 169432176cfdSRui Paulo ic->ic_flags |= IEEE80211_F_USEBARKER; 169532176cfdSRui Paulo } 169632176cfdSRui Paulo ieee80211_set_shortslottime(ic, 169732176cfdSRui Paulo IEEE80211_IS_CHAN_A(ic->ic_curchan) || 169832176cfdSRui Paulo (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); 169932176cfdSRui Paulo /* 170032176cfdSRui Paulo * Honor ERP protection. 170132176cfdSRui Paulo * 170232176cfdSRui Paulo * NB: ni_erp should zero for non-11g operation. 170332176cfdSRui Paulo */ 170432176cfdSRui Paulo if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 170532176cfdSRui Paulo (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) 170632176cfdSRui Paulo ic->ic_flags |= IEEE80211_F_USEPROT; 170732176cfdSRui Paulo else 170832176cfdSRui Paulo ic->ic_flags &= ~IEEE80211_F_USEPROT; 170932176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, 171032176cfdSRui Paulo IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2, 171132176cfdSRui Paulo "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s", 171232176cfdSRui Paulo ISREASSOC(subtype) ? "re" : "", 171332176cfdSRui Paulo IEEE80211_NODE_AID(ni), 171432176cfdSRui Paulo ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", 171532176cfdSRui Paulo ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", 171632176cfdSRui Paulo ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "", 171732176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", 171832176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_HT ? 171932176cfdSRui Paulo (ni->ni_chw == 40 ? ", HT40" : ", HT20") : "", 172032176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", 172132176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_MIMO_RTS ? " (+SMPS-DYN)" : 172232176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_MIMO_PS ? " (+SMPS)" : "", 172332176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_RIFS ? " (+RIFS)" : "", 172432176cfdSRui Paulo IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ? 172532176cfdSRui Paulo ", fast-frames" : "", 172632176cfdSRui Paulo IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ? 172732176cfdSRui Paulo ", turbo" : "" 172832176cfdSRui Paulo ); 172932176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_RUN, subtype); 173032176cfdSRui Paulo break; 173132176cfdSRui Paulo } 173232176cfdSRui Paulo 173332176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_DEAUTH: { 173432176cfdSRui Paulo uint16_t reason; 173532176cfdSRui Paulo 173632176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_SCAN) { 173732176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 173832176cfdSRui Paulo return; 173932176cfdSRui Paulo } 174032176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { 174132176cfdSRui Paulo /* NB: can happen when in promiscuous mode */ 174232176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 174332176cfdSRui Paulo break; 174432176cfdSRui Paulo } 174532176cfdSRui Paulo 174632176cfdSRui Paulo /* 174732176cfdSRui Paulo * deauth frame format 174832176cfdSRui Paulo * [2] reason 174932176cfdSRui Paulo */ 175032176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); 175132176cfdSRui Paulo reason = le16toh(*(uint16_t *)frm); 175232176cfdSRui Paulo 175332176cfdSRui Paulo vap->iv_stats.is_rx_deauth++; 175432176cfdSRui Paulo vap->iv_stats.is_rx_deauth_code = reason; 175532176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_deauth); 175632176cfdSRui Paulo 175732176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, 175832176cfdSRui Paulo "recv deauthenticate (reason %d)", reason); 175932176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_AUTH, 176032176cfdSRui Paulo (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH); 176132176cfdSRui Paulo break; 176232176cfdSRui Paulo } 176332176cfdSRui Paulo 176432176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_DISASSOC: { 176532176cfdSRui Paulo uint16_t reason; 176632176cfdSRui Paulo 176732176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_RUN && 176832176cfdSRui Paulo vap->iv_state != IEEE80211_S_ASSOC && 176932176cfdSRui Paulo vap->iv_state != IEEE80211_S_AUTH) { 177032176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 177132176cfdSRui Paulo return; 177232176cfdSRui Paulo } 177332176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { 177432176cfdSRui Paulo /* NB: can happen when in promiscuous mode */ 177532176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 177632176cfdSRui Paulo break; 177732176cfdSRui Paulo } 177832176cfdSRui Paulo 177932176cfdSRui Paulo /* 178032176cfdSRui Paulo * disassoc frame format 178132176cfdSRui Paulo * [2] reason 178232176cfdSRui Paulo */ 178332176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); 178432176cfdSRui Paulo reason = le16toh(*(uint16_t *)frm); 178532176cfdSRui Paulo 178632176cfdSRui Paulo vap->iv_stats.is_rx_disassoc++; 178732176cfdSRui Paulo vap->iv_stats.is_rx_disassoc_code = reason; 178832176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_disassoc); 178932176cfdSRui Paulo 179032176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, 179132176cfdSRui Paulo "recv disassociate (reason %d)", reason); 179232176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); 179332176cfdSRui Paulo break; 179432176cfdSRui Paulo } 179532176cfdSRui Paulo 179632176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ACTION: 1797085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: 1798085ff963SMatthew Dillon if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && 1799085ff963SMatthew Dillon !IEEE80211_IS_MULTICAST(wh->i_addr1)) { 1800085ff963SMatthew Dillon IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1801085ff963SMatthew Dillon wh, NULL, "%s", "not for us"); 1802085ff963SMatthew Dillon vap->iv_stats.is_rx_mgtdiscard++; 1803085ff963SMatthew Dillon } else if (vap->iv_state != IEEE80211_S_RUN) { 1804085ff963SMatthew Dillon IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1805085ff963SMatthew Dillon wh, NULL, "wrong state %s", 1806085ff963SMatthew Dillon ieee80211_state_name[vap->iv_state]); 1807085ff963SMatthew Dillon vap->iv_stats.is_rx_mgtdiscard++; 1808085ff963SMatthew Dillon } else { 180932176cfdSRui Paulo if (ieee80211_parse_action(ni, m0) == 0) 1810085ff963SMatthew Dillon (void)ic->ic_recv_action(ni, wh, frm, efrm); 1811085ff963SMatthew Dillon } 1812085ff963SMatthew Dillon break; 1813085ff963SMatthew Dillon 1814085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 1815085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 1816085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 1817085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_ATIM: 1818085ff963SMatthew Dillon IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1819085ff963SMatthew Dillon wh, NULL, "%s", "not handled"); 182032176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; 182132176cfdSRui Paulo break; 182232176cfdSRui Paulo 182332176cfdSRui Paulo default: 182432176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 182532176cfdSRui Paulo wh, "mgt", "subtype 0x%x not handled", subtype); 182632176cfdSRui Paulo vap->iv_stats.is_rx_badsubtype++; 182732176cfdSRui Paulo break; 182832176cfdSRui Paulo } 182932176cfdSRui Paulo #undef ISREASSOC 183032176cfdSRui Paulo #undef ISPROBE 183132176cfdSRui Paulo } 183232176cfdSRui Paulo 183332176cfdSRui Paulo static void 1834085ff963SMatthew Dillon sta_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) 183532176cfdSRui Paulo { 1836085ff963SMatthew Dillon switch (subtype) { 1837085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_BAR: 1838085ff963SMatthew Dillon ieee80211_recv_bar(ni, m); 1839085ff963SMatthew Dillon break; 1840085ff963SMatthew Dillon } 184132176cfdSRui Paulo } 1842