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);
734f898719SImre Vadász static int sta_input(struct ieee80211_node *, struct mbuf *,
744f898719SImre Vadász const struct ieee80211_rx_stats *, int, int);
7532176cfdSRui Paulo static void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *,
764f898719SImre Vadász int subtype, const struct ieee80211_rx_stats *, int rssi, int nf);
7732176cfdSRui Paulo static void sta_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype);
7832176cfdSRui Paulo
7932176cfdSRui Paulo void
ieee80211_sta_attach(struct ieee80211com * ic)8032176cfdSRui Paulo ieee80211_sta_attach(struct ieee80211com *ic)
8132176cfdSRui Paulo {
8232176cfdSRui Paulo ic->ic_vattach[IEEE80211_M_STA] = sta_vattach;
8332176cfdSRui Paulo }
8432176cfdSRui Paulo
8532176cfdSRui Paulo void
ieee80211_sta_detach(struct ieee80211com * ic)8632176cfdSRui Paulo ieee80211_sta_detach(struct ieee80211com *ic)
8732176cfdSRui Paulo {
8832176cfdSRui Paulo }
8932176cfdSRui Paulo
9032176cfdSRui Paulo static void
sta_vdetach(struct ieee80211vap * vap)9132176cfdSRui Paulo sta_vdetach(struct ieee80211vap *vap)
9232176cfdSRui Paulo {
9332176cfdSRui Paulo }
9432176cfdSRui Paulo
9532176cfdSRui Paulo static void
sta_vattach(struct ieee80211vap * vap)9632176cfdSRui Paulo sta_vattach(struct ieee80211vap *vap)
9732176cfdSRui Paulo {
9832176cfdSRui Paulo vap->iv_newstate = sta_newstate;
9932176cfdSRui Paulo vap->iv_input = sta_input;
10032176cfdSRui Paulo vap->iv_recv_mgmt = sta_recv_mgmt;
10132176cfdSRui Paulo vap->iv_recv_ctl = sta_recv_ctl;
10232176cfdSRui Paulo vap->iv_opdetach = sta_vdetach;
10332176cfdSRui Paulo vap->iv_bmiss = sta_beacon_miss;
10432176cfdSRui Paulo }
10532176cfdSRui Paulo
10632176cfdSRui Paulo /*
10732176cfdSRui Paulo * Handle a beacon miss event. The common code filters out
10832176cfdSRui Paulo * spurious events that can happen when scanning and/or before
10932176cfdSRui Paulo * reaching RUN state.
11032176cfdSRui Paulo */
11132176cfdSRui Paulo static void
sta_beacon_miss(struct ieee80211vap * vap)11232176cfdSRui Paulo sta_beacon_miss(struct ieee80211vap *vap)
11332176cfdSRui Paulo {
11432176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
11532176cfdSRui Paulo
116085ff963SMatthew Dillon IEEE80211_LOCK_ASSERT(ic);
117085ff963SMatthew Dillon
11832176cfdSRui Paulo KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
11932176cfdSRui Paulo KASSERT(vap->iv_state >= IEEE80211_S_RUN,
12032176cfdSRui Paulo ("wrong state %s", ieee80211_state_name[vap->iv_state]));
12132176cfdSRui Paulo
12232176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
12332176cfdSRui Paulo "beacon miss, mode %s state %s\n",
12432176cfdSRui Paulo ieee80211_opmode_name[vap->iv_opmode],
12532176cfdSRui Paulo ieee80211_state_name[vap->iv_state]);
12632176cfdSRui Paulo
12732176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_CSA) {
12832176cfdSRui Paulo /*
12932176cfdSRui Paulo * A Channel Switch is pending; assume we missed the
13032176cfdSRui Paulo * beacon that would've completed the process and just
13132176cfdSRui Paulo * force the switch. If we made a mistake we'll not
13232176cfdSRui Paulo * find the AP on the new channel and fall back to a
13332176cfdSRui Paulo * normal scan.
13432176cfdSRui Paulo */
13532176cfdSRui Paulo ieee80211_csa_completeswitch(ic);
13632176cfdSRui Paulo return;
13732176cfdSRui Paulo }
13832176cfdSRui Paulo if (++vap->iv_bmiss_count < vap->iv_bmiss_max) {
13932176cfdSRui Paulo /*
14032176cfdSRui Paulo * Send a directed probe req before falling back to a
14132176cfdSRui Paulo * scan; if we receive a response ic_bmiss_count will
14232176cfdSRui Paulo * be reset. Some cards mistakenly report beacon miss
14332176cfdSRui Paulo * so this avoids the expensive scan if the ap is
14432176cfdSRui Paulo * still there.
14532176cfdSRui Paulo */
14632176cfdSRui Paulo ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr,
14732176cfdSRui Paulo vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid,
14832176cfdSRui Paulo vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen);
14932176cfdSRui Paulo return;
15032176cfdSRui Paulo }
151085ff963SMatthew Dillon
152085ff963SMatthew Dillon callout_stop(&vap->iv_swbmiss);
15332176cfdSRui Paulo vap->iv_bmiss_count = 0;
15432176cfdSRui Paulo vap->iv_stats.is_beacon_miss++;
15532176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
15632176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG
15732176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
15832176cfdSRui Paulo
15932176cfdSRui Paulo /*
16032176cfdSRui Paulo * If we receive a beacon miss interrupt when using
16132176cfdSRui Paulo * dynamic turbo, attempt to switch modes before
16232176cfdSRui Paulo * reassociating.
16332176cfdSRui Paulo */
16432176cfdSRui Paulo if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP))
16532176cfdSRui Paulo ieee80211_dturbo_switch(vap,
16632176cfdSRui Paulo ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
16732176cfdSRui Paulo #endif
16832176cfdSRui Paulo /*
16932176cfdSRui Paulo * Try to reassociate before scanning for a new ap.
17032176cfdSRui Paulo */
17132176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
17232176cfdSRui Paulo } else {
17332176cfdSRui Paulo /*
17432176cfdSRui Paulo * Somebody else is controlling state changes (e.g.
17532176cfdSRui Paulo * a user-mode app) don't do anything that would
17632176cfdSRui Paulo * confuse them; just drop into scan mode so they'll
17732176cfdSRui Paulo * notified of the state change and given control.
17832176cfdSRui Paulo */
17932176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
18032176cfdSRui Paulo }
18132176cfdSRui Paulo }
18232176cfdSRui Paulo
18332176cfdSRui Paulo /*
18432176cfdSRui Paulo * Handle deauth with reason. We retry only for
18532176cfdSRui Paulo * the cases where we might succeed. Otherwise
18632176cfdSRui Paulo * we downgrade the ap and scan.
18732176cfdSRui Paulo */
18832176cfdSRui Paulo static void
sta_authretry(struct ieee80211vap * vap,struct ieee80211_node * ni,int reason)18932176cfdSRui Paulo sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason)
19032176cfdSRui Paulo {
19132176cfdSRui Paulo switch (reason) {
19232176cfdSRui Paulo case IEEE80211_STATUS_SUCCESS: /* NB: MLME assoc */
19332176cfdSRui Paulo case IEEE80211_STATUS_TIMEOUT:
19432176cfdSRui Paulo case IEEE80211_REASON_ASSOC_EXPIRE:
19532176cfdSRui Paulo case IEEE80211_REASON_NOT_AUTHED:
19632176cfdSRui Paulo case IEEE80211_REASON_NOT_ASSOCED:
19732176cfdSRui Paulo case IEEE80211_REASON_ASSOC_LEAVE:
19832176cfdSRui Paulo case IEEE80211_REASON_ASSOC_NOT_AUTHED:
19932176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
20032176cfdSRui Paulo break;
20132176cfdSRui Paulo default:
20232176cfdSRui Paulo ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason);
20332176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
20432176cfdSRui Paulo ieee80211_check_scan_current(vap);
20532176cfdSRui Paulo break;
20632176cfdSRui Paulo }
20732176cfdSRui Paulo }
20832176cfdSRui Paulo
2094f655ef5SMatthew Dillon static void
sta_swbmiss_start(struct ieee80211vap * vap)2104f655ef5SMatthew Dillon sta_swbmiss_start(struct ieee80211vap *vap)
2114f655ef5SMatthew Dillon {
2124f655ef5SMatthew Dillon
2134f655ef5SMatthew Dillon if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) {
2144f655ef5SMatthew Dillon /*
2154f655ef5SMatthew Dillon * Start s/w beacon miss timer for devices w/o
2164f655ef5SMatthew Dillon * hardware support. We fudge a bit here since
2174f655ef5SMatthew Dillon * we're doing this in software.
2184f655ef5SMatthew Dillon */
2194f655ef5SMatthew Dillon vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS(
2204f655ef5SMatthew Dillon 2 * vap->iv_bmissthreshold * vap->iv_bss->ni_intval);
2214f655ef5SMatthew Dillon vap->iv_swbmiss_count = 0;
2224f655ef5SMatthew Dillon callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
2234f655ef5SMatthew Dillon ieee80211_swbmiss, vap);
2244f655ef5SMatthew Dillon }
2254f655ef5SMatthew Dillon }
2264f655ef5SMatthew Dillon
22732176cfdSRui Paulo /*
22832176cfdSRui Paulo * IEEE80211_M_STA vap state machine handler.
22932176cfdSRui Paulo * This routine handles the main states in the 802.11 protocol.
23032176cfdSRui Paulo */
23132176cfdSRui Paulo static int
sta_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)23232176cfdSRui Paulo sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
23332176cfdSRui Paulo {
23432176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
23532176cfdSRui Paulo struct ieee80211_node *ni;
23632176cfdSRui Paulo enum ieee80211_state ostate;
237085ff963SMatthew Dillon
238085ff963SMatthew Dillon IEEE80211_LOCK_ASSERT(ic);
239085ff963SMatthew Dillon
24032176cfdSRui Paulo ostate = vap->iv_state;
24132176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
24232176cfdSRui Paulo __func__, ieee80211_state_name[ostate],
24332176cfdSRui Paulo ieee80211_state_name[nstate], arg);
24432176cfdSRui Paulo vap->iv_state = nstate; /* state transition */
245085ff963SMatthew Dillon callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */
24632176cfdSRui Paulo if (ostate != IEEE80211_S_SCAN)
24732176cfdSRui Paulo ieee80211_cancel_scan(vap); /* background scan */
24832176cfdSRui Paulo ni = vap->iv_bss; /* NB: no reference held */
24932176cfdSRui Paulo if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
25032176cfdSRui Paulo callout_stop(&vap->iv_swbmiss);
25132176cfdSRui Paulo switch (nstate) {
25232176cfdSRui Paulo case IEEE80211_S_INIT:
25332176cfdSRui Paulo switch (ostate) {
25432176cfdSRui Paulo case IEEE80211_S_SLEEP:
25532176cfdSRui Paulo /* XXX wakeup */
256d98a0bcfSMatthew Dillon /* XXX driver hook to wakeup the hardware? */
25732176cfdSRui Paulo case IEEE80211_S_RUN:
25832176cfdSRui Paulo IEEE80211_SEND_MGMT(ni,
25932176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_DISASSOC,
26032176cfdSRui Paulo IEEE80211_REASON_ASSOC_LEAVE);
26132176cfdSRui Paulo ieee80211_sta_leave(ni);
26232176cfdSRui Paulo break;
26332176cfdSRui Paulo case IEEE80211_S_ASSOC:
26432176cfdSRui Paulo IEEE80211_SEND_MGMT(ni,
26532176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_DEAUTH,
26632176cfdSRui Paulo IEEE80211_REASON_AUTH_LEAVE);
26732176cfdSRui Paulo break;
26832176cfdSRui Paulo case IEEE80211_S_SCAN:
26932176cfdSRui Paulo ieee80211_cancel_scan(vap);
27032176cfdSRui Paulo break;
27132176cfdSRui Paulo default:
2724f655ef5SMatthew Dillon break;
27332176cfdSRui Paulo }
27432176cfdSRui Paulo if (ostate != IEEE80211_S_INIT) {
27532176cfdSRui Paulo /* NB: optimize INIT -> INIT case */
27632176cfdSRui Paulo ieee80211_reset_bss(vap);
27732176cfdSRui Paulo }
27832176cfdSRui Paulo if (vap->iv_auth->ia_detach != NULL)
27932176cfdSRui Paulo vap->iv_auth->ia_detach(vap);
28032176cfdSRui Paulo break;
28132176cfdSRui Paulo case IEEE80211_S_SCAN:
28232176cfdSRui Paulo switch (ostate) {
28332176cfdSRui Paulo case IEEE80211_S_INIT:
28432176cfdSRui Paulo /*
28532176cfdSRui Paulo * Initiate a scan. We can come here as a result
28632176cfdSRui Paulo * of an IEEE80211_IOC_SCAN_REQ too in which case
28732176cfdSRui Paulo * the vap will be marked with IEEE80211_FEXT_SCANREQ
28832176cfdSRui Paulo * and the scan request parameters will be present
28932176cfdSRui Paulo * in iv_scanreq. Otherwise we do the default.
29032176cfdSRui Paulo */
29132176cfdSRui Paulo if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
29232176cfdSRui Paulo ieee80211_check_scan(vap,
29332176cfdSRui Paulo vap->iv_scanreq_flags,
29432176cfdSRui Paulo vap->iv_scanreq_duration,
29532176cfdSRui Paulo vap->iv_scanreq_mindwell,
29632176cfdSRui Paulo vap->iv_scanreq_maxdwell,
29732176cfdSRui Paulo vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
29832176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
29932176cfdSRui Paulo } else
30032176cfdSRui Paulo ieee80211_check_scan_current(vap);
30132176cfdSRui Paulo break;
30232176cfdSRui Paulo case IEEE80211_S_SCAN:
30332176cfdSRui Paulo case IEEE80211_S_AUTH:
30432176cfdSRui Paulo case IEEE80211_S_ASSOC:
30532176cfdSRui Paulo /*
30632176cfdSRui Paulo * These can happen either because of a timeout
30732176cfdSRui Paulo * on an assoc/auth response or because of a
30832176cfdSRui Paulo * change in state that requires a reset. For
30932176cfdSRui Paulo * the former we're called with a non-zero arg
31032176cfdSRui Paulo * that is the cause for the failure; pass this
31132176cfdSRui Paulo * to the scan code so it can update state.
31232176cfdSRui Paulo * Otherwise trigger a new scan unless we're in
31332176cfdSRui Paulo * manual roaming mode in which case an application
31432176cfdSRui Paulo * must issue an explicit scan request.
31532176cfdSRui Paulo */
31632176cfdSRui Paulo if (arg != 0)
31732176cfdSRui Paulo ieee80211_scan_assoc_fail(vap,
31832176cfdSRui Paulo vap->iv_bss->ni_macaddr, arg);
31932176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
32032176cfdSRui Paulo ieee80211_check_scan_current(vap);
32132176cfdSRui Paulo break;
322d98a0bcfSMatthew Dillon case IEEE80211_S_SLEEP: /* beacon miss */
323d98a0bcfSMatthew Dillon /*
324d98a0bcfSMatthew Dillon * XXX if in sleep we need to wakeup the hardware.
325d98a0bcfSMatthew Dillon */
326d98a0bcfSMatthew Dillon /* FALLTHROUGH */
32732176cfdSRui Paulo case IEEE80211_S_RUN: /* beacon miss */
32832176cfdSRui Paulo /*
32932176cfdSRui Paulo * Beacon miss. Notify user space and if not
33032176cfdSRui Paulo * under control of a user application (roaming
33132176cfdSRui Paulo * manual) kick off a scan to re-connect.
33232176cfdSRui Paulo */
333d98a0bcfSMatthew Dillon
33432176cfdSRui Paulo ieee80211_sta_leave(ni);
33532176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
33632176cfdSRui Paulo ieee80211_check_scan_current(vap);
33732176cfdSRui Paulo break;
33832176cfdSRui Paulo default:
33932176cfdSRui Paulo goto invalid;
34032176cfdSRui Paulo }
34132176cfdSRui Paulo break;
34232176cfdSRui Paulo case IEEE80211_S_AUTH:
34332176cfdSRui Paulo switch (ostate) {
34432176cfdSRui Paulo case IEEE80211_S_INIT:
34532176cfdSRui Paulo case IEEE80211_S_SCAN:
34632176cfdSRui Paulo IEEE80211_SEND_MGMT(ni,
34732176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 1);
34832176cfdSRui Paulo break;
34932176cfdSRui Paulo case IEEE80211_S_AUTH:
35032176cfdSRui Paulo case IEEE80211_S_ASSOC:
35132176cfdSRui Paulo switch (arg & 0xff) {
35232176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_AUTH:
35332176cfdSRui Paulo /* ??? */
35432176cfdSRui Paulo IEEE80211_SEND_MGMT(ni,
35532176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 2);
35632176cfdSRui Paulo break;
35732176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_DEAUTH:
35832176cfdSRui Paulo sta_authretry(vap, ni, arg>>8);
35932176cfdSRui Paulo break;
36032176cfdSRui Paulo }
36132176cfdSRui Paulo break;
3624f655ef5SMatthew Dillon case IEEE80211_S_SLEEP:
36332176cfdSRui Paulo case IEEE80211_S_RUN:
36432176cfdSRui Paulo switch (arg & 0xff) {
36532176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_AUTH:
36632176cfdSRui Paulo IEEE80211_SEND_MGMT(ni,
36732176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 2);
3684f655ef5SMatthew Dillon vap->iv_state = IEEE80211_S_RUN; /* stay RUN */
36932176cfdSRui Paulo break;
37032176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_DEAUTH:
37132176cfdSRui Paulo ieee80211_sta_leave(ni);
37232176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
37332176cfdSRui Paulo /* try to reauth */
37432176cfdSRui Paulo IEEE80211_SEND_MGMT(ni,
37532176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 1);
37632176cfdSRui Paulo }
37732176cfdSRui Paulo break;
37832176cfdSRui Paulo }
37932176cfdSRui Paulo break;
38032176cfdSRui Paulo default:
38132176cfdSRui Paulo goto invalid;
38232176cfdSRui Paulo }
38332176cfdSRui Paulo break;
38432176cfdSRui Paulo case IEEE80211_S_ASSOC:
38532176cfdSRui Paulo switch (ostate) {
38632176cfdSRui Paulo case IEEE80211_S_AUTH:
38732176cfdSRui Paulo case IEEE80211_S_ASSOC:
38832176cfdSRui Paulo IEEE80211_SEND_MGMT(ni,
38932176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
39032176cfdSRui Paulo break;
39132176cfdSRui Paulo case IEEE80211_S_SLEEP: /* cannot happen */
39232176cfdSRui Paulo case IEEE80211_S_RUN:
39332176cfdSRui Paulo ieee80211_sta_leave(ni);
39432176cfdSRui Paulo if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
39532176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, arg ?
39632176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
39732176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
39832176cfdSRui Paulo }
39932176cfdSRui Paulo break;
40032176cfdSRui Paulo default:
40132176cfdSRui Paulo goto invalid;
40232176cfdSRui Paulo }
40332176cfdSRui Paulo break;
40432176cfdSRui Paulo case IEEE80211_S_RUN:
40532176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_WPA) {
40632176cfdSRui Paulo /* XXX validate prerequisites */
40732176cfdSRui Paulo }
40832176cfdSRui Paulo switch (ostate) {
40932176cfdSRui Paulo case IEEE80211_S_RUN:
41032176cfdSRui Paulo case IEEE80211_S_CSA:
41132176cfdSRui Paulo break;
41232176cfdSRui Paulo case IEEE80211_S_AUTH: /* when join is done in fw */
41332176cfdSRui Paulo case IEEE80211_S_ASSOC:
41432176cfdSRui Paulo #ifdef IEEE80211_DEBUG
41532176cfdSRui Paulo if (ieee80211_msg_debug(vap)) {
4161e290df3SAntonio Huete Jimenez ieee80211_note(vap, "%s with %s ssid ",
41732176cfdSRui Paulo (vap->iv_opmode == IEEE80211_M_STA ?
41832176cfdSRui Paulo "associated" : "synchronized"),
419085ff963SMatthew Dillon ether_sprintf(ni->ni_bssid));
42032176cfdSRui Paulo ieee80211_print_essid(vap->iv_bss->ni_essid,
42132176cfdSRui Paulo ni->ni_esslen);
42232176cfdSRui Paulo /* XXX MCS/HT */
4236168f72eSRui Paulo kprintf(" channel %d start %uMb\n",
42432176cfdSRui Paulo ieee80211_chan2ieee(ic, ic->ic_curchan),
42532176cfdSRui Paulo IEEE80211_RATE2MBS(ni->ni_txrate));
42632176cfdSRui Paulo }
42732176cfdSRui Paulo #endif
42832176cfdSRui Paulo ieee80211_scan_assoc_success(vap, ni->ni_macaddr);
42932176cfdSRui Paulo ieee80211_notify_node_join(ni,
43032176cfdSRui Paulo arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
43132176cfdSRui Paulo break;
43232176cfdSRui Paulo case IEEE80211_S_SLEEP:
433d98a0bcfSMatthew Dillon /* Wake up from sleep */
434d98a0bcfSMatthew Dillon vap->iv_sta_ps(vap, 0);
43532176cfdSRui Paulo break;
43632176cfdSRui Paulo default:
43732176cfdSRui Paulo goto invalid;
43832176cfdSRui Paulo }
43932176cfdSRui Paulo ieee80211_sync_curchan(ic);
4404f655ef5SMatthew Dillon if (ostate != IEEE80211_S_RUN)
4414f655ef5SMatthew Dillon sta_swbmiss_start(vap);
44232176cfdSRui Paulo /*
44332176cfdSRui Paulo * When 802.1x is not in use mark the port authorized
44432176cfdSRui Paulo * at this point so traffic can flow.
44532176cfdSRui Paulo */
44632176cfdSRui Paulo if (ni->ni_authmode != IEEE80211_AUTH_8021X)
44732176cfdSRui Paulo ieee80211_node_authorize(ni);
44832176cfdSRui Paulo /*
44932176cfdSRui Paulo * Fake association when joining an existing bss.
450d98a0bcfSMatthew Dillon *
451d98a0bcfSMatthew Dillon * Don't do this if we're doing SLEEP->RUN.
45232176cfdSRui Paulo */
453d98a0bcfSMatthew Dillon if (ic->ic_newassoc != NULL && ostate != IEEE80211_S_SLEEP)
454d98a0bcfSMatthew Dillon ic->ic_newassoc(vap->iv_bss, (ostate != IEEE80211_S_RUN));
45532176cfdSRui Paulo break;
45632176cfdSRui Paulo case IEEE80211_S_CSA:
45732176cfdSRui Paulo if (ostate != IEEE80211_S_RUN)
45832176cfdSRui Paulo goto invalid;
45932176cfdSRui Paulo break;
46032176cfdSRui Paulo case IEEE80211_S_SLEEP:
4614f655ef5SMatthew Dillon sta_swbmiss_start(vap);
462085ff963SMatthew Dillon vap->iv_sta_ps(vap, 1);
46332176cfdSRui Paulo break;
46432176cfdSRui Paulo default:
46532176cfdSRui Paulo invalid:
46632176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
46732176cfdSRui Paulo "%s: unexpected state transition %s -> %s\n", __func__,
46832176cfdSRui Paulo ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
46932176cfdSRui Paulo break;
47032176cfdSRui Paulo }
47132176cfdSRui Paulo return 0;
47232176cfdSRui Paulo }
47332176cfdSRui Paulo
47432176cfdSRui Paulo /*
47532176cfdSRui Paulo * Return non-zero if the frame is an echo of a multicast
47632176cfdSRui Paulo * frame sent by ourself. The dir is known to be DSTODS.
47732176cfdSRui Paulo */
47832176cfdSRui Paulo static __inline int
isdstods_mcastecho(struct ieee80211vap * vap,const struct ieee80211_frame * wh)47932176cfdSRui Paulo isdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
48032176cfdSRui Paulo {
48132176cfdSRui Paulo #define QWH4(wh) ((const struct ieee80211_qosframe_addr4 *)wh)
48232176cfdSRui Paulo #define WH4(wh) ((const struct ieee80211_frame_addr4 *)wh)
48332176cfdSRui Paulo const uint8_t *sa;
48432176cfdSRui Paulo
48532176cfdSRui Paulo KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
48632176cfdSRui Paulo
48732176cfdSRui Paulo if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
48832176cfdSRui Paulo return 0;
48932176cfdSRui Paulo sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4;
49032176cfdSRui Paulo return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr);
49132176cfdSRui Paulo #undef WH4
49232176cfdSRui Paulo #undef QWH4
49332176cfdSRui Paulo }
49432176cfdSRui Paulo
49532176cfdSRui Paulo /*
49632176cfdSRui Paulo * Return non-zero if the frame is an echo of a multicast
49732176cfdSRui Paulo * frame sent by ourself. The dir is known to be FROMDS.
49832176cfdSRui Paulo */
49932176cfdSRui Paulo static __inline int
isfromds_mcastecho(struct ieee80211vap * vap,const struct ieee80211_frame * wh)50032176cfdSRui Paulo isfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
50132176cfdSRui Paulo {
50232176cfdSRui Paulo KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
50332176cfdSRui Paulo
50432176cfdSRui Paulo if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
50532176cfdSRui Paulo return 0;
50632176cfdSRui Paulo return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
50732176cfdSRui Paulo }
50832176cfdSRui Paulo
50932176cfdSRui Paulo /*
51032176cfdSRui Paulo * Decide if a received management frame should be
51132176cfdSRui Paulo * printed when debugging is enabled. This filters some
51232176cfdSRui Paulo * of the less interesting frames that come frequently
51332176cfdSRui Paulo * (e.g. beacons).
51432176cfdSRui Paulo */
51532176cfdSRui Paulo static __inline int
doprint(struct ieee80211vap * vap,int subtype)51632176cfdSRui Paulo doprint(struct ieee80211vap *vap, int subtype)
51732176cfdSRui Paulo {
51832176cfdSRui Paulo switch (subtype) {
51932176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_BEACON:
52032176cfdSRui Paulo return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
52132176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
52232176cfdSRui Paulo return 0;
52332176cfdSRui Paulo }
52432176cfdSRui Paulo return 1;
52532176cfdSRui Paulo }
52632176cfdSRui Paulo
52732176cfdSRui Paulo /*
52832176cfdSRui Paulo * Process a received frame. The node associated with the sender
52932176cfdSRui Paulo * should be supplied. If nothing was found in the node table then
53032176cfdSRui Paulo * the caller is assumed to supply a reference to iv_bss instead.
53132176cfdSRui Paulo * The RSSI and a timestamp are also supplied. The RSSI data is used
53232176cfdSRui Paulo * during AP scanning to select a AP to associate with; it can have
53332176cfdSRui Paulo * any units so long as values have consistent units and higher values
53432176cfdSRui Paulo * mean ``better signal''. The receive timestamp is currently not used
53532176cfdSRui Paulo * by the 802.11 layer.
53632176cfdSRui Paulo */
53732176cfdSRui Paulo static int
sta_input(struct ieee80211_node * ni,struct mbuf * m,const struct ieee80211_rx_stats * rxs,int rssi,int nf)5384f898719SImre Vadász sta_input(struct ieee80211_node *ni, struct mbuf *m,
5394f898719SImre Vadász const struct ieee80211_rx_stats *rxs, int rssi, int nf)
54032176cfdSRui Paulo {
54132176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
54232176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
54332176cfdSRui Paulo struct ifnet *ifp = vap->iv_ifp;
54432176cfdSRui Paulo struct ieee80211_frame *wh;
54532176cfdSRui Paulo struct ieee80211_key *key;
54632176cfdSRui Paulo struct ether_header *eh;
54732176cfdSRui Paulo int hdrspace, need_tap = 1; /* mbuf need to be tapped. */
54832176cfdSRui Paulo uint8_t dir, type, subtype, qos;
54932176cfdSRui Paulo uint8_t *bssid;
55032176cfdSRui Paulo
55132176cfdSRui Paulo if (m->m_flags & M_AMPDU_MPDU) {
55232176cfdSRui Paulo /*
55332176cfdSRui Paulo * Fastpath for A-MPDU reorder q resubmission. Frames
55432176cfdSRui Paulo * w/ M_AMPDU_MPDU marked have already passed through
55532176cfdSRui Paulo * here but were received out of order and been held on
55632176cfdSRui Paulo * the reorder queue. When resubmitted they are marked
55732176cfdSRui Paulo * with the M_AMPDU_MPDU flag and we can bypass most of
55832176cfdSRui Paulo * the normal processing.
55932176cfdSRui Paulo */
56032176cfdSRui Paulo wh = mtod(m, struct ieee80211_frame *);
56132176cfdSRui Paulo type = IEEE80211_FC0_TYPE_DATA;
56232176cfdSRui Paulo dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
56332176cfdSRui Paulo subtype = IEEE80211_FC0_SUBTYPE_QOS;
56432176cfdSRui Paulo hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
56532176cfdSRui Paulo goto resubmit_ampdu;
56632176cfdSRui Paulo }
56732176cfdSRui Paulo
56832176cfdSRui Paulo KASSERT(ni != NULL, ("null node"));
56932176cfdSRui Paulo ni->ni_inact = ni->ni_inact_reload;
57032176cfdSRui Paulo
57132176cfdSRui Paulo type = -1; /* undefined */
57232176cfdSRui Paulo
57332176cfdSRui Paulo if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
57432176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
57532176cfdSRui Paulo ni->ni_macaddr, NULL,
57632176cfdSRui Paulo "too short (1): len %u", m->m_pkthdr.len);
57732176cfdSRui Paulo vap->iv_stats.is_rx_tooshort++;
57832176cfdSRui Paulo goto out;
57932176cfdSRui Paulo }
58032176cfdSRui Paulo /*
58132176cfdSRui Paulo * Bit of a cheat here, we use a pointer for a 3-address
58232176cfdSRui Paulo * frame format but don't reference fields past outside
58332176cfdSRui Paulo * ieee80211_frame_min w/o first validating the data is
58432176cfdSRui Paulo * present.
58532176cfdSRui Paulo */
58632176cfdSRui Paulo wh = mtod(m, struct ieee80211_frame *);
58732176cfdSRui Paulo
58832176cfdSRui Paulo if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
58932176cfdSRui Paulo IEEE80211_FC0_VERSION_0) {
59032176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
59132176cfdSRui Paulo ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x",
59232176cfdSRui Paulo wh->i_fc[0], wh->i_fc[1]);
59332176cfdSRui Paulo vap->iv_stats.is_rx_badversion++;
59432176cfdSRui Paulo goto err;
59532176cfdSRui Paulo }
59632176cfdSRui Paulo
59732176cfdSRui Paulo dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
59832176cfdSRui Paulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
59932176cfdSRui Paulo subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
60032176cfdSRui Paulo if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
60132176cfdSRui Paulo bssid = wh->i_addr2;
60232176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
60332176cfdSRui Paulo /* not interested in */
60432176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
60532176cfdSRui Paulo bssid, NULL, "%s", "not to bss");
60632176cfdSRui Paulo vap->iv_stats.is_rx_wrongbss++;
60732176cfdSRui Paulo goto out;
60832176cfdSRui Paulo }
609085ff963SMatthew Dillon
610085ff963SMatthew Dillon /*
611085ff963SMatthew Dillon * Some devices may be in a promiscuous mode
612085ff963SMatthew Dillon * where they receive frames for multiple station
613085ff963SMatthew Dillon * addresses.
614085ff963SMatthew Dillon *
615085ff963SMatthew Dillon * If we receive a data frame that isn't
616085ff963SMatthew Dillon * destined to our VAP MAC, drop it.
617085ff963SMatthew Dillon *
618085ff963SMatthew Dillon * XXX TODO: This is only enforced when not scanning;
619085ff963SMatthew Dillon * XXX it assumes a software-driven scan will put the NIC
620085ff963SMatthew Dillon * XXX into a "no data frames" mode before setting this
621085ff963SMatthew Dillon * XXX flag. Otherwise it may be possible that we'll still
622085ff963SMatthew Dillon * XXX process data frames whilst scanning.
623085ff963SMatthew Dillon */
624085ff963SMatthew Dillon if ((! IEEE80211_IS_MULTICAST(wh->i_addr1))
625085ff963SMatthew Dillon && (! IEEE80211_ADDR_EQ(wh->i_addr1, IF_LLADDR(ifp)))) {
626085ff963SMatthew Dillon IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
6274f655ef5SMatthew Dillon #if defined(__DragonFly__)
628f92fae3fSSascha Wildner bssid, NULL, "not to cur sta: lladdr=%s, addr1=%s",
629f92fae3fSSascha Wildner ether_sprintf(IF_LLADDR(ifp)),
630f92fae3fSSascha Wildner ether_sprintf(wh->i_addr1));
6314f655ef5SMatthew Dillon #else
6324f655ef5SMatthew Dillon bssid, NULL, "not to cur sta: lladdr=%6D, addr1=%6D",
6334f655ef5SMatthew Dillon IF_LLADDR(ifp), ":", wh->i_addr1, ":");
6344f655ef5SMatthew Dillon #endif
635085ff963SMatthew Dillon vap->iv_stats.is_rx_wrongbss++;
636085ff963SMatthew Dillon goto out;
637085ff963SMatthew Dillon }
638085ff963SMatthew Dillon
63932176cfdSRui Paulo IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
64032176cfdSRui Paulo ni->ni_noise = nf;
6414f898719SImre Vadász if ( IEEE80211_HAS_SEQ(type, subtype) &&
6424f898719SImre Vadász !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
64332176cfdSRui Paulo uint8_t tid = ieee80211_gettid(wh);
64432176cfdSRui Paulo if (IEEE80211_QOS_HAS_SEQ(wh) &&
64532176cfdSRui Paulo TID_TO_WME_AC(tid) >= WME_AC_VI)
64632176cfdSRui Paulo ic->ic_wme.wme_hipri_traffic++;
6474f655ef5SMatthew Dillon if (! ieee80211_check_rxseq(ni, wh, bssid))
64832176cfdSRui Paulo goto out;
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",
8894f655ef5SMatthew Dillon ieee80211_mgt_subtype_name(subtype),
890085ff963SMatthew Dillon ether_sprintf(wh->i_addr2), rssi);
89132176cfdSRui Paulo }
89232176cfdSRui Paulo #endif
893085ff963SMatthew Dillon if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
89432176cfdSRui Paulo if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
89532176cfdSRui Paulo /*
89632176cfdSRui Paulo * Only shared key auth frames with a challenge
89732176cfdSRui Paulo * should be encrypted, discard all others.
89832176cfdSRui Paulo */
89932176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
9004f655ef5SMatthew Dillon wh, ieee80211_mgt_subtype_name(subtype),
90132176cfdSRui Paulo "%s", "WEP set but not permitted");
90232176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
90332176cfdSRui Paulo goto out;
90432176cfdSRui Paulo }
90532176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
90632176cfdSRui Paulo /*
90732176cfdSRui Paulo * Discard encrypted frames when privacy is off.
90832176cfdSRui Paulo */
90932176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
91032176cfdSRui Paulo wh, "mgt", "%s", "WEP set but PRIVACY off");
91132176cfdSRui Paulo vap->iv_stats.is_rx_noprivacy++;
91232176cfdSRui Paulo goto out;
91332176cfdSRui Paulo }
91432176cfdSRui Paulo hdrspace = ieee80211_hdrspace(ic, wh);
91532176cfdSRui Paulo key = ieee80211_crypto_decap(ni, m, hdrspace);
91632176cfdSRui Paulo if (key == NULL) {
91732176cfdSRui Paulo /* NB: stats+msgs handled in crypto_decap */
91832176cfdSRui Paulo goto out;
91932176cfdSRui Paulo }
92032176cfdSRui Paulo wh = mtod(m, struct ieee80211_frame *);
921085ff963SMatthew Dillon wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
92232176cfdSRui Paulo }
9234f898719SImre Vadász vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
92432176cfdSRui Paulo goto out;
92532176cfdSRui Paulo
92632176cfdSRui Paulo case IEEE80211_FC0_TYPE_CTL:
92732176cfdSRui Paulo vap->iv_stats.is_rx_ctl++;
92832176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_ctrl);
92932176cfdSRui Paulo vap->iv_recv_ctl(ni, m, subtype);
93032176cfdSRui Paulo goto out;
93132176cfdSRui Paulo
93232176cfdSRui Paulo default:
93332176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
93432176cfdSRui Paulo wh, NULL, "bad frame type 0x%x", type);
93532176cfdSRui Paulo /* should not come here */
93632176cfdSRui Paulo break;
93732176cfdSRui Paulo }
93832176cfdSRui Paulo err:
9394f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
94032176cfdSRui Paulo out:
94132176cfdSRui Paulo if (m != NULL) {
94232176cfdSRui Paulo if (need_tap && ieee80211_radiotap_active_vap(vap))
94332176cfdSRui Paulo ieee80211_radiotap_rx(vap, m);
94432176cfdSRui Paulo m_freem(m);
94532176cfdSRui Paulo }
94632176cfdSRui Paulo return type;
94732176cfdSRui Paulo }
94832176cfdSRui Paulo
94932176cfdSRui Paulo static void
sta_auth_open(struct ieee80211_node * ni,struct ieee80211_frame * wh,int rssi,int nf,uint16_t seq,uint16_t status)95032176cfdSRui Paulo sta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
95132176cfdSRui Paulo int rssi, int nf, uint16_t seq, uint16_t status)
95232176cfdSRui Paulo {
95332176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
95432176cfdSRui Paulo
95532176cfdSRui Paulo if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
95632176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
95732176cfdSRui Paulo ni->ni_macaddr, "open auth",
95832176cfdSRui Paulo "bad sta auth mode %u", ni->ni_authmode);
95932176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++; /* XXX */
96032176cfdSRui Paulo return;
96132176cfdSRui Paulo }
96232176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_AUTH ||
96332176cfdSRui Paulo seq != IEEE80211_AUTH_OPEN_RESPONSE) {
96432176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++;
96532176cfdSRui Paulo return;
96632176cfdSRui Paulo }
96732176cfdSRui Paulo if (status != 0) {
96832176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
96932176cfdSRui Paulo ni, "open auth failed (reason %d)", status);
97032176cfdSRui Paulo vap->iv_stats.is_rx_auth_fail++;
97132176cfdSRui Paulo vap->iv_stats.is_rx_authfail_code = status;
97232176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN,
97332176cfdSRui Paulo IEEE80211_SCAN_FAIL_STATUS);
97432176cfdSRui Paulo } else
97532176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
97632176cfdSRui Paulo }
97732176cfdSRui Paulo
97832176cfdSRui Paulo static void
sta_auth_shared(struct ieee80211_node * ni,struct ieee80211_frame * wh,uint8_t * frm,uint8_t * efrm,int rssi,int nf,uint16_t seq,uint16_t status)97932176cfdSRui Paulo sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
98032176cfdSRui Paulo uint8_t *frm, uint8_t *efrm, int rssi, int nf,
98132176cfdSRui Paulo uint16_t seq, uint16_t status)
98232176cfdSRui Paulo {
98332176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
98432176cfdSRui Paulo uint8_t *challenge;
985085ff963SMatthew Dillon int estatus;
98632176cfdSRui Paulo
98732176cfdSRui Paulo /*
98832176cfdSRui Paulo * NB: this can happen as we allow pre-shared key
98932176cfdSRui Paulo * authentication to be enabled w/o wep being turned
99032176cfdSRui Paulo * on so that configuration of these can be done
99132176cfdSRui Paulo * in any order. It may be better to enforce the
99232176cfdSRui Paulo * ordering in which case this check would just be
99332176cfdSRui Paulo * for sanity/consistency.
99432176cfdSRui Paulo */
99532176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
99632176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
99732176cfdSRui Paulo ni->ni_macaddr, "shared key auth",
99832176cfdSRui Paulo "%s", " PRIVACY is disabled");
999085ff963SMatthew Dillon estatus = IEEE80211_STATUS_ALG;
100032176cfdSRui Paulo goto bad;
100132176cfdSRui Paulo }
100232176cfdSRui Paulo /*
100332176cfdSRui Paulo * Pre-shared key authentication is evil; accept
100432176cfdSRui Paulo * it only if explicitly configured (it is supported
100532176cfdSRui Paulo * mainly for compatibility with clients like OS X).
100632176cfdSRui Paulo */
100732176cfdSRui Paulo if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
100832176cfdSRui Paulo ni->ni_authmode != IEEE80211_AUTH_SHARED) {
100932176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
101032176cfdSRui Paulo ni->ni_macaddr, "shared key auth",
101132176cfdSRui Paulo "bad sta auth mode %u", ni->ni_authmode);
101232176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
1013085ff963SMatthew Dillon estatus = IEEE80211_STATUS_ALG;
101432176cfdSRui Paulo goto bad;
101532176cfdSRui Paulo }
101632176cfdSRui Paulo
101732176cfdSRui Paulo challenge = NULL;
101832176cfdSRui Paulo if (frm + 1 < efrm) {
101932176cfdSRui Paulo if ((frm[1] + 2) > (efrm - frm)) {
102032176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
102132176cfdSRui Paulo ni->ni_macaddr, "shared key auth",
10222c7ccc4aSSascha Wildner "ie %d/%ld too long",
1023085ff963SMatthew Dillon frm[0], (frm[1] + 2) - (efrm - frm));
102432176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++;
1025085ff963SMatthew Dillon estatus = IEEE80211_STATUS_CHALLENGE;
102632176cfdSRui Paulo goto bad;
102732176cfdSRui Paulo }
102832176cfdSRui Paulo if (*frm == IEEE80211_ELEMID_CHALLENGE)
102932176cfdSRui Paulo challenge = frm;
103032176cfdSRui Paulo frm += frm[1] + 2;
103132176cfdSRui Paulo }
103232176cfdSRui Paulo switch (seq) {
103332176cfdSRui Paulo case IEEE80211_AUTH_SHARED_CHALLENGE:
103432176cfdSRui Paulo case IEEE80211_AUTH_SHARED_RESPONSE:
103532176cfdSRui Paulo if (challenge == NULL) {
103632176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
103732176cfdSRui Paulo ni->ni_macaddr, "shared key auth",
103832176cfdSRui Paulo "%s", "no challenge");
103932176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++;
1040085ff963SMatthew Dillon estatus = IEEE80211_STATUS_CHALLENGE;
104132176cfdSRui Paulo goto bad;
104232176cfdSRui Paulo }
104332176cfdSRui Paulo if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
104432176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
104532176cfdSRui Paulo ni->ni_macaddr, "shared key auth",
104632176cfdSRui Paulo "bad challenge len %d", challenge[1]);
104732176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++;
1048085ff963SMatthew Dillon estatus = IEEE80211_STATUS_CHALLENGE;
104932176cfdSRui Paulo goto bad;
105032176cfdSRui Paulo }
105132176cfdSRui Paulo default:
105232176cfdSRui Paulo break;
105332176cfdSRui Paulo }
105432176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_AUTH)
105532176cfdSRui Paulo return;
105632176cfdSRui Paulo switch (seq) {
105732176cfdSRui Paulo case IEEE80211_AUTH_SHARED_PASS:
105832176cfdSRui Paulo if (ni->ni_challenge != NULL) {
10594f655ef5SMatthew Dillon IEEE80211_FREE(ni->ni_challenge, M_80211_NODE);
106032176cfdSRui Paulo ni->ni_challenge = NULL;
106132176cfdSRui Paulo }
106232176cfdSRui Paulo if (status != 0) {
106332176cfdSRui Paulo IEEE80211_NOTE_FRAME(vap,
106432176cfdSRui Paulo IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh,
106532176cfdSRui Paulo "shared key auth failed (reason %d)", status);
106632176cfdSRui Paulo vap->iv_stats.is_rx_auth_fail++;
106732176cfdSRui Paulo vap->iv_stats.is_rx_authfail_code = status;
106832176cfdSRui Paulo return;
106932176cfdSRui Paulo }
107032176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
107132176cfdSRui Paulo break;
107232176cfdSRui Paulo case IEEE80211_AUTH_SHARED_CHALLENGE:
107332176cfdSRui Paulo if (!ieee80211_alloc_challenge(ni))
107432176cfdSRui Paulo return;
107532176cfdSRui Paulo /* XXX could optimize by passing recvd challenge */
107632176cfdSRui Paulo memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
107732176cfdSRui Paulo IEEE80211_SEND_MGMT(ni,
107832176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
107932176cfdSRui Paulo break;
108032176cfdSRui Paulo default:
108132176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH,
108232176cfdSRui Paulo wh, "shared key auth", "bad seq %d", seq);
108332176cfdSRui Paulo vap->iv_stats.is_rx_bad_auth++;
108432176cfdSRui Paulo return;
108532176cfdSRui Paulo }
108632176cfdSRui Paulo return;
108732176cfdSRui Paulo bad:
108832176cfdSRui Paulo /*
108932176cfdSRui Paulo * Kick the state machine. This short-circuits
109032176cfdSRui Paulo * using the mgt frame timeout to trigger the
109132176cfdSRui Paulo * state transition.
109232176cfdSRui Paulo */
109332176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_AUTH)
109432176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN,
109532176cfdSRui Paulo IEEE80211_SCAN_FAIL_STATUS);
109632176cfdSRui Paulo }
109732176cfdSRui Paulo
1098085ff963SMatthew Dillon int
ieee80211_parse_wmeparams(struct ieee80211vap * vap,uint8_t * frm,const struct ieee80211_frame * wh)109932176cfdSRui Paulo ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
110032176cfdSRui Paulo const struct ieee80211_frame *wh)
110132176cfdSRui Paulo {
110232176cfdSRui Paulo #define MS(_v, _f) (((_v) & _f) >> _f##_S)
110332176cfdSRui Paulo struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme;
110432176cfdSRui Paulo u_int len = frm[1], qosinfo;
110532176cfdSRui Paulo int i;
110632176cfdSRui Paulo
110732176cfdSRui Paulo if (len < sizeof(struct ieee80211_wme_param)-2) {
110832176cfdSRui Paulo IEEE80211_DISCARD_IE(vap,
110932176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
111032176cfdSRui Paulo wh, "WME", "too short, len %u", len);
111132176cfdSRui Paulo return -1;
111232176cfdSRui Paulo }
111332176cfdSRui Paulo qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
111432176cfdSRui Paulo qosinfo &= WME_QOSINFO_COUNT;
111532176cfdSRui Paulo /* XXX do proper check for wraparound */
111632176cfdSRui Paulo if (qosinfo == wme->wme_wmeChanParams.cap_info)
111732176cfdSRui Paulo return 0;
111832176cfdSRui Paulo frm += __offsetof(struct ieee80211_wme_param, params_acParams);
111932176cfdSRui Paulo for (i = 0; i < WME_NUM_AC; i++) {
112032176cfdSRui Paulo struct wmeParams *wmep =
112132176cfdSRui Paulo &wme->wme_wmeChanParams.cap_wmeParams[i];
112232176cfdSRui Paulo /* NB: ACI not used */
112332176cfdSRui Paulo wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
112432176cfdSRui Paulo wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
112532176cfdSRui Paulo wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
112632176cfdSRui Paulo wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
11274f655ef5SMatthew Dillon wmep->wmep_txopLimit = le16dec(frm+2);
112832176cfdSRui Paulo frm += 4;
112932176cfdSRui Paulo }
113032176cfdSRui Paulo wme->wme_wmeChanParams.cap_info = qosinfo;
113132176cfdSRui Paulo return 1;
113232176cfdSRui Paulo #undef MS
113332176cfdSRui Paulo }
113432176cfdSRui Paulo
113532176cfdSRui Paulo /*
113632176cfdSRui Paulo * Process 11h Channel Switch Announcement (CSA) ie. If this
113732176cfdSRui Paulo * is the first CSA then initiate the switch. Otherwise we
113832176cfdSRui Paulo * track state and trigger completion and/or cancel of the switch.
113932176cfdSRui Paulo * XXX should be public for IBSS use
114032176cfdSRui Paulo */
114132176cfdSRui Paulo static void
ieee80211_parse_csaparams(struct ieee80211vap * vap,uint8_t * frm,const struct ieee80211_frame * wh)114232176cfdSRui Paulo ieee80211_parse_csaparams(struct ieee80211vap *vap, uint8_t *frm,
114332176cfdSRui Paulo const struct ieee80211_frame *wh)
114432176cfdSRui Paulo {
114532176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
114632176cfdSRui Paulo const struct ieee80211_csa_ie *csa =
114732176cfdSRui Paulo (const struct ieee80211_csa_ie *) frm;
114832176cfdSRui Paulo
114932176cfdSRui Paulo KASSERT(vap->iv_state >= IEEE80211_S_RUN,
115032176cfdSRui Paulo ("state %s", ieee80211_state_name[vap->iv_state]));
115132176cfdSRui Paulo
115232176cfdSRui Paulo if (csa->csa_mode > 1) {
115332176cfdSRui Paulo IEEE80211_DISCARD_IE(vap,
115432176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH,
115532176cfdSRui Paulo wh, "CSA", "invalid mode %u", csa->csa_mode);
115632176cfdSRui Paulo return;
115732176cfdSRui Paulo }
1158085ff963SMatthew Dillon IEEE80211_LOCK(ic);
115932176cfdSRui Paulo if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) {
116032176cfdSRui Paulo /*
116132176cfdSRui Paulo * Convert the channel number to a channel reference. We
116232176cfdSRui Paulo * try first to preserve turbo attribute of the current
116332176cfdSRui Paulo * channel then fallback. Note this will not work if the
116432176cfdSRui Paulo * CSA specifies a channel that requires a band switch (e.g.
116532176cfdSRui Paulo * 11a => 11g). This is intentional as 11h is defined only
116632176cfdSRui Paulo * for 5GHz/11a and because the switch does not involve a
116732176cfdSRui Paulo * reassociation, protocol state (capabilities, negotated
116832176cfdSRui Paulo * rates, etc) may/will be wrong.
116932176cfdSRui Paulo */
117032176cfdSRui Paulo struct ieee80211_channel *c =
117132176cfdSRui Paulo ieee80211_find_channel_byieee(ic, csa->csa_newchan,
117232176cfdSRui Paulo (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_ALLTURBO));
117332176cfdSRui Paulo if (c == NULL) {
117432176cfdSRui Paulo c = ieee80211_find_channel_byieee(ic,
117532176cfdSRui Paulo csa->csa_newchan,
117632176cfdSRui Paulo (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_ALL));
117732176cfdSRui Paulo if (c == NULL) {
117832176cfdSRui Paulo IEEE80211_DISCARD_IE(vap,
117932176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH,
118032176cfdSRui Paulo wh, "CSA", "invalid channel %u",
118132176cfdSRui Paulo csa->csa_newchan);
118232176cfdSRui Paulo goto done;
118332176cfdSRui Paulo }
118432176cfdSRui Paulo }
118532176cfdSRui Paulo #if IEEE80211_CSA_COUNT_MIN > 0
118632176cfdSRui Paulo if (csa->csa_count < IEEE80211_CSA_COUNT_MIN) {
118732176cfdSRui Paulo /*
118832176cfdSRui Paulo * Require at least IEEE80211_CSA_COUNT_MIN count to
118932176cfdSRui Paulo * reduce the risk of being redirected by a fabricated
119032176cfdSRui Paulo * CSA. If a valid CSA is dropped we'll still get a
119132176cfdSRui Paulo * beacon miss when the AP leaves the channel so we'll
119232176cfdSRui Paulo * eventually follow to the new channel.
119332176cfdSRui Paulo *
119432176cfdSRui Paulo * NOTE: this violates the 11h spec that states that
119532176cfdSRui Paulo * count may be any value and if 0 then a switch
119632176cfdSRui Paulo * should happen asap.
119732176cfdSRui Paulo */
119832176cfdSRui Paulo IEEE80211_DISCARD_IE(vap,
119932176cfdSRui Paulo IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH,
120032176cfdSRui Paulo wh, "CSA", "count %u too small, must be >= %u",
120132176cfdSRui Paulo csa->csa_count, IEEE80211_CSA_COUNT_MIN);
120232176cfdSRui Paulo goto done;
120332176cfdSRui Paulo }
120432176cfdSRui Paulo #endif
120532176cfdSRui Paulo ieee80211_csa_startswitch(ic, c, csa->csa_mode, csa->csa_count);
120632176cfdSRui Paulo } else {
120732176cfdSRui Paulo /*
120832176cfdSRui Paulo * Validate this ie against the initial CSA. We require
120932176cfdSRui Paulo * mode and channel not change and the count must be
121032176cfdSRui Paulo * monotonically decreasing. This may be pointless and
121132176cfdSRui Paulo * canceling the switch as a result may be too paranoid but
121232176cfdSRui Paulo * in the worst case if we drop out of CSA because of this
121332176cfdSRui Paulo * and the AP does move then we'll just end up taking a
121432176cfdSRui Paulo * beacon miss and scan to find the AP.
121532176cfdSRui Paulo *
121632176cfdSRui Paulo * XXX may want <= on count as we also process ProbeResp
121732176cfdSRui Paulo * frames and those may come in w/ the same count as the
121832176cfdSRui Paulo * previous beacon; but doing so leaves us open to a stuck
121932176cfdSRui Paulo * count until we add a dead-man timer
122032176cfdSRui Paulo */
122132176cfdSRui Paulo if (!(csa->csa_count < ic->ic_csa_count &&
122232176cfdSRui Paulo csa->csa_mode == ic->ic_csa_mode &&
122332176cfdSRui Paulo csa->csa_newchan == ieee80211_chan2ieee(ic, ic->ic_csa_newchan))) {
122432176cfdSRui Paulo IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_DOTH, wh,
122532176cfdSRui Paulo "CSA ie mismatch, initial ie <%d,%d,%d>, "
122632176cfdSRui Paulo "this ie <%d,%d,%d>", ic->ic_csa_mode,
12272c7ccc4aSSascha Wildner ic->ic_csa_newchan->ic_ieee, ic->ic_csa_count,
122832176cfdSRui Paulo csa->csa_mode, csa->csa_newchan, csa->csa_count);
122932176cfdSRui Paulo ieee80211_csa_cancelswitch(ic);
123032176cfdSRui Paulo } else {
123132176cfdSRui Paulo if (csa->csa_count <= 1)
123232176cfdSRui Paulo ieee80211_csa_completeswitch(ic);
123332176cfdSRui Paulo else
123432176cfdSRui Paulo ic->ic_csa_count = csa->csa_count;
123532176cfdSRui Paulo }
123632176cfdSRui Paulo }
123732176cfdSRui Paulo done:
1238085ff963SMatthew Dillon IEEE80211_UNLOCK(ic);
123932176cfdSRui Paulo }
124032176cfdSRui Paulo
124132176cfdSRui Paulo /*
124232176cfdSRui Paulo * Return non-zero if a background scan may be continued:
124332176cfdSRui Paulo * o bg scan is active
124432176cfdSRui Paulo * o no channel switch is pending
124532176cfdSRui Paulo * o there has not been any traffic recently
1246*3aca8a44SImre Vadász * o no full-offload scan support (no need for explicitly continuing scan then)
124732176cfdSRui Paulo *
124832176cfdSRui Paulo * Note we do not check if there is an administrative enable;
124932176cfdSRui Paulo * this is only done to start the scan. We assume that any
125032176cfdSRui Paulo * change in state will be accompanied by a request to cancel
125132176cfdSRui Paulo * active scans which will otherwise cause this test to fail.
125232176cfdSRui Paulo */
125332176cfdSRui Paulo static __inline int
contbgscan(struct ieee80211vap * vap)125432176cfdSRui Paulo contbgscan(struct ieee80211vap *vap)
125532176cfdSRui Paulo {
125632176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
125732176cfdSRui Paulo
125832176cfdSRui Paulo return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
125932176cfdSRui Paulo (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
1260*3aca8a44SImre Vadász !(vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) &&
126132176cfdSRui Paulo vap->iv_state == IEEE80211_S_RUN && /* XXX? */
12624f655ef5SMatthew Dillon ieee80211_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
1271*3aca8a44SImre Vadász * o there has not been any traffic recently (don't check if full-offload scan)
127232176cfdSRui Paulo */
127332176cfdSRui Paulo static __inline int
startbgscan(struct ieee80211vap * vap)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
12834f655ef5SMatthew Dillon ieee80211_time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) &&
1284*3aca8a44SImre Vadász ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) ||
1285*3aca8a44SImre Vadász ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)));
128632176cfdSRui Paulo }
128732176cfdSRui Paulo
128832176cfdSRui Paulo static void
sta_recv_mgmt(struct ieee80211_node * ni,struct mbuf * m0,int subtype,const struct ieee80211_rx_stats * rxs,int rssi,int nf)12894f898719SImre Vadász sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
12904f898719SImre Vadász const struct ieee80211_rx_stats *rxs,
12914f898719SImre Vadász int rssi, int nf)
129232176cfdSRui Paulo {
129332176cfdSRui Paulo #define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
129432176cfdSRui Paulo #define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
129532176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
129632176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic;
12974f898719SImre Vadász struct ieee80211_channel *rxchan = ic->ic_curchan;
129832176cfdSRui Paulo struct ieee80211_frame *wh;
129932176cfdSRui Paulo uint8_t *frm, *efrm;
130032176cfdSRui Paulo uint8_t *rates, *xrates, *wme, *htcap, *htinfo;
130132176cfdSRui Paulo uint8_t rate;
1302085ff963SMatthew Dillon int ht_state_change = 0;
130332176cfdSRui Paulo
130432176cfdSRui Paulo wh = mtod(m0, struct ieee80211_frame *);
130532176cfdSRui Paulo frm = (uint8_t *)&wh[1];
130632176cfdSRui Paulo efrm = mtod(m0, uint8_t *) + m0->m_len;
130732176cfdSRui Paulo switch (subtype) {
130832176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
130932176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_BEACON: {
131032176cfdSRui Paulo struct ieee80211_scanparams scan;
13114f898719SImre Vadász struct ieee80211_channel *c;
131232176cfdSRui Paulo /*
131332176cfdSRui Paulo * We process beacon/probe response frames:
131432176cfdSRui Paulo * o when scanning, or
131532176cfdSRui Paulo * o station mode when associated (to collect state
131632176cfdSRui Paulo * updates such as 802.11g slot time)
131732176cfdSRui Paulo * Frames otherwise received are discarded.
131832176cfdSRui Paulo */
131932176cfdSRui Paulo if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) {
132032176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
132132176cfdSRui Paulo return;
132232176cfdSRui Paulo }
13234f655ef5SMatthew Dillon
13244f898719SImre Vadász /* Override RX channel as appropriate */
13254f898719SImre Vadász if (rxs != NULL) {
13264f898719SImre Vadász c = ieee80211_lookup_channel_rxstatus(vap, rxs);
13274f898719SImre Vadász if (c != NULL)
13284f898719SImre Vadász rxchan = c;
13294f898719SImre Vadász }
13304f655ef5SMatthew Dillon
133132176cfdSRui Paulo /* XXX probe response in sta mode when !scanning? */
13324f898719SImre Vadász if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0) {
1333085ff963SMatthew Dillon if (! (ic->ic_flags & IEEE80211_F_SCAN))
1334085ff963SMatthew Dillon vap->iv_stats.is_beacon_bad++;
133532176cfdSRui Paulo return;
1336085ff963SMatthew Dillon }
1337085ff963SMatthew Dillon
133832176cfdSRui Paulo /*
133932176cfdSRui Paulo * Count frame now that we know it's to be processed.
134032176cfdSRui Paulo */
134132176cfdSRui Paulo if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
134232176cfdSRui Paulo vap->iv_stats.is_rx_beacon++; /* XXX remove */
134332176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_beacons);
134432176cfdSRui Paulo } else
134532176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_proberesp);
134632176cfdSRui Paulo /*
134732176cfdSRui Paulo * When operating in station mode, check for state updates.
134832176cfdSRui Paulo * Be careful to ignore beacons received while doing a
134932176cfdSRui Paulo * background scan. We consider only 11g/WMM stuff right now.
135032176cfdSRui Paulo */
135132176cfdSRui Paulo if (ni->ni_associd != 0 &&
135232176cfdSRui Paulo ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
135332176cfdSRui Paulo IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
135432176cfdSRui Paulo /* record tsf of last beacon */
135532176cfdSRui Paulo memcpy(ni->ni_tstamp.data, scan.tstamp,
135632176cfdSRui Paulo sizeof(ni->ni_tstamp));
135732176cfdSRui Paulo /* count beacon frame for s/w bmiss handling */
135832176cfdSRui Paulo vap->iv_swbmiss_count++;
135932176cfdSRui Paulo vap->iv_bmiss_count = 0;
136032176cfdSRui Paulo if (ni->ni_erp != scan.erp) {
136132176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
136232176cfdSRui Paulo wh->i_addr2,
136332176cfdSRui Paulo "erp change: was 0x%x, now 0x%x",
136432176cfdSRui Paulo ni->ni_erp, scan.erp);
136532176cfdSRui Paulo if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
136632176cfdSRui Paulo (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
136732176cfdSRui Paulo ic->ic_flags |= IEEE80211_F_USEPROT;
136832176cfdSRui Paulo else
136932176cfdSRui Paulo ic->ic_flags &= ~IEEE80211_F_USEPROT;
137032176cfdSRui Paulo ni->ni_erp = scan.erp;
137132176cfdSRui Paulo /* XXX statistic */
137232176cfdSRui Paulo /* XXX driver notification */
137332176cfdSRui Paulo }
137432176cfdSRui Paulo if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
137532176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
137632176cfdSRui Paulo wh->i_addr2,
137732176cfdSRui Paulo "capabilities change: was 0x%x, now 0x%x",
137832176cfdSRui Paulo ni->ni_capinfo, scan.capinfo);
137932176cfdSRui Paulo /*
138032176cfdSRui Paulo * NB: we assume short preamble doesn't
138132176cfdSRui Paulo * change dynamically
138232176cfdSRui Paulo */
138332176cfdSRui Paulo ieee80211_set_shortslottime(ic,
138432176cfdSRui Paulo IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
138532176cfdSRui Paulo (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
138632176cfdSRui Paulo ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
138732176cfdSRui Paulo | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
138832176cfdSRui Paulo /* XXX statistic */
138932176cfdSRui Paulo }
139032176cfdSRui Paulo if (scan.wme != NULL &&
139132176cfdSRui Paulo (ni->ni_flags & IEEE80211_NODE_QOS) &&
139232176cfdSRui Paulo ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0)
139332176cfdSRui Paulo ieee80211_wme_updateparams(vap);
139432176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG
139532176cfdSRui Paulo if (scan.ath != NULL)
139632176cfdSRui Paulo ieee80211_parse_athparams(ni, scan.ath, wh);
139732176cfdSRui Paulo #endif
139832176cfdSRui Paulo if (scan.htcap != NULL && scan.htinfo != NULL &&
139932176cfdSRui Paulo (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
140032176cfdSRui Paulo /* XXX state changes? */
1401085ff963SMatthew Dillon if (ieee80211_ht_updateparams(ni,
1402085ff963SMatthew Dillon scan.htcap, scan.htinfo))
1403085ff963SMatthew Dillon ht_state_change = 1;
140432176cfdSRui Paulo }
1405085ff963SMatthew Dillon if (scan.quiet)
1406085ff963SMatthew Dillon ic->ic_set_quiet(ni, scan.quiet);
1407d98a0bcfSMatthew Dillon
140832176cfdSRui Paulo if (scan.tim != NULL) {
140932176cfdSRui Paulo struct ieee80211_tim_ie *tim =
141032176cfdSRui Paulo (struct ieee80211_tim_ie *) scan.tim;
1411d98a0bcfSMatthew Dillon /*
1412d98a0bcfSMatthew Dillon * XXX Check/debug this code; see if it's about
1413d98a0bcfSMatthew Dillon * the right time to force the VAP awake if we
1414d98a0bcfSMatthew Dillon * receive a frame destined for us?
1415d98a0bcfSMatthew Dillon */
141632176cfdSRui Paulo int aid = IEEE80211_AID(ni->ni_associd);
141732176cfdSRui Paulo int ix = aid / NBBY;
141832176cfdSRui Paulo int min = tim->tim_bitctl &~ 1;
141932176cfdSRui Paulo int max = tim->tim_len + min - 4;
14204f898719SImre Vadász int tim_ucast = 0, tim_mcast = 0;
1421d98a0bcfSMatthew Dillon
142232176cfdSRui Paulo /*
1423d98a0bcfSMatthew Dillon * Only do this for unicast traffic in the TIM
1424d98a0bcfSMatthew Dillon * The multicast traffic notification for
1425d98a0bcfSMatthew Dillon * the scan notification stuff should occur
1426d98a0bcfSMatthew Dillon * differently.
142732176cfdSRui Paulo */
1428d98a0bcfSMatthew Dillon if (min <= ix && ix <= max &&
1429d98a0bcfSMatthew Dillon isset(tim->tim_bitmap - min, aid)) {
14304f898719SImre Vadász tim_ucast = 1;
1431d98a0bcfSMatthew Dillon }
1432d98a0bcfSMatthew Dillon
1433d98a0bcfSMatthew Dillon /*
14344f898719SImre Vadász * Do a separate notification
1435d98a0bcfSMatthew Dillon * for the multicast bit being set.
1436d98a0bcfSMatthew Dillon */
1437d98a0bcfSMatthew Dillon if (tim->tim_bitctl & 1) {
14384f898719SImre Vadász tim_mcast = 1;
14394f898719SImre Vadász }
14404f898719SImre Vadász
14414f898719SImre Vadász /*
14424f898719SImre Vadász * If the TIM indicates there's traffic for
14434f898719SImre Vadász * us then get us out of STA mode powersave.
14444f898719SImre Vadász */
14454f898719SImre Vadász if (tim_ucast == 1) {
14464f898719SImre Vadász
14474f898719SImre Vadász /*
14484f898719SImre Vadász * Wake us out of SLEEP state if we're
14494f898719SImre Vadász * in it; and if we're doing bgscan
14504f898719SImre Vadász * then wake us out of STA powersave.
14514f898719SImre Vadász */
1452d98a0bcfSMatthew Dillon ieee80211_sta_tim_notify(vap, 1);
14534f898719SImre Vadász
14544f898719SImre Vadász /*
14554f898719SImre Vadász * This is preventing us from
14564f898719SImre Vadász * continuing a bgscan; because it
14574f898719SImre Vadász * tricks the contbgscan()
14584f898719SImre Vadász * routine to think there's always
14594f898719SImre Vadász * traffic for us.
14604f898719SImre Vadász *
14614f898719SImre Vadász * I think we need both an RX and
14624f898719SImre Vadász * TX ic_lastdata field.
14634f898719SImre Vadász */
1464d98a0bcfSMatthew Dillon ic->ic_lastdata = ticks;
146532176cfdSRui Paulo }
1466085ff963SMatthew Dillon
146732176cfdSRui Paulo ni->ni_dtim_count = tim->tim_count;
146832176cfdSRui Paulo ni->ni_dtim_period = tim->tim_period;
146932176cfdSRui Paulo }
147032176cfdSRui Paulo if (scan.csa != NULL &&
147132176cfdSRui Paulo (vap->iv_flags & IEEE80211_F_DOTH))
147232176cfdSRui Paulo ieee80211_parse_csaparams(vap, scan.csa, wh);
147332176cfdSRui Paulo else if (ic->ic_flags & IEEE80211_F_CSAPENDING) {
147432176cfdSRui Paulo /*
147532176cfdSRui Paulo * No CSA ie or 11h disabled, but a channel
147632176cfdSRui Paulo * switch is pending; drop out so we aren't
147732176cfdSRui Paulo * stuck in CSA state. If the AP really is
147832176cfdSRui Paulo * moving we'll get a beacon miss and scan.
147932176cfdSRui Paulo */
1480085ff963SMatthew Dillon IEEE80211_LOCK(ic);
148132176cfdSRui Paulo ieee80211_csa_cancelswitch(ic);
1482085ff963SMatthew Dillon IEEE80211_UNLOCK(ic);
148332176cfdSRui Paulo }
148432176cfdSRui Paulo /*
148532176cfdSRui Paulo * If scanning, pass the info to the scan module.
148632176cfdSRui Paulo * Otherwise, check if it's the right time to do
148732176cfdSRui Paulo * a background scan. Background scanning must
148832176cfdSRui Paulo * be enabled and we must not be operating in the
148932176cfdSRui Paulo * turbo phase of dynamic turbo mode. Then,
149032176cfdSRui Paulo * it's been a while since the last background
149132176cfdSRui Paulo * scan and if no data frames have come through
149232176cfdSRui Paulo * recently, kick off a scan. Note that this
149332176cfdSRui Paulo * is the mechanism by which a background scan
149432176cfdSRui Paulo * is started _and_ continued each time we
149532176cfdSRui Paulo * return on-channel to receive a beacon from
149632176cfdSRui Paulo * our ap.
149732176cfdSRui Paulo */
149832176cfdSRui Paulo if (ic->ic_flags & IEEE80211_F_SCAN) {
14994f898719SImre Vadász ieee80211_add_scan(vap, rxchan,
15004f898719SImre Vadász &scan, wh, subtype, rssi, nf);
150132176cfdSRui Paulo } else if (contbgscan(vap)) {
150232176cfdSRui Paulo ieee80211_bg_scan(vap, 0);
150332176cfdSRui Paulo } else if (startbgscan(vap)) {
150432176cfdSRui Paulo vap->iv_stats.is_scan_bg++;
150532176cfdSRui Paulo #if 0
150632176cfdSRui Paulo /* wakeup if we are sleeing */
150732176cfdSRui Paulo ieee80211_set_pwrsave(vap, 0);
150832176cfdSRui Paulo #endif
150932176cfdSRui Paulo ieee80211_bg_scan(vap, 0);
151032176cfdSRui Paulo }
1511d98a0bcfSMatthew Dillon
1512d98a0bcfSMatthew Dillon /*
1513d98a0bcfSMatthew Dillon * Put the station to sleep if we haven't seen
1514d98a0bcfSMatthew Dillon * traffic in a while.
1515d98a0bcfSMatthew Dillon */
1516085ff963SMatthew Dillon IEEE80211_LOCK(ic);
1517d98a0bcfSMatthew Dillon ieee80211_sta_ps_timer_check(vap);
1518085ff963SMatthew Dillon IEEE80211_UNLOCK(ic);
1519d98a0bcfSMatthew Dillon
1520085ff963SMatthew Dillon /*
1521085ff963SMatthew Dillon * If we've had a channel width change (eg HT20<->HT40)
1522085ff963SMatthew Dillon * then schedule a delayed driver notification.
1523085ff963SMatthew Dillon */
1524085ff963SMatthew Dillon if (ht_state_change)
1525085ff963SMatthew Dillon ieee80211_update_chw(ic);
152632176cfdSRui Paulo return;
152732176cfdSRui Paulo }
152832176cfdSRui Paulo /*
152932176cfdSRui Paulo * If scanning, just pass information to the scan module.
153032176cfdSRui Paulo */
153132176cfdSRui Paulo if (ic->ic_flags & IEEE80211_F_SCAN) {
153232176cfdSRui Paulo if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
153332176cfdSRui Paulo /*
153432176cfdSRui Paulo * Actively scanning a channel marked passive;
153532176cfdSRui Paulo * send a probe request now that we know there
153632176cfdSRui Paulo * is 802.11 traffic present.
153732176cfdSRui Paulo *
153832176cfdSRui Paulo * XXX check if the beacon we recv'd gives
153932176cfdSRui Paulo * us what we need and suppress the probe req
154032176cfdSRui Paulo */
154132176cfdSRui Paulo ieee80211_probe_curchan(vap, 1);
154232176cfdSRui Paulo ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
154332176cfdSRui Paulo }
15444f898719SImre Vadász ieee80211_add_scan(vap, rxchan, &scan, wh,
15454f898719SImre Vadász subtype, rssi, nf);
154632176cfdSRui Paulo return;
154732176cfdSRui Paulo }
154832176cfdSRui Paulo break;
154932176cfdSRui Paulo }
155032176cfdSRui Paulo
155132176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_AUTH: {
155232176cfdSRui Paulo uint16_t algo, seq, status;
155332176cfdSRui Paulo /*
155432176cfdSRui Paulo * auth frame format
155532176cfdSRui Paulo * [2] algorithm
155632176cfdSRui Paulo * [2] sequence
155732176cfdSRui Paulo * [2] status
155832176cfdSRui Paulo * [tlv*] challenge
155932176cfdSRui Paulo */
156032176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
156132176cfdSRui Paulo algo = le16toh(*(uint16_t *)frm);
156232176cfdSRui Paulo seq = le16toh(*(uint16_t *)(frm + 2));
156332176cfdSRui Paulo status = le16toh(*(uint16_t *)(frm + 4));
156432176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
156532176cfdSRui Paulo "recv auth frame with algorithm %d seq %d", algo, seq);
156632176cfdSRui Paulo
156732176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_COUNTERM) {
156832176cfdSRui Paulo IEEE80211_DISCARD(vap,
156932176cfdSRui Paulo IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
157032176cfdSRui Paulo wh, "auth", "%s", "TKIP countermeasures enabled");
157132176cfdSRui Paulo vap->iv_stats.is_rx_auth_countermeasures++;
157232176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
157332176cfdSRui Paulo ieee80211_send_error(ni, wh->i_addr2,
157432176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH,
157532176cfdSRui Paulo IEEE80211_REASON_MIC_FAILURE);
157632176cfdSRui Paulo }
157732176cfdSRui Paulo return;
157832176cfdSRui Paulo }
157932176cfdSRui Paulo if (algo == IEEE80211_AUTH_ALG_SHARED)
158032176cfdSRui Paulo sta_auth_shared(ni, wh, frm + 6, efrm, rssi, nf,
158132176cfdSRui Paulo seq, status);
158232176cfdSRui Paulo else if (algo == IEEE80211_AUTH_ALG_OPEN)
158332176cfdSRui Paulo sta_auth_open(ni, wh, rssi, nf, seq, status);
158432176cfdSRui Paulo else {
158532176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
158632176cfdSRui Paulo wh, "auth", "unsupported alg %d", algo);
158732176cfdSRui Paulo vap->iv_stats.is_rx_auth_unsupported++;
158832176cfdSRui Paulo return;
158932176cfdSRui Paulo }
159032176cfdSRui Paulo break;
159132176cfdSRui Paulo }
159232176cfdSRui Paulo
159332176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
159432176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
159532176cfdSRui Paulo uint16_t capinfo, associd;
159632176cfdSRui Paulo uint16_t status;
159732176cfdSRui Paulo
159832176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_ASSOC) {
159932176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
160032176cfdSRui Paulo return;
160132176cfdSRui Paulo }
160232176cfdSRui Paulo
160332176cfdSRui Paulo /*
160432176cfdSRui Paulo * asresp frame format
160532176cfdSRui Paulo * [2] capability information
160632176cfdSRui Paulo * [2] status
160732176cfdSRui Paulo * [2] association ID
160832176cfdSRui Paulo * [tlv] supported rates
160932176cfdSRui Paulo * [tlv] extended supported rates
161032176cfdSRui Paulo * [tlv] WME
161132176cfdSRui Paulo * [tlv] HT capabilities
161232176cfdSRui Paulo * [tlv] HT info
161332176cfdSRui Paulo */
161432176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
161532176cfdSRui Paulo ni = vap->iv_bss;
161632176cfdSRui Paulo capinfo = le16toh(*(uint16_t *)frm);
161732176cfdSRui Paulo frm += 2;
161832176cfdSRui Paulo status = le16toh(*(uint16_t *)frm);
161932176cfdSRui Paulo frm += 2;
162032176cfdSRui Paulo if (status != 0) {
162132176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
162232176cfdSRui Paulo wh->i_addr2, "%sassoc failed (reason %d)",
162332176cfdSRui Paulo ISREASSOC(subtype) ? "re" : "", status);
162432176cfdSRui Paulo vap->iv_stats.is_rx_auth_fail++; /* XXX */
162532176cfdSRui Paulo return;
162632176cfdSRui Paulo }
162732176cfdSRui Paulo associd = le16toh(*(uint16_t *)frm);
162832176cfdSRui Paulo frm += 2;
162932176cfdSRui Paulo
163032176cfdSRui Paulo rates = xrates = wme = htcap = htinfo = NULL;
163132176cfdSRui Paulo while (efrm - frm > 1) {
163232176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
163332176cfdSRui Paulo switch (*frm) {
163432176cfdSRui Paulo case IEEE80211_ELEMID_RATES:
163532176cfdSRui Paulo rates = frm;
163632176cfdSRui Paulo break;
163732176cfdSRui Paulo case IEEE80211_ELEMID_XRATES:
163832176cfdSRui Paulo xrates = frm;
163932176cfdSRui Paulo break;
164032176cfdSRui Paulo case IEEE80211_ELEMID_HTCAP:
164132176cfdSRui Paulo htcap = frm;
164232176cfdSRui Paulo break;
164332176cfdSRui Paulo case IEEE80211_ELEMID_HTINFO:
164432176cfdSRui Paulo htinfo = frm;
164532176cfdSRui Paulo break;
164632176cfdSRui Paulo case IEEE80211_ELEMID_VENDOR:
164732176cfdSRui Paulo if (iswmeoui(frm))
164832176cfdSRui Paulo wme = frm;
164932176cfdSRui Paulo else if (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) {
165032176cfdSRui Paulo /*
165132176cfdSRui Paulo * Accept pre-draft HT ie's if the
165232176cfdSRui Paulo * standard ones have not been seen.
165332176cfdSRui Paulo */
165432176cfdSRui Paulo if (ishtcapoui(frm)) {
165532176cfdSRui Paulo if (htcap == NULL)
165632176cfdSRui Paulo htcap = frm;
165732176cfdSRui Paulo } else if (ishtinfooui(frm)) {
165832176cfdSRui Paulo if (htinfo == NULL)
1659085ff963SMatthew Dillon htinfo = frm;
166032176cfdSRui Paulo }
166132176cfdSRui Paulo }
166232176cfdSRui Paulo /* XXX Atheros OUI support */
166332176cfdSRui Paulo break;
166432176cfdSRui Paulo }
166532176cfdSRui Paulo frm += frm[1] + 2;
166632176cfdSRui Paulo }
166732176cfdSRui Paulo
166832176cfdSRui Paulo IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
166932176cfdSRui Paulo if (xrates != NULL)
167032176cfdSRui Paulo IEEE80211_VERIFY_ELEMENT(xrates,
167132176cfdSRui Paulo IEEE80211_RATE_MAXSIZE - rates[1], return);
167232176cfdSRui Paulo rate = ieee80211_setup_rates(ni, rates, xrates,
167332176cfdSRui Paulo IEEE80211_F_JOIN |
167432176cfdSRui Paulo IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
167532176cfdSRui Paulo IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
167632176cfdSRui Paulo if (rate & IEEE80211_RATE_BASIC) {
167732176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
167832176cfdSRui Paulo wh->i_addr2,
167932176cfdSRui Paulo "%sassoc failed (rate set mismatch)",
168032176cfdSRui Paulo ISREASSOC(subtype) ? "re" : "");
168132176cfdSRui Paulo vap->iv_stats.is_rx_assoc_norate++;
168232176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN,
168332176cfdSRui Paulo IEEE80211_SCAN_FAIL_STATUS);
168432176cfdSRui Paulo return;
168532176cfdSRui Paulo }
168632176cfdSRui Paulo
168732176cfdSRui Paulo ni->ni_capinfo = capinfo;
168832176cfdSRui Paulo ni->ni_associd = associd;
168932176cfdSRui Paulo if (ni->ni_jointime == 0)
1690cec73927SMatthew Dillon ni->ni_jointime = time_uptime;
169132176cfdSRui Paulo if (wme != NULL &&
169232176cfdSRui Paulo ieee80211_parse_wmeparams(vap, wme, wh) >= 0) {
169332176cfdSRui Paulo ni->ni_flags |= IEEE80211_NODE_QOS;
169432176cfdSRui Paulo ieee80211_wme_updateparams(vap);
169532176cfdSRui Paulo } else
169632176cfdSRui Paulo ni->ni_flags &= ~IEEE80211_NODE_QOS;
169732176cfdSRui Paulo /*
169832176cfdSRui Paulo * Setup HT state according to the negotiation.
169932176cfdSRui Paulo *
170032176cfdSRui Paulo * NB: shouldn't need to check if HT use is enabled but some
170132176cfdSRui Paulo * ap's send back HT ie's even when we don't indicate we
170232176cfdSRui Paulo * are HT capable in our AssocReq.
170332176cfdSRui Paulo */
170432176cfdSRui Paulo if (htcap != NULL && htinfo != NULL &&
170532176cfdSRui Paulo (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
170632176cfdSRui Paulo ieee80211_ht_node_init(ni);
170732176cfdSRui Paulo ieee80211_ht_updateparams(ni, htcap, htinfo);
170832176cfdSRui Paulo ieee80211_setup_htrates(ni, htcap,
170932176cfdSRui Paulo IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
171032176cfdSRui Paulo ieee80211_setup_basic_htrates(ni, htinfo);
171132176cfdSRui Paulo ieee80211_node_setuptxparms(ni);
17124fbce6bdSSascha Wildner ieee80211_ratectl_node_init(ni);
17134f655ef5SMatthew Dillon }
17144f655ef5SMatthew Dillon
17154f655ef5SMatthew Dillon /*
17164f655ef5SMatthew Dillon * Always initialise FF/superg state; we can use this
17174f655ef5SMatthew Dillon * for doing A-MSDU encapsulation as well.
17184f655ef5SMatthew Dillon */
171932176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG
172032176cfdSRui Paulo ieee80211_ff_node_init(ni);
172132176cfdSRui Paulo #endif
17224f655ef5SMatthew Dillon
172332176cfdSRui Paulo /*
172432176cfdSRui Paulo * Configure state now that we are associated.
172532176cfdSRui Paulo *
172632176cfdSRui Paulo * XXX may need different/additional driver callbacks?
172732176cfdSRui Paulo */
172832176cfdSRui Paulo if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
172932176cfdSRui Paulo (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
173032176cfdSRui Paulo ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
173132176cfdSRui Paulo ic->ic_flags &= ~IEEE80211_F_USEBARKER;
173232176cfdSRui Paulo } else {
173332176cfdSRui Paulo ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
173432176cfdSRui Paulo ic->ic_flags |= IEEE80211_F_USEBARKER;
173532176cfdSRui Paulo }
173632176cfdSRui Paulo ieee80211_set_shortslottime(ic,
173732176cfdSRui Paulo IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
173832176cfdSRui Paulo (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
173932176cfdSRui Paulo /*
174032176cfdSRui Paulo * Honor ERP protection.
174132176cfdSRui Paulo *
174232176cfdSRui Paulo * NB: ni_erp should zero for non-11g operation.
174332176cfdSRui Paulo */
174432176cfdSRui Paulo if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
174532176cfdSRui Paulo (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
174632176cfdSRui Paulo ic->ic_flags |= IEEE80211_F_USEPROT;
174732176cfdSRui Paulo else
174832176cfdSRui Paulo ic->ic_flags &= ~IEEE80211_F_USEPROT;
174932176cfdSRui Paulo IEEE80211_NOTE_MAC(vap,
175032176cfdSRui Paulo IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2,
175132176cfdSRui Paulo "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s",
175232176cfdSRui Paulo ISREASSOC(subtype) ? "re" : "",
175332176cfdSRui Paulo IEEE80211_NODE_AID(ni),
175432176cfdSRui Paulo ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
175532176cfdSRui Paulo ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
175632176cfdSRui Paulo ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
175732176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
175832176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_HT ?
175932176cfdSRui Paulo (ni->ni_chw == 40 ? ", HT40" : ", HT20") : "",
176032176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
176132176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_MIMO_RTS ? " (+SMPS-DYN)" :
176232176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_MIMO_PS ? " (+SMPS)" : "",
176332176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_RIFS ? " (+RIFS)" : "",
176432176cfdSRui Paulo IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
176532176cfdSRui Paulo ", fast-frames" : "",
176632176cfdSRui Paulo IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ?
176732176cfdSRui Paulo ", turbo" : ""
176832176cfdSRui Paulo );
176932176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_RUN, subtype);
177032176cfdSRui Paulo break;
177132176cfdSRui Paulo }
177232176cfdSRui Paulo
177332176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_DEAUTH: {
177432176cfdSRui Paulo uint16_t reason;
177532176cfdSRui Paulo
177632176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_SCAN) {
177732176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
177832176cfdSRui Paulo return;
177932176cfdSRui Paulo }
178032176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
178132176cfdSRui Paulo /* NB: can happen when in promiscuous mode */
178232176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
178332176cfdSRui Paulo break;
178432176cfdSRui Paulo }
178532176cfdSRui Paulo
178632176cfdSRui Paulo /*
178732176cfdSRui Paulo * deauth frame format
178832176cfdSRui Paulo * [2] reason
178932176cfdSRui Paulo */
179032176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
179132176cfdSRui Paulo reason = le16toh(*(uint16_t *)frm);
179232176cfdSRui Paulo
179332176cfdSRui Paulo vap->iv_stats.is_rx_deauth++;
179432176cfdSRui Paulo vap->iv_stats.is_rx_deauth_code = reason;
179532176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_deauth);
179632176cfdSRui Paulo
179732176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
17984f655ef5SMatthew Dillon "recv deauthenticate (reason: %d (%s))", reason,
17994f655ef5SMatthew Dillon ieee80211_reason_to_string(reason));
180032176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_AUTH,
180132176cfdSRui Paulo (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
180232176cfdSRui Paulo break;
180332176cfdSRui Paulo }
180432176cfdSRui Paulo
180532176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_DISASSOC: {
180632176cfdSRui Paulo uint16_t reason;
180732176cfdSRui Paulo
180832176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_RUN &&
180932176cfdSRui Paulo vap->iv_state != IEEE80211_S_ASSOC &&
181032176cfdSRui Paulo vap->iv_state != IEEE80211_S_AUTH) {
181132176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
181232176cfdSRui Paulo return;
181332176cfdSRui Paulo }
181432176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
181532176cfdSRui Paulo /* NB: can happen when in promiscuous mode */
181632176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
181732176cfdSRui Paulo break;
181832176cfdSRui Paulo }
181932176cfdSRui Paulo
182032176cfdSRui Paulo /*
182132176cfdSRui Paulo * disassoc frame format
182232176cfdSRui Paulo * [2] reason
182332176cfdSRui Paulo */
182432176cfdSRui Paulo IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
182532176cfdSRui Paulo reason = le16toh(*(uint16_t *)frm);
182632176cfdSRui Paulo
182732176cfdSRui Paulo vap->iv_stats.is_rx_disassoc++;
182832176cfdSRui Paulo vap->iv_stats.is_rx_disassoc_code = reason;
182932176cfdSRui Paulo IEEE80211_NODE_STAT(ni, rx_disassoc);
183032176cfdSRui Paulo
183132176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
18324f655ef5SMatthew Dillon "recv disassociate (reason: %d (%s))", reason,
18334f655ef5SMatthew Dillon ieee80211_reason_to_string(reason));
183432176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
183532176cfdSRui Paulo break;
183632176cfdSRui Paulo }
183732176cfdSRui Paulo
183832176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ACTION:
1839085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
1840085ff963SMatthew Dillon if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) &&
1841085ff963SMatthew Dillon !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1842085ff963SMatthew Dillon IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1843085ff963SMatthew Dillon wh, NULL, "%s", "not for us");
1844085ff963SMatthew Dillon vap->iv_stats.is_rx_mgtdiscard++;
1845085ff963SMatthew Dillon } else if (vap->iv_state != IEEE80211_S_RUN) {
1846085ff963SMatthew Dillon IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1847085ff963SMatthew Dillon wh, NULL, "wrong state %s",
1848085ff963SMatthew Dillon ieee80211_state_name[vap->iv_state]);
1849085ff963SMatthew Dillon vap->iv_stats.is_rx_mgtdiscard++;
1850085ff963SMatthew Dillon } else {
185132176cfdSRui Paulo if (ieee80211_parse_action(ni, m0) == 0)
1852085ff963SMatthew Dillon (void)ic->ic_recv_action(ni, wh, frm, efrm);
1853085ff963SMatthew Dillon }
1854085ff963SMatthew Dillon break;
1855085ff963SMatthew Dillon
1856085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
1857085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
1858085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
18594f655ef5SMatthew Dillon case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
1860085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_ATIM:
1861085ff963SMatthew Dillon IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
1862085ff963SMatthew Dillon wh, NULL, "%s", "not handled");
186332176cfdSRui Paulo vap->iv_stats.is_rx_mgtdiscard++;
186432176cfdSRui Paulo break;
186532176cfdSRui Paulo
186632176cfdSRui Paulo default:
186732176cfdSRui Paulo IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
186832176cfdSRui Paulo wh, "mgt", "subtype 0x%x not handled", subtype);
186932176cfdSRui Paulo vap->iv_stats.is_rx_badsubtype++;
187032176cfdSRui Paulo break;
187132176cfdSRui Paulo }
187232176cfdSRui Paulo #undef ISREASSOC
187332176cfdSRui Paulo #undef ISPROBE
187432176cfdSRui Paulo }
187532176cfdSRui Paulo
187632176cfdSRui Paulo static void
sta_recv_ctl(struct ieee80211_node * ni,struct mbuf * m,int subtype)1877085ff963SMatthew Dillon sta_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
187832176cfdSRui Paulo {
1879085ff963SMatthew Dillon switch (subtype) {
1880085ff963SMatthew Dillon case IEEE80211_FC0_SUBTYPE_BAR:
1881085ff963SMatthew Dillon ieee80211_recv_bar(ni, m);
1882085ff963SMatthew Dillon break;
1883085ff963SMatthew Dillon }
188432176cfdSRui Paulo }
1885