1*7aeb1bc9Sstsp /* $OpenBSD: ieee80211_input.c,v 1.254 2024/05/23 11:19:13 stsp Exp $ */
2b16bc2beSdaniel /* $NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung Exp $ */
3e03e709cSdamien
491b2158bSmillert /*-
591b2158bSmillert * Copyright (c) 2001 Atsushi Onoe
691b2158bSmillert * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
745eec175Sdamien * Copyright (c) 2007-2009 Damien Bergamini
891b2158bSmillert * All rights reserved.
991b2158bSmillert *
1091b2158bSmillert * Redistribution and use in source and binary forms, with or without
1191b2158bSmillert * modification, are permitted provided that the following conditions
1291b2158bSmillert * are met:
1391b2158bSmillert * 1. Redistributions of source code must retain the above copyright
1491b2158bSmillert * notice, this list of conditions and the following disclaimer.
1591b2158bSmillert * 2. Redistributions in binary form must reproduce the above copyright
1691b2158bSmillert * notice, this list of conditions and the following disclaimer in the
1791b2158bSmillert * documentation and/or other materials provided with the distribution.
1891b2158bSmillert * 3. The name of the author may not be used to endorse or promote products
1991b2158bSmillert * derived from this software without specific prior written permission.
2091b2158bSmillert *
2191b2158bSmillert * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2291b2158bSmillert * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2391b2158bSmillert * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2491b2158bSmillert * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2591b2158bSmillert * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2691b2158bSmillert * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2791b2158bSmillert * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2891b2158bSmillert * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2991b2158bSmillert * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3091b2158bSmillert * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3191b2158bSmillert */
3291b2158bSmillert
3391b2158bSmillert #include "bpfilter.h"
3491b2158bSmillert
3591b2158bSmillert #include <sys/param.h>
3691b2158bSmillert #include <sys/systm.h>
3791b2158bSmillert #include <sys/mbuf.h>
3891b2158bSmillert #include <sys/malloc.h>
3991b2158bSmillert #include <sys/kernel.h>
4091b2158bSmillert #include <sys/socket.h>
4191b2158bSmillert #include <sys/sockio.h>
4291b2158bSmillert #include <sys/endian.h>
4391b2158bSmillert #include <sys/errno.h>
4491b2158bSmillert #include <sys/sysctl.h>
45a1d10fefSblambert #include <sys/task.h>
4691b2158bSmillert
4791b2158bSmillert #include <net/if.h>
4891b2158bSmillert #include <net/if_dl.h>
4991b2158bSmillert #include <net/if_media.h>
5091b2158bSmillert #include <net/if_llc.h>
5191b2158bSmillert
5291b2158bSmillert #if NBPFILTER > 0
5391b2158bSmillert #include <net/bpf.h>
5491b2158bSmillert #endif
5591b2158bSmillert
5691b2158bSmillert #include <netinet/in.h>
5791b2158bSmillert #include <netinet/if_ether.h>
5891b2158bSmillert
5991b2158bSmillert #include <net80211/ieee80211_var.h>
606aaa29faSdamien #include <net80211/ieee80211_priv.h>
6191b2158bSmillert
62e995d523Sstsp struct mbuf *ieee80211_input_hwdecrypt(struct ieee80211com *,
63ccc2d1c4Sstsp struct ieee80211_node *, struct mbuf *,
64ccc2d1c4Sstsp struct ieee80211_rxinfo *rxi);
650c2a2ba1Sdamien struct mbuf *ieee80211_defrag(struct ieee80211com *, struct mbuf *, int);
660c2a2ba1Sdamien void ieee80211_defrag_timeout(void *);
6720fce2ddSstsp void ieee80211_input_ba(struct ieee80211com *, struct mbuf *,
688fbaf8a2Sstsp struct ieee80211_node *, int, struct ieee80211_rxinfo *,
698fbaf8a2Sstsp struct mbuf_list *);
7020fce2ddSstsp void ieee80211_input_ba_flush(struct ieee80211com *, struct ieee80211_node *,
718fbaf8a2Sstsp struct ieee80211_rx_ba *, struct mbuf_list *);
7220599cf9Sstsp int ieee80211_input_ba_gap_skip(struct ieee80211_rx_ba *);
7320fce2ddSstsp void ieee80211_input_ba_gap_timeout(void *arg);
7445eec175Sdamien void ieee80211_ba_move_window(struct ieee80211com *,
758fbaf8a2Sstsp struct ieee80211_node *, u_int8_t, u_int16_t, struct mbuf_list *);
762bfbd258Sstsp void ieee80211_input_ba_seq(struct ieee80211com *,
778fbaf8a2Sstsp struct ieee80211_node *, uint8_t, uint16_t, struct mbuf_list *);
7845eec175Sdamien void ieee80211_decap(struct ieee80211com *, struct mbuf *,
798fbaf8a2Sstsp struct ieee80211_node *, int, struct mbuf_list *);
80e12e039eSstsp int ieee80211_amsdu_decap_validate(struct ieee80211com *, struct mbuf *,
81e12e039eSstsp struct ieee80211_node *);
8245eec175Sdamien void ieee80211_amsdu_decap(struct ieee80211com *, struct mbuf *,
838fbaf8a2Sstsp struct ieee80211_node *, int, struct mbuf_list *);
848fbaf8a2Sstsp void ieee80211_enqueue_data(struct ieee80211com *, struct mbuf *,
858fbaf8a2Sstsp struct ieee80211_node *, int, struct mbuf_list *);
862d5bc21cSdamien int ieee80211_parse_edca_params_body(struct ieee80211com *,
87ab235185Sdamien const u_int8_t *);
88ab235185Sdamien int ieee80211_parse_edca_params(struct ieee80211com *, const u_int8_t *);
89ab235185Sdamien int ieee80211_parse_wmm_params(struct ieee80211com *, const u_int8_t *);
9004cdc2f2Spatrick enum ieee80211_cipher ieee80211_parse_rsn_cipher(const u_int8_t *);
9104cdc2f2Spatrick enum ieee80211_akm ieee80211_parse_rsn_akm(const u_int8_t *);
92e03e709cSdamien int ieee80211_parse_rsn_body(struct ieee80211com *, const u_int8_t *,
93e03e709cSdamien u_int, struct ieee80211_rsnparams *);
94b3a42803Sdamien int ieee80211_save_ie(const u_int8_t *, u_int8_t **);
959e9787ffSdamien void ieee80211_recv_probe_resp(struct ieee80211com *, struct mbuf *,
9630d05aacSdamien struct ieee80211_node *, struct ieee80211_rxinfo *, int);
97171ac09aSdamien #ifndef IEEE80211_STA_ONLY
989e9787ffSdamien void ieee80211_recv_probe_req(struct ieee80211com *, struct mbuf *,
9930d05aacSdamien struct ieee80211_node *, struct ieee80211_rxinfo *);
100171ac09aSdamien #endif
1019e9787ffSdamien void ieee80211_recv_auth(struct ieee80211com *, struct mbuf *,
10230d05aacSdamien struct ieee80211_node *, struct ieee80211_rxinfo *);
103171ac09aSdamien #ifndef IEEE80211_STA_ONLY
1049e9787ffSdamien void ieee80211_recv_assoc_req(struct ieee80211com *, struct mbuf *,
10530d05aacSdamien struct ieee80211_node *, struct ieee80211_rxinfo *, int);
106171ac09aSdamien #endif
1079e9787ffSdamien void ieee80211_recv_assoc_resp(struct ieee80211com *, struct mbuf *,
108e03e709cSdamien struct ieee80211_node *, int);
1099e9787ffSdamien void ieee80211_recv_deauth(struct ieee80211com *, struct mbuf *,
110e03e709cSdamien struct ieee80211_node *);
1119e9787ffSdamien void ieee80211_recv_disassoc(struct ieee80211com *, struct mbuf *,
112e03e709cSdamien struct ieee80211_node *);
11345eec175Sdamien void ieee80211_recv_addba_req(struct ieee80211com *, struct mbuf *,
11445eec175Sdamien struct ieee80211_node *);
11545eec175Sdamien void ieee80211_recv_addba_resp(struct ieee80211com *, struct mbuf *,
11645eec175Sdamien struct ieee80211_node *);
11745eec175Sdamien void ieee80211_recv_delba(struct ieee80211com *, struct mbuf *,
11845eec175Sdamien struct ieee80211_node *);
11945eec175Sdamien void ieee80211_recv_sa_query_req(struct ieee80211com *, struct mbuf *,
12045eec175Sdamien struct ieee80211_node *);
12145eec175Sdamien #ifndef IEEE80211_STA_ONLY
12245eec175Sdamien void ieee80211_recv_sa_query_resp(struct ieee80211com *, struct mbuf *,
12345eec175Sdamien struct ieee80211_node *);
12445eec175Sdamien #endif
125fcdd546dSdamien void ieee80211_recv_action(struct ieee80211com *, struct mbuf *,
126fcdd546dSdamien struct ieee80211_node *);
12745eec175Sdamien #ifndef IEEE80211_STA_ONLY
12845eec175Sdamien void ieee80211_recv_pspoll(struct ieee80211com *, struct mbuf *,
12945eec175Sdamien struct ieee80211_node *);
13045eec175Sdamien #endif
13145eec175Sdamien void ieee80211_recv_bar(struct ieee80211com *, struct mbuf *,
13245eec175Sdamien struct ieee80211_node *);
13345eec175Sdamien void ieee80211_bar_tid(struct ieee80211com *, struct ieee80211_node *,
13445eec175Sdamien u_int8_t, u_int16_t);
135a1d10fefSblambert
13691b2158bSmillert /*
13745eec175Sdamien * Retrieve the length in bytes of an 802.11 header.
13827d2383aSdamien */
13927d2383aSdamien u_int
ieee80211_get_hdrlen(const struct ieee80211_frame * wh)140b4e72b72Sdamien ieee80211_get_hdrlen(const struct ieee80211_frame *wh)
14127d2383aSdamien {
142b4e72b72Sdamien u_int size = sizeof(*wh);
14327d2383aSdamien
144b4e72b72Sdamien /* NB: does not work with control frames */
145b4e72b72Sdamien KASSERT(ieee80211_has_seq(wh));
14627d2383aSdamien
147b4e72b72Sdamien if (ieee80211_has_addr4(wh))
14827d2383aSdamien size += IEEE80211_ADDR_LEN; /* i_addr4 */
149b4e72b72Sdamien if (ieee80211_has_qos(wh))
15027d2383aSdamien size += sizeof(u_int16_t); /* i_qos */
151b4e72b72Sdamien if (ieee80211_has_htc(wh))
15227d2383aSdamien size += sizeof(u_int32_t); /* i_ht */
15327d2383aSdamien return size;
15427d2383aSdamien }
15527d2383aSdamien
156e995d523Sstsp /* Post-processing for drivers which perform decryption in hardware. */
157e995d523Sstsp struct mbuf *
ieee80211_input_hwdecrypt(struct ieee80211com * ic,struct ieee80211_node * ni,struct mbuf * m,struct ieee80211_rxinfo * rxi)158e995d523Sstsp ieee80211_input_hwdecrypt(struct ieee80211com *ic, struct ieee80211_node *ni,
159ccc2d1c4Sstsp struct mbuf *m, struct ieee80211_rxinfo *rxi)
160e995d523Sstsp {
161e995d523Sstsp struct ieee80211_key *k;
162e995d523Sstsp struct ieee80211_frame *wh;
163e995d523Sstsp uint64_t pn, *prsc;
164e995d523Sstsp int hdrlen;
165e995d523Sstsp
166e995d523Sstsp k = ieee80211_get_rxkey(ic, m, ni);
167e995d523Sstsp if (k == NULL)
168e995d523Sstsp return NULL;
169e995d523Sstsp
170e995d523Sstsp wh = mtod(m, struct ieee80211_frame *);
171e995d523Sstsp hdrlen = ieee80211_get_hdrlen(wh);
172e995d523Sstsp
173e995d523Sstsp /*
174e995d523Sstsp * Update the last-seen packet number (PN) for drivers using hardware
175e995d523Sstsp * crypto offloading. This cannot be done by drivers because A-MPDU
176e995d523Sstsp * reordering needs to occur before a valid lower bound can be
177e995d523Sstsp * determined for the PN. Drivers will read the PN we write here and
178e995d523Sstsp * are expected to discard replayed frames based on it.
179e995d523Sstsp * Drivers are expected to leave the IV of decrypted frames intact
180e995d523Sstsp * so we can update the last-seen PN and strip the IV here.
181e995d523Sstsp */
182e995d523Sstsp switch (k->k_cipher) {
183e995d523Sstsp case IEEE80211_CIPHER_CCMP:
184e995d523Sstsp if (!(wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) {
1856b204e2aSstsp /*
1866b204e2aSstsp * If the protected bit is clear then hardware has
1876b204e2aSstsp * stripped the IV and we must trust that it handles
1886b204e2aSstsp * replay detection correctly.
1896b204e2aSstsp */
1906b204e2aSstsp break;
191e995d523Sstsp }
192*7aeb1bc9Sstsp if (ieee80211_ccmp_get_pn(&pn, &prsc, m, k) != 0) {
193*7aeb1bc9Sstsp ic->ic_stats.is_ccmp_dec_errs++;
194e995d523Sstsp return NULL;
195*7aeb1bc9Sstsp }
196ccc2d1c4Sstsp if (rxi->rxi_flags & IEEE80211_RXI_HWDEC_SAME_PN) {
197ccc2d1c4Sstsp if (pn < *prsc) {
198ccc2d1c4Sstsp ic->ic_stats.is_ccmp_replays++;
199ccc2d1c4Sstsp return NULL;
200ccc2d1c4Sstsp }
201ccc2d1c4Sstsp } else if (pn <= *prsc) {
202e995d523Sstsp ic->ic_stats.is_ccmp_replays++;
203e995d523Sstsp return NULL;
204e995d523Sstsp }
205e995d523Sstsp
206e995d523Sstsp /* Update last-seen packet number. */
207e995d523Sstsp *prsc = pn;
208e995d523Sstsp
209e995d523Sstsp /* Clear Protected bit and strip IV. */
210e995d523Sstsp wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
211e995d523Sstsp memmove(mtod(m, caddr_t) + IEEE80211_CCMP_HDRLEN, wh, hdrlen);
212e995d523Sstsp m_adj(m, IEEE80211_CCMP_HDRLEN);
213e995d523Sstsp /* Drivers are expected to strip the MIC. */
214e995d523Sstsp break;
215e995d523Sstsp case IEEE80211_CIPHER_TKIP:
216e995d523Sstsp if (!(wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) {
2176b204e2aSstsp /*
2186b204e2aSstsp * If the protected bit is clear then hardware has
2196b204e2aSstsp * stripped the IV and we must trust that it handles
2206b204e2aSstsp * replay detection correctly.
2216b204e2aSstsp */
2226b204e2aSstsp break;
223e995d523Sstsp }
224e995d523Sstsp if (ieee80211_tkip_get_tsc(&pn, &prsc, m, k) != 0)
225e995d523Sstsp return NULL;
226ccc2d1c4Sstsp if (rxi->rxi_flags & IEEE80211_RXI_HWDEC_SAME_PN) {
227ccc2d1c4Sstsp if (pn < *prsc) {
228ccc2d1c4Sstsp ic->ic_stats.is_tkip_replays++;
229ccc2d1c4Sstsp return NULL;
230ccc2d1c4Sstsp }
231ccc2d1c4Sstsp } else if (pn <= *prsc) {
232e995d523Sstsp ic->ic_stats.is_tkip_replays++;
233e995d523Sstsp return NULL;
234e995d523Sstsp }
235e995d523Sstsp
236e995d523Sstsp /* Update last-seen packet number. */
237e995d523Sstsp *prsc = pn;
238e995d523Sstsp
239e995d523Sstsp /* Clear Protected bit and strip IV. */
240e995d523Sstsp wh = mtod(m, struct ieee80211_frame *);
241e995d523Sstsp wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
242e995d523Sstsp memmove(mtod(m, caddr_t) + IEEE80211_TKIP_HDRLEN, wh, hdrlen);
243e995d523Sstsp m_adj(m, IEEE80211_TKIP_HDRLEN);
244e995d523Sstsp /* Drivers are expected to strip the MIC. */
245e995d523Sstsp break;
246e995d523Sstsp default:
247e995d523Sstsp break;
248e995d523Sstsp }
249e995d523Sstsp
250e995d523Sstsp return m;
251e995d523Sstsp }
252e995d523Sstsp
25327d2383aSdamien /*
25491b2158bSmillert * Process a received frame. The node associated with the sender
25591b2158bSmillert * should be supplied. If nothing was found in the node table then
25691b2158bSmillert * the caller is assumed to supply a reference to ic_bss instead.
25791b2158bSmillert * The RSSI and a timestamp are also supplied. The RSSI data is used
25891b2158bSmillert * during AP scanning to select a AP to associate with; it can have
25991b2158bSmillert * any units so long as values have consistent units and higher values
26091b2158bSmillert * mean ``better signal''. The receive timestamp is currently not used
26191b2158bSmillert * by the 802.11 layer.
2628fbaf8a2Sstsp *
2638fbaf8a2Sstsp * This function acts on management frames immediately and queues data frames
2648fbaf8a2Sstsp * on the specified mbuf list. Delivery of queued data frames to upper layers
2658fbaf8a2Sstsp * must be triggered with if_input(). Drivers should call if_input() only once
2668fbaf8a2Sstsp * per Rx interrupt to avoid triggering the input ifq pressure drop mechanism
2678fbaf8a2Sstsp * unnecessarily.
26891b2158bSmillert */
26991b2158bSmillert void
ieee80211_inputm(struct ifnet * ifp,struct mbuf * m,struct ieee80211_node * ni,struct ieee80211_rxinfo * rxi,struct mbuf_list * ml)2708fbaf8a2Sstsp ieee80211_inputm(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
2718fbaf8a2Sstsp struct ieee80211_rxinfo *rxi, struct mbuf_list *ml)
27291b2158bSmillert {
27391b2158bSmillert struct ieee80211com *ic = (void *)ifp;
27491b2158bSmillert struct ieee80211_frame *wh;
27545eec175Sdamien u_int16_t *orxseq, nrxseq, qos;
276aea3374dSdamien u_int8_t dir, type, subtype, tid;
27745eec175Sdamien int hdrlen, hasqos;
27891b2158bSmillert
27945eec175Sdamien KASSERT(ni != NULL);
28045eec175Sdamien
281361aed72Sdamien /* in monitor mode, send everything directly to bpf */
282159c9b61Sdamien if (ic->ic_opmode == IEEE80211_M_MONITOR)
28391b2158bSmillert goto out;
28491b2158bSmillert
285361aed72Sdamien /*
286361aed72Sdamien * Do not process frames without an Address 2 field any further.
287361aed72Sdamien * Only CTS and ACK control frames do not have this field.
288361aed72Sdamien */
289361aed72Sdamien if (m->m_len < sizeof(struct ieee80211_frame_min)) {
290361aed72Sdamien DPRINTF(("frame too short, len %u\n", m->m_len));
291159c9b61Sdamien ic->ic_stats.is_rx_tooshort++;
292159c9b61Sdamien goto out;
293159c9b61Sdamien }
294159c9b61Sdamien
29591b2158bSmillert wh = mtod(m, struct ieee80211_frame *);
29691b2158bSmillert if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
29791b2158bSmillert IEEE80211_FC0_VERSION_0) {
298361aed72Sdamien DPRINTF(("frame with wrong version: %x\n", wh->i_fc[0]));
29991b2158bSmillert ic->ic_stats.is_rx_badversion++;
30091b2158bSmillert goto err;
30191b2158bSmillert }
30291b2158bSmillert
30391b2158bSmillert dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
30491b2158bSmillert type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
30583c6196dSstsp subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
306fb4c23fcSdamien
307361aed72Sdamien if (type != IEEE80211_FC0_TYPE_CTL) {
308361aed72Sdamien hdrlen = ieee80211_get_hdrlen(wh);
309361aed72Sdamien if (m->m_len < hdrlen) {
310361aed72Sdamien DPRINTF(("frame too short, len %u\n", m->m_len));
31191b2158bSmillert ic->ic_stats.is_rx_tooshort++;
312962c982dSdamien goto err;
313361aed72Sdamien }
314c8010ca2Stobhe } else
315c8010ca2Stobhe hdrlen = 0;
31645eec175Sdamien if ((hasqos = ieee80211_has_qos(wh))) {
31745eec175Sdamien qos = ieee80211_get_qos(wh);
31845eec175Sdamien tid = qos & IEEE80211_QOS_TID;
3193dc83bf9Shaesbaert } else {
3203dc83bf9Shaesbaert qos = 0;
3213dc83bf9Shaesbaert tid = 0;
32245eec175Sdamien }
32345eec175Sdamien
324f330c64aSstsp if (ic->ic_state == IEEE80211_S_RUN &&
325f330c64aSstsp type == IEEE80211_FC0_TYPE_DATA && hasqos &&
32694b1ed4eSstsp (subtype & IEEE80211_FC0_SUBTYPE_NODATA) == 0 &&
327f330c64aSstsp !(rxi->rxi_flags & IEEE80211_RXI_AMPDU_DONE)
328f330c64aSstsp #ifndef IEEE80211_STA_ONLY
329f330c64aSstsp && (ic->ic_opmode == IEEE80211_M_STA || ni != ic->ic_bss)
330f330c64aSstsp #endif
331f330c64aSstsp ) {
332633cd82cSstsp int ba_state = ni->ni_rx_ba[tid].ba_state;
333633cd82cSstsp
334f330c64aSstsp #ifndef IEEE80211_STA_ONLY
335f330c64aSstsp if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
336f330c64aSstsp if (!IEEE80211_ADDR_EQ(wh->i_addr1,
337f330c64aSstsp ic->ic_bss->ni_bssid)) {
338f330c64aSstsp ic->ic_stats.is_rx_wrongbss++;
339f330c64aSstsp goto err;
340f330c64aSstsp }
341f330c64aSstsp if (ni->ni_state != IEEE80211_S_ASSOC) {
342f330c64aSstsp ic->ic_stats.is_rx_notassoc++;
343f330c64aSstsp goto err;
344f330c64aSstsp }
345f330c64aSstsp }
346f330c64aSstsp #endif
347633cd82cSstsp /*
348633cd82cSstsp * If Block Ack was explicitly requested, check
349633cd82cSstsp * if we have a BA agreement for this RA/TID.
350633cd82cSstsp */
351633cd82cSstsp if ((qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
352633cd82cSstsp IEEE80211_QOS_ACK_POLICY_BA &&
353633cd82cSstsp ba_state != IEEE80211_BA_AGREED) {
354633cd82cSstsp DPRINTF(("no BA agreement for %s, TID %d\n",
355633cd82cSstsp ether_sprintf(ni->ni_macaddr), tid));
356633cd82cSstsp /* send a DELBA with reason code UNKNOWN-BA */
357633cd82cSstsp IEEE80211_SEND_ACTION(ic, ni,
358633cd82cSstsp IEEE80211_CATEG_BA, IEEE80211_ACTION_DELBA,
359633cd82cSstsp IEEE80211_REASON_SETUP_REQUIRED << 16 | tid);
360633cd82cSstsp goto err;
361633cd82cSstsp }
362633cd82cSstsp
363633cd82cSstsp /*
364633cd82cSstsp * Check if we have an explicit or implicit
365633cd82cSstsp * Block Ack Request for a valid BA agreement.
366633cd82cSstsp */
367633cd82cSstsp if (ba_state == IEEE80211_BA_AGREED &&
368633cd82cSstsp ((qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
369633cd82cSstsp IEEE80211_QOS_ACK_POLICY_BA ||
370633cd82cSstsp (qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
371633cd82cSstsp IEEE80211_QOS_ACK_POLICY_NORMAL)) {
372633cd82cSstsp /* go through A-MPDU reordering */
3738fbaf8a2Sstsp ieee80211_input_ba(ic, m, ni, tid, rxi, ml);
374633cd82cSstsp return; /* don't free m! */
37519817d19Stobhe } else if (ba_state == IEEE80211_BA_REQUESTED &&
37619817d19Stobhe (qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
37719817d19Stobhe IEEE80211_QOS_ACK_POLICY_NORMAL) {
37819817d19Stobhe /*
37919817d19Stobhe * Apparently, qos frames for a tid where a
38019817d19Stobhe * block ack agreement was requested but not
38119817d19Stobhe * yet confirmed by us should still contribute
38219817d19Stobhe * to the sequence number for this tid.
38319817d19Stobhe */
38419817d19Stobhe ieee80211_input_ba(ic, m, ni, tid, rxi, ml);
38519817d19Stobhe return; /* don't free m! */
386633cd82cSstsp }
387633cd82cSstsp }
388633cd82cSstsp
389fe5684e3Sstsp /*
390fe5684e3Sstsp * We do not yet support fragments. Drop any fragmented packets.
391fe5684e3Sstsp * Counter-measure against attacks where an arbitrary packet is
392fe5684e3Sstsp * injected via a fragment with attacker-controlled content.
393fe5684e3Sstsp * See https://papers.mathyvanhoef.com/usenix2021.pdf
394fe5684e3Sstsp * Section 6.8 "Treating fragments as full frames"
395fe5684e3Sstsp */
396fe5684e3Sstsp if (ieee80211_has_seq(wh)) {
397fe5684e3Sstsp uint16_t rxseq = letoh16(*(const u_int16_t *)wh->i_seq);
398fe5684e3Sstsp if ((wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) ||
399fe5684e3Sstsp (rxseq & IEEE80211_SEQ_FRAG_MASK))
400fe5684e3Sstsp goto err;
401fe5684e3Sstsp }
402fe5684e3Sstsp
40345eec175Sdamien /* duplicate detection (see 9.2.9) */
404b4e72b72Sdamien if (ieee80211_has_seq(wh) &&
405b4e72b72Sdamien ic->ic_state != IEEE80211_S_SCAN) {
406361aed72Sdamien nrxseq = letoh16(*(u_int16_t *)wh->i_seq) >>
407361aed72Sdamien IEEE80211_SEQ_SEQ_SHIFT;
40845eec175Sdamien if (hasqos)
409361aed72Sdamien orxseq = &ni->ni_qos_rxseqs[tid];
41045eec175Sdamien else
411361aed72Sdamien orxseq = &ni->ni_rxseq;
412ccc2d1c4Sstsp if (rxi->rxi_flags & IEEE80211_RXI_SAME_SEQ) {
413ccc2d1c4Sstsp if (nrxseq != *orxseq) {
414ccc2d1c4Sstsp /* duplicate, silently discarded */
415ccc2d1c4Sstsp ic->ic_stats.is_rx_dup++;
416ccc2d1c4Sstsp goto out;
417ccc2d1c4Sstsp }
418ccc2d1c4Sstsp } else if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
419361aed72Sdamien nrxseq == *orxseq) {
420361aed72Sdamien /* duplicate, silently discarded */
421361aed72Sdamien ic->ic_stats.is_rx_dup++;
4220fd4e251Sreyk goto out;
42391b2158bSmillert }
424361aed72Sdamien *orxseq = nrxseq;
425361aed72Sdamien }
426c0bda930Sstsp if (ic->ic_state > IEEE80211_S_SCAN) {
42730d05aacSdamien ni->ni_rssi = rxi->rxi_rssi;
42830d05aacSdamien ni->ni_rstamp = rxi->rxi_tstamp;
42991b2158bSmillert ni->ni_inact = 0;
43006e069b5Sstsp
431de9919e2Sstsp if (ic->ic_state == IEEE80211_S_RUN && ic->ic_bgscan_start) {
43206e069b5Sstsp /* Cancel or start background scan based on RSSI. */
43306e069b5Sstsp if ((*ic->ic_node_checkrssi)(ic, ni))
43406e069b5Sstsp timeout_del(&ic->ic_bgscan_timeout);
43506e069b5Sstsp else if (!timeout_pending(&ic->ic_bgscan_timeout) &&
436cc580788Sstsp (ic->ic_flags & IEEE80211_F_BGSCAN) == 0 &&
437cc580788Sstsp (ic->ic_flags & IEEE80211_F_DESBSSID) == 0)
43806e069b5Sstsp timeout_add_msec(&ic->ic_bgscan_timeout,
43906e069b5Sstsp 500 * (ic->ic_bgscan_fail + 1));
44091b2158bSmillert }
441c0bda930Sstsp }
44291b2158bSmillert
443171ac09aSdamien #ifndef IEEE80211_STA_ONLY
44493d01030Sdamien if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
44593d01030Sdamien (ic->ic_caps & IEEE80211_C_APPMGT) &&
4463945a2e1Sdamien ni->ni_state == IEEE80211_STA_ASSOC) {
4473945a2e1Sdamien if (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) {
4483945a2e1Sdamien if (ni->ni_pwrsave == IEEE80211_PS_AWAKE) {
449361aed72Sdamien /* turn on PS mode */
450361aed72Sdamien ni->ni_pwrsave = IEEE80211_PS_DOZE;
451ca7fda7eSstsp DPRINTF(("PS mode on for %s\n",
452ca7fda7eSstsp ether_sprintf(wh->i_addr2)));
45391b2158bSmillert }
4543945a2e1Sdamien } else if (ni->ni_pwrsave == IEEE80211_PS_DOZE) {
455351e1934Sdlg struct mbuf *m;
456351e1934Sdlg
457361aed72Sdamien /* turn off PS mode */
458361aed72Sdamien ni->ni_pwrsave = IEEE80211_PS_AWAKE;
459ca7fda7eSstsp DPRINTF(("PS mode off for %s\n",
460ca7fda7eSstsp ether_sprintf(wh->i_addr2)));
46191b2158bSmillert
462158c4605Sdamien (*ic->ic_set_tim)(ic, ni->ni_associd, 0);
46391b2158bSmillert
464361aed72Sdamien /* dequeue buffered unicast frames */
465351e1934Sdlg while ((m = mq_dequeue(&ni->ni_savedq)) != NULL) {
466351e1934Sdlg mq_enqueue(&ic->ic_pwrsaveq, m);
46767de757dSmpi if_start(ifp);
46891b2158bSmillert }
46991b2158bSmillert }
470e03e709cSdamien }
471171ac09aSdamien #endif
47291b2158bSmillert switch (type) {
47391b2158bSmillert case IEEE80211_FC0_TYPE_DATA:
47491b2158bSmillert switch (ic->ic_opmode) {
47591b2158bSmillert case IEEE80211_M_STA:
47691b2158bSmillert if (dir != IEEE80211_FC1_DIR_FROMDS) {
47791b2158bSmillert ic->ic_stats.is_rx_wrongdir++;
47891b2158bSmillert goto out;
47991b2158bSmillert }
4800fd4e251Sreyk if (ic->ic_state != IEEE80211_S_SCAN &&
4810fd4e251Sreyk !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) {
4820fd4e251Sreyk /* Source address is not our BSS. */
483932b9027Sdamien DPRINTF(("discard frame from SA %s\n",
484932b9027Sdamien ether_sprintf(wh->i_addr2)));
4850fd4e251Sreyk ic->ic_stats.is_rx_wrongbss++;
4860fd4e251Sreyk goto out;
4870fd4e251Sreyk }
48891b2158bSmillert if ((ifp->if_flags & IFF_SIMPLEX) &&
48991b2158bSmillert IEEE80211_IS_MULTICAST(wh->i_addr1) &&
49091b2158bSmillert IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) {
49191b2158bSmillert /*
492361aed72Sdamien * In IEEE802.11 network, multicast frame
49391b2158bSmillert * sent from me is broadcasted from AP.
49491b2158bSmillert * It should be silently discarded for
49591b2158bSmillert * SIMPLEX interface.
49691b2158bSmillert */
49791b2158bSmillert ic->ic_stats.is_rx_mcastecho++;
49891b2158bSmillert goto out;
49991b2158bSmillert }
50091b2158bSmillert break;
501171ac09aSdamien #ifndef IEEE80211_STA_ONLY
50291b2158bSmillert case IEEE80211_M_IBSS:
50391b2158bSmillert case IEEE80211_M_AHDEMO:
50491b2158bSmillert if (dir != IEEE80211_FC1_DIR_NODS) {
50591b2158bSmillert ic->ic_stats.is_rx_wrongdir++;
50691b2158bSmillert goto out;
50791b2158bSmillert }
5080fd4e251Sreyk if (ic->ic_state != IEEE80211_S_SCAN &&
5090fd4e251Sreyk !IEEE80211_ADDR_EQ(wh->i_addr3,
5100fd4e251Sreyk ic->ic_bss->ni_bssid) &&
511cf1b8840Smickey !IEEE80211_ADDR_EQ(wh->i_addr3,
512cf1b8840Smickey etherbroadcastaddr)) {
5130fd4e251Sreyk /* Destination is not our BSS or broadcast. */
514932b9027Sdamien DPRINTF(("discard data frame to DA %s\n",
515932b9027Sdamien ether_sprintf(wh->i_addr3)));
5160fd4e251Sreyk ic->ic_stats.is_rx_wrongbss++;
5170fd4e251Sreyk goto out;
5180fd4e251Sreyk }
51991b2158bSmillert break;
52091b2158bSmillert case IEEE80211_M_HOSTAP:
52191b2158bSmillert if (dir != IEEE80211_FC1_DIR_TODS) {
52291b2158bSmillert ic->ic_stats.is_rx_wrongdir++;
52391b2158bSmillert goto out;
52491b2158bSmillert }
5250fd4e251Sreyk if (ic->ic_state != IEEE80211_S_SCAN &&
5260fd4e251Sreyk !IEEE80211_ADDR_EQ(wh->i_addr1,
5270fd4e251Sreyk ic->ic_bss->ni_bssid) &&
528cf1b8840Smickey !IEEE80211_ADDR_EQ(wh->i_addr1,
529cf1b8840Smickey etherbroadcastaddr)) {
5300fd4e251Sreyk /* BSS is not us or broadcast. */
531932b9027Sdamien DPRINTF(("discard data frame to BSS %s\n",
532932b9027Sdamien ether_sprintf(wh->i_addr1)));
5330fd4e251Sreyk ic->ic_stats.is_rx_wrongbss++;
5340fd4e251Sreyk goto out;
5350fd4e251Sreyk }
53691b2158bSmillert /* check if source STA is associated */
53791b2158bSmillert if (ni == ic->ic_bss) {
538932b9027Sdamien DPRINTF(("data from unknown src %s\n",
53991b2158bSmillert ether_sprintf(wh->i_addr2)));
54091b2158bSmillert /* NB: caller deals with reference */
541d4dcdec6Sstsp ni = ieee80211_find_node(ic, wh->i_addr2);
542d4dcdec6Sstsp if (ni == NULL)
54391b2158bSmillert ni = ieee80211_dup_bss(ic, wh->i_addr2);
54491b2158bSmillert if (ni != NULL) {
54591b2158bSmillert IEEE80211_SEND_MGMT(ic, ni,
54691b2158bSmillert IEEE80211_FC0_SUBTYPE_DEAUTH,
54791b2158bSmillert IEEE80211_REASON_NOT_AUTHED);
54891b2158bSmillert }
54991b2158bSmillert ic->ic_stats.is_rx_notassoc++;
55091b2158bSmillert goto err;
55191b2158bSmillert }
55263711141Sstsp if (ni->ni_state != IEEE80211_STA_ASSOC) {
553932b9027Sdamien DPRINTF(("data from unassoc src %s\n",
55491b2158bSmillert ether_sprintf(wh->i_addr2)));
55591b2158bSmillert IEEE80211_SEND_MGMT(ic, ni,
55691b2158bSmillert IEEE80211_FC0_SUBTYPE_DISASSOC,
55791b2158bSmillert IEEE80211_REASON_NOT_ASSOCED);
55891b2158bSmillert ic->ic_stats.is_rx_notassoc++;
55991b2158bSmillert goto err;
56091b2158bSmillert }
56191b2158bSmillert break;
562171ac09aSdamien #endif /* IEEE80211_STA_ONLY */
563171ac09aSdamien default:
564fb4c23fcSdamien /* can't get there */
565fb4c23fcSdamien goto out;
56691b2158bSmillert }
56723726d56Sdamien
56839356ae6Sstsp /* Do not process "no data" frames any further. */
56939356ae6Sstsp if (subtype & IEEE80211_FC0_SUBTYPE_NODATA) {
570e8ee43aaSmvs #if NBPFILTER > 0
57139356ae6Sstsp if (ic->ic_rawbpf)
57239356ae6Sstsp bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_IN);
573e8ee43aaSmvs #endif
57439356ae6Sstsp goto out;
57539356ae6Sstsp }
57639356ae6Sstsp
57701ad6d9fSdamien if ((ic->ic_flags & IEEE80211_F_WEPON) ||
57801ad6d9fSdamien ((ic->ic_flags & IEEE80211_F_RSNON) &&
57901ad6d9fSdamien (ni->ni_flags & IEEE80211_NODE_RXPROT))) {
58001ad6d9fSdamien /* protection is on for Rx */
58101ad6d9fSdamien if (!(rxi->rxi_flags & IEEE80211_RXI_HWDEC)) {
58201ad6d9fSdamien if (!(wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) {
58301ad6d9fSdamien /* drop unencrypted */
58401ad6d9fSdamien ic->ic_stats.is_rx_unencrypted++;
58501ad6d9fSdamien goto err;
58601ad6d9fSdamien }
58701ad6d9fSdamien /* do software decryption */
588e03e709cSdamien m = ieee80211_decrypt(ic, m, ni);
58991b2158bSmillert if (m == NULL) {
59091b2158bSmillert ic->ic_stats.is_rx_wepfail++;
59191b2158bSmillert goto err;
59291b2158bSmillert }
593e995d523Sstsp } else {
594ccc2d1c4Sstsp m = ieee80211_input_hwdecrypt(ic, ni, m, rxi);
595e995d523Sstsp if (m == NULL)
596e995d523Sstsp goto err;
59701ad6d9fSdamien }
598e995d523Sstsp wh = mtod(m, struct ieee80211_frame *);
59901ad6d9fSdamien } else if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) ||
60001ad6d9fSdamien (rxi->rxi_flags & IEEE80211_RXI_HWDEC)) {
60101ad6d9fSdamien /* frame encrypted but protection off for Rx */
60291b2158bSmillert ic->ic_stats.is_rx_nowep++;
60391b2158bSmillert goto out;
60491b2158bSmillert }
60501ad6d9fSdamien
60691b2158bSmillert #if NBPFILTER > 0
60791b2158bSmillert /* copy to listener after decrypt */
60891b2158bSmillert if (ic->ic_rawbpf)
609c4acdf64Sdjm bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_IN);
61091b2158bSmillert #endif
61145eec175Sdamien
61245eec175Sdamien if ((ni->ni_flags & IEEE80211_NODE_HT) &&
61345eec175Sdamien hasqos && (qos & IEEE80211_QOS_AMSDU))
6148fbaf8a2Sstsp ieee80211_amsdu_decap(ic, m, ni, hdrlen, ml);
61545eec175Sdamien else
6168fbaf8a2Sstsp ieee80211_decap(ic, m, ni, hdrlen, ml);
61791b2158bSmillert return;
61891b2158bSmillert
61991b2158bSmillert case IEEE80211_FC0_TYPE_MGT:
62091b2158bSmillert if (dir != IEEE80211_FC1_DIR_NODS) {
62191b2158bSmillert ic->ic_stats.is_rx_wrongdir++;
62291b2158bSmillert goto err;
62391b2158bSmillert }
624171ac09aSdamien #ifndef IEEE80211_STA_ONLY
62591b2158bSmillert if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
62691b2158bSmillert ic->ic_stats.is_rx_ahdemo_mgt++;
62791b2158bSmillert goto out;
62891b2158bSmillert }
629171ac09aSdamien #endif
63091b2158bSmillert /* drop frames without interest */
63191b2158bSmillert if (ic->ic_state == IEEE80211_S_SCAN) {
63291b2158bSmillert if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
63391b2158bSmillert subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
63491b2158bSmillert ic->ic_stats.is_rx_mgtdiscard++;
63591b2158bSmillert goto out;
63691b2158bSmillert }
63791b2158bSmillert }
63891b2158bSmillert
639e8163130Sdamien if (ni->ni_flags & IEEE80211_NODE_RXMGMTPROT) {
640e8163130Sdamien /* MMPDU protection is on for Rx */
641e8163130Sdamien if (subtype == IEEE80211_FC0_SUBTYPE_DISASSOC ||
642e8163130Sdamien subtype == IEEE80211_FC0_SUBTYPE_DEAUTH ||
643e8163130Sdamien subtype == IEEE80211_FC0_SUBTYPE_ACTION) {
644e8163130Sdamien if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
645e8163130Sdamien !(wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) {
646e8163130Sdamien /* unicast mgmt not encrypted */
647e8163130Sdamien goto out;
648e8163130Sdamien }
649e8163130Sdamien /* do software decryption */
650e8163130Sdamien m = ieee80211_decrypt(ic, m, ni);
651e8163130Sdamien if (m == NULL) {
652e8163130Sdamien /* XXX stats */
653e8163130Sdamien goto out;
654e8163130Sdamien }
655e8163130Sdamien wh = mtod(m, struct ieee80211_frame *);
656e8163130Sdamien }
657e8163130Sdamien } else if ((ic->ic_flags & IEEE80211_F_RSNON) &&
658e8163130Sdamien (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) {
659e8163130Sdamien /* encrypted but MMPDU Rx protection off for TA */
660e8163130Sdamien goto out;
661e8163130Sdamien }
662e8163130Sdamien
66391b2158bSmillert #if NBPFILTER > 0
664de3fab2cSdlg if (bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_IN) != 0) {
665832330f3Sreyk /*
666de3fab2cSdlg * Drop mbuf if it was filtered by bpf. Normally,
667de3fab2cSdlg * this is done in ether_input() but IEEE 802.11
668de3fab2cSdlg * management frames are a special case.
669832330f3Sreyk */
670832330f3Sreyk m_freem(m);
671832330f3Sreyk return;
672832330f3Sreyk }
67391b2158bSmillert #endif
67430d05aacSdamien (*ic->ic_recv_mgmt)(ic, m, ni, rxi, subtype);
67591b2158bSmillert m_freem(m);
67691b2158bSmillert return;
67791b2158bSmillert
67891b2158bSmillert case IEEE80211_FC0_TYPE_CTL:
679361aed72Sdamien switch (subtype) {
680171ac09aSdamien #ifndef IEEE80211_STA_ONLY
681361aed72Sdamien case IEEE80211_FC0_SUBTYPE_PS_POLL:
6823945a2e1Sdamien ieee80211_recv_pspoll(ic, m, ni);
683361aed72Sdamien break;
684171ac09aSdamien #endif
685f2b57c48Sdamien case IEEE80211_FC0_SUBTYPE_BAR:
68645eec175Sdamien ieee80211_recv_bar(ic, m, ni);
687f2b57c48Sdamien break;
68845eec175Sdamien default:
689100ae3fbSstsp ic->ic_stats.is_rx_ctl++;
690f2b57c48Sdamien break;
69191b2158bSmillert }
69291b2158bSmillert goto out;
69391b2158bSmillert
69491b2158bSmillert default:
695361aed72Sdamien DPRINTF(("bad frame type %x\n", type));
69691b2158bSmillert /* should not come here */
69791b2158bSmillert break;
69891b2158bSmillert }
69991b2158bSmillert err:
70091b2158bSmillert ifp->if_ierrors++;
70191b2158bSmillert out:
70291b2158bSmillert if (m != NULL) {
70391b2158bSmillert #if NBPFILTER > 0
70491b2158bSmillert if (ic->ic_rawbpf)
705c4acdf64Sdjm bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_IN);
70691b2158bSmillert #endif
70791b2158bSmillert m_freem(m);
70891b2158bSmillert }
70991b2158bSmillert }
71091b2158bSmillert
7118fbaf8a2Sstsp /* Input handler for drivers which only receive one frame per interrupt. */
7128fbaf8a2Sstsp void
ieee80211_input(struct ifnet * ifp,struct mbuf * m,struct ieee80211_node * ni,struct ieee80211_rxinfo * rxi)7138fbaf8a2Sstsp ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
7148fbaf8a2Sstsp struct ieee80211_rxinfo *rxi)
7158fbaf8a2Sstsp {
7168fbaf8a2Sstsp struct mbuf_list ml = MBUF_LIST_INITIALIZER();
7178fbaf8a2Sstsp
7188fbaf8a2Sstsp ieee80211_inputm(ifp, m, ni, rxi, &ml);
7198fbaf8a2Sstsp if_input(ifp, &ml);
7208fbaf8a2Sstsp }
7218fbaf8a2Sstsp
7224d38a678Sstsp #ifdef notyet
7230c2a2ba1Sdamien /*
7240c2a2ba1Sdamien * Handle defragmentation (see 9.5 and Annex C). We support the concurrent
7250c2a2ba1Sdamien * reception of fragments of three fragmented MSDUs or MMPDUs.
7260c2a2ba1Sdamien */
7270c2a2ba1Sdamien struct mbuf *
ieee80211_defrag(struct ieee80211com * ic,struct mbuf * m,int hdrlen)7280c2a2ba1Sdamien ieee80211_defrag(struct ieee80211com *ic, struct mbuf *m, int hdrlen)
7290c2a2ba1Sdamien {
7300c2a2ba1Sdamien const struct ieee80211_frame *owh, *wh;
7310c2a2ba1Sdamien struct ieee80211_defrag *df;
7320c2a2ba1Sdamien u_int16_t rxseq, seq;
7330c2a2ba1Sdamien u_int8_t frag;
7340c2a2ba1Sdamien int i;
7350c2a2ba1Sdamien
7360c2a2ba1Sdamien wh = mtod(m, struct ieee80211_frame *);
7370c2a2ba1Sdamien rxseq = letoh16(*(const u_int16_t *)wh->i_seq);
7380c2a2ba1Sdamien seq = rxseq >> IEEE80211_SEQ_SEQ_SHIFT;
7390c2a2ba1Sdamien frag = rxseq & IEEE80211_SEQ_FRAG_MASK;
7400c2a2ba1Sdamien
7410c2a2ba1Sdamien if (frag == 0 && !(wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG))
7420c2a2ba1Sdamien return m; /* not fragmented */
7430c2a2ba1Sdamien
7440c2a2ba1Sdamien if (frag == 0) {
7450c2a2ba1Sdamien /* first fragment, setup entry in the fragment cache */
7460c2a2ba1Sdamien if (++ic->ic_defrag_cur == IEEE80211_DEFRAG_SIZE)
7470c2a2ba1Sdamien ic->ic_defrag_cur = 0;
7480c2a2ba1Sdamien df = &ic->ic_defrag[ic->ic_defrag_cur];
7490c2a2ba1Sdamien m_freem(df->df_m); /* discard old entry */
7500c2a2ba1Sdamien df->df_seq = seq;
7510c2a2ba1Sdamien df->df_frag = 0;
7520c2a2ba1Sdamien df->df_m = m;
7530c2a2ba1Sdamien /* start receive MSDU timer of aMaxReceiveLifetime */
7540c2a2ba1Sdamien timeout_add_sec(&df->df_to, 1);
7550c2a2ba1Sdamien return NULL; /* MSDU or MMPDU not yet complete */
7560c2a2ba1Sdamien }
7570c2a2ba1Sdamien
7580c2a2ba1Sdamien /* find matching entry in the fragment cache */
7590c2a2ba1Sdamien for (i = 0; i < IEEE80211_DEFRAG_SIZE; i++) {
7600c2a2ba1Sdamien df = &ic->ic_defrag[i];
7610c2a2ba1Sdamien if (df->df_m == NULL)
7620c2a2ba1Sdamien continue;
7630c2a2ba1Sdamien if (df->df_seq != seq || df->df_frag + 1 != frag)
7640c2a2ba1Sdamien continue;
7650c2a2ba1Sdamien owh = mtod(df->df_m, struct ieee80211_frame *);
7660c2a2ba1Sdamien /* frame type, source and destination must match */
7670c2a2ba1Sdamien if (((wh->i_fc[0] ^ owh->i_fc[0]) & IEEE80211_FC0_TYPE_MASK) ||
7680c2a2ba1Sdamien !IEEE80211_ADDR_EQ(wh->i_addr1, owh->i_addr1) ||
7690c2a2ba1Sdamien !IEEE80211_ADDR_EQ(wh->i_addr2, owh->i_addr2))
7700c2a2ba1Sdamien continue;
7710c2a2ba1Sdamien /* matching entry found */
7720c2a2ba1Sdamien break;
7730c2a2ba1Sdamien }
7740c2a2ba1Sdamien if (i == IEEE80211_DEFRAG_SIZE) {
7750c2a2ba1Sdamien /* no matching entry found, discard fragment */
7760c2a2ba1Sdamien ic->ic_if.if_ierrors++;
7770c2a2ba1Sdamien m_freem(m);
7780c2a2ba1Sdamien return NULL;
7790c2a2ba1Sdamien }
7800c2a2ba1Sdamien
7810c2a2ba1Sdamien df->df_frag = frag;
7820c2a2ba1Sdamien /* strip 802.11 header and concatenate fragment */
7830c2a2ba1Sdamien m_adj(m, hdrlen);
7840c2a2ba1Sdamien m_cat(df->df_m, m);
7850c2a2ba1Sdamien df->df_m->m_pkthdr.len += m->m_pkthdr.len;
7860c2a2ba1Sdamien
7870c2a2ba1Sdamien if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
7880c2a2ba1Sdamien return NULL; /* MSDU or MMPDU not yet complete */
7890c2a2ba1Sdamien
7900c2a2ba1Sdamien /* MSDU or MMPDU complete */
7910c2a2ba1Sdamien timeout_del(&df->df_to);
7920c2a2ba1Sdamien m = df->df_m;
7930c2a2ba1Sdamien df->df_m = NULL;
7940c2a2ba1Sdamien return m;
7950c2a2ba1Sdamien }
7960c2a2ba1Sdamien
7970c2a2ba1Sdamien /*
7980c2a2ba1Sdamien * Receive MSDU defragmentation timer exceeds aMaxReceiveLifetime.
7990c2a2ba1Sdamien */
8000c2a2ba1Sdamien void
ieee80211_defrag_timeout(void * arg)8010c2a2ba1Sdamien ieee80211_defrag_timeout(void *arg)
8020c2a2ba1Sdamien {
8030c2a2ba1Sdamien struct ieee80211_defrag *df = arg;
8040c2a2ba1Sdamien int s = splnet();
8050c2a2ba1Sdamien
8060c2a2ba1Sdamien /* discard all received fragments */
8070c2a2ba1Sdamien m_freem(df->df_m);
8080c2a2ba1Sdamien df->df_m = NULL;
8090c2a2ba1Sdamien
8100c2a2ba1Sdamien splx(s);
8110c2a2ba1Sdamien }
8124d38a678Sstsp #endif
8130c2a2ba1Sdamien
81445eec175Sdamien /*
81545eec175Sdamien * Process a received data MPDU related to a specific HT-immediate Block Ack
81645eec175Sdamien * agreement (see 9.10.7.6).
81745eec175Sdamien */
81845eec175Sdamien void
ieee80211_input_ba(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,int tid,struct ieee80211_rxinfo * rxi,struct mbuf_list * ml)81920fce2ddSstsp ieee80211_input_ba(struct ieee80211com *ic, struct mbuf *m,
8208fbaf8a2Sstsp struct ieee80211_node *ni, int tid, struct ieee80211_rxinfo *rxi,
8218fbaf8a2Sstsp struct mbuf_list *ml)
82245eec175Sdamien {
82320fce2ddSstsp struct ifnet *ifp = &ic->ic_if;
824ec69e05aSdamien struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
82545eec175Sdamien struct ieee80211_frame *wh;
82645eec175Sdamien int idx, count;
82745eec175Sdamien u_int16_t sn;
82845eec175Sdamien
82945eec175Sdamien wh = mtod(m, struct ieee80211_frame *);
83045eec175Sdamien sn = letoh16(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
83145eec175Sdamien
83245eec175Sdamien /* reset Block Ack inactivity timer */
8335c7c7275Sstsp if (ba->ba_timeout_val != 0)
83445eec175Sdamien timeout_add_usec(&ba->ba_to, ba->ba_timeout_val);
83545eec175Sdamien
83645eec175Sdamien if (SEQ_LT(sn, ba->ba_winstart)) { /* SN < WinStartB */
83744fdf27cSstsp ic->ic_stats.is_ht_rx_frame_below_ba_winstart++;
83845eec175Sdamien m_freem(m); /* discard the MPDU */
83945eec175Sdamien return;
84045eec175Sdamien }
84145eec175Sdamien if (SEQ_LT(ba->ba_winend, sn)) { /* WinEndB < SN */
84244fdf27cSstsp ic->ic_stats.is_ht_rx_frame_above_ba_winend++;
8432bfbd258Sstsp count = (sn - ba->ba_winend) & 0xfff;
844737c942dSstsp if (count > ba->ba_winsize) {
8452bfbd258Sstsp /*
8462bfbd258Sstsp * Check whether we're consistently behind the window,
847678831beSjsg * and let the window move forward if necessary.
8482bfbd258Sstsp */
8490b126c70Sstsp if (ba->ba_winmiss < IEEE80211_BA_MAX_WINMISS) {
8502f33e90fStb if (ba->ba_missedsn == ((sn - 1) & 0xfff))
8510b126c70Sstsp ba->ba_winmiss++;
8520b126c70Sstsp else
8530b126c70Sstsp ba->ba_winmiss = 0;
8540b126c70Sstsp ba->ba_missedsn = sn;
8550b126c70Sstsp ifp->if_ierrors++;
8560b126c70Sstsp m_freem(m); /* discard the MPDU */
8570b126c70Sstsp return;
8580b126c70Sstsp }
8590b126c70Sstsp
8600b126c70Sstsp /* It appears the window has moved for real. */
86144fdf27cSstsp ic->ic_stats.is_ht_rx_ba_window_jump++;
8620b126c70Sstsp ba->ba_winmiss = 0;
8630b126c70Sstsp ba->ba_missedsn = 0;
8648fbaf8a2Sstsp ieee80211_ba_move_window(ic, ni, tid, sn, ml);
8652bfbd258Sstsp } else {
8662bfbd258Sstsp ic->ic_stats.is_ht_rx_ba_window_slide++;
8672bfbd258Sstsp ieee80211_input_ba_seq(ic, ni, tid,
8688fbaf8a2Sstsp (ba->ba_winstart + count) & 0xfff, ml);
8698fbaf8a2Sstsp ieee80211_input_ba_flush(ic, ni, ba, ml);
8700b126c70Sstsp }
87145eec175Sdamien }
87245eec175Sdamien /* WinStartB <= SN <= WinEndB */
87345eec175Sdamien
87408fd8c90Sstsp ba->ba_winmiss = 0;
87508fd8c90Sstsp ba->ba_missedsn = 0;
87645eec175Sdamien idx = (sn - ba->ba_winstart) & 0xfff;
87745eec175Sdamien idx = (ba->ba_head + idx) % IEEE80211_BA_MAX_WINSZ;
87845eec175Sdamien /* store the received MPDU in the buffer */
87945eec175Sdamien if (ba->ba_buf[idx].m != NULL) {
88045eec175Sdamien ifp->if_ierrors++;
88144fdf27cSstsp ic->ic_stats.is_ht_rx_ba_no_buf++;
88245eec175Sdamien m_freem(m);
88345eec175Sdamien return;
88445eec175Sdamien }
88545eec175Sdamien ba->ba_buf[idx].m = m;
88645eec175Sdamien /* store Rx meta-data too */
88745eec175Sdamien rxi->rxi_flags |= IEEE80211_RXI_AMPDU_DONE;
88845eec175Sdamien ba->ba_buf[idx].rxi = *rxi;
88920599cf9Sstsp ba->ba_gapwait++;
890e565f475Stobhe
891e565f475Stobhe if (ba->ba_buf[ba->ba_head].m == NULL && ba->ba_gapwait == 1)
892e565f475Stobhe timeout_add_msec(&ba->ba_gap_to, IEEE80211_BA_GAP_TIMEOUT);
89320fce2ddSstsp
8948fbaf8a2Sstsp ieee80211_input_ba_flush(ic, ni, ba, ml);
89520fce2ddSstsp }
89620fce2ddSstsp
8972bfbd258Sstsp /*
8982bfbd258Sstsp * Forward buffered frames with sequence number lower than max_seq.
8992bfbd258Sstsp * See 802.11-2012 9.21.7.6.2 b.
9002bfbd258Sstsp */
9012bfbd258Sstsp void
ieee80211_input_ba_seq(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid,uint16_t max_seq,struct mbuf_list * ml)9022bfbd258Sstsp ieee80211_input_ba_seq(struct ieee80211com *ic, struct ieee80211_node *ni,
9038fbaf8a2Sstsp uint8_t tid, uint16_t max_seq, struct mbuf_list *ml)
9042bfbd258Sstsp {
9052bfbd258Sstsp struct ifnet *ifp = &ic->ic_if;
9062bfbd258Sstsp struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
9072bfbd258Sstsp struct ieee80211_frame *wh;
9082bfbd258Sstsp uint16_t seq;
9092bfbd258Sstsp int i = 0;
9102bfbd258Sstsp
9112bfbd258Sstsp while (i++ < ba->ba_winsize) {
9122bfbd258Sstsp /* gaps may exist */
9132bfbd258Sstsp if (ba->ba_buf[ba->ba_head].m != NULL) {
9142bfbd258Sstsp wh = mtod(ba->ba_buf[ba->ba_head].m,
9152bfbd258Sstsp struct ieee80211_frame *);
9162bfbd258Sstsp KASSERT(ieee80211_has_seq(wh));
9172bfbd258Sstsp seq = letoh16(*(u_int16_t *)wh->i_seq) >>
9182bfbd258Sstsp IEEE80211_SEQ_SEQ_SHIFT;
9192bfbd258Sstsp if (!SEQ_LT(seq, max_seq))
92004b94a2bSstsp break;
9218fbaf8a2Sstsp ieee80211_inputm(ifp, ba->ba_buf[ba->ba_head].m,
9228fbaf8a2Sstsp ni, &ba->ba_buf[ba->ba_head].rxi, ml);
9232bfbd258Sstsp ba->ba_buf[ba->ba_head].m = NULL;
924e565f475Stobhe ba->ba_gapwait--;
9252bfbd258Sstsp } else
9262bfbd258Sstsp ic->ic_stats.is_ht_rx_ba_frame_lost++;
9272bfbd258Sstsp ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ;
928cda29a62Sstsp /* move window forward */
929cda29a62Sstsp ba->ba_winstart = (ba->ba_winstart + 1) & 0xfff;
9302bfbd258Sstsp }
931cda29a62Sstsp ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff;
9322bfbd258Sstsp }
9332bfbd258Sstsp
93420fce2ddSstsp /* Flush a consecutive sequence of frames from the reorder buffer. */
93520fce2ddSstsp void
ieee80211_input_ba_flush(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_rx_ba * ba,struct mbuf_list * ml)93620fce2ddSstsp ieee80211_input_ba_flush(struct ieee80211com *ic, struct ieee80211_node *ni,
9378fbaf8a2Sstsp struct ieee80211_rx_ba *ba, struct mbuf_list *ml)
93820fce2ddSstsp
93920fce2ddSstsp {
94020fce2ddSstsp struct ifnet *ifp = &ic->ic_if;
94120fce2ddSstsp
9428d36f12dSstsp /* Do not re-arm the gap timeout if we made no progress. */
9438d36f12dSstsp if (ba->ba_buf[ba->ba_head].m == NULL)
9448d36f12dSstsp return;
9458d36f12dSstsp
94645eec175Sdamien /* pass reordered MPDUs up to the next MAC process */
94745eec175Sdamien while (ba->ba_buf[ba->ba_head].m != NULL) {
9488fbaf8a2Sstsp ieee80211_inputm(ifp, ba->ba_buf[ba->ba_head].m, ni,
9498fbaf8a2Sstsp &ba->ba_buf[ba->ba_head].rxi, ml);
95045eec175Sdamien ba->ba_buf[ba->ba_head].m = NULL;
951e565f475Stobhe ba->ba_gapwait--;
95245eec175Sdamien
95345eec175Sdamien ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ;
95445eec175Sdamien /* move window forward */
95545eec175Sdamien ba->ba_winstart = (ba->ba_winstart + 1) & 0xfff;
95645eec175Sdamien }
95745eec175Sdamien ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff;
958e565f475Stobhe
959e565f475Stobhe if (timeout_pending(&ba->ba_gap_to))
960e565f475Stobhe timeout_del(&ba->ba_gap_to);
961e565f475Stobhe if (ba->ba_gapwait)
962e565f475Stobhe timeout_add_msec(&ba->ba_gap_to, IEEE80211_BA_GAP_TIMEOUT);
96345eec175Sdamien }
96445eec175Sdamien
96545eec175Sdamien /*
96620fce2ddSstsp * Forcibly move the BA window forward to remove a leading gap which has
96720fce2ddSstsp * been causing frames to linger in the reordering buffer for too long.
96820fce2ddSstsp * A leading gap will occur if a particular A-MPDU subframe never arrives
96920fce2ddSstsp * or if a bug in the sender causes sequence numbers to jump forward by > 1.
97020fce2ddSstsp */
97120599cf9Sstsp int
ieee80211_input_ba_gap_skip(struct ieee80211_rx_ba * ba)97220599cf9Sstsp ieee80211_input_ba_gap_skip(struct ieee80211_rx_ba *ba)
97320599cf9Sstsp {
97420599cf9Sstsp int skipped = 0;
97520599cf9Sstsp
97620599cf9Sstsp while (skipped < ba->ba_winsize && ba->ba_buf[ba->ba_head].m == NULL) {
97720599cf9Sstsp /* move window forward */
97820599cf9Sstsp ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ;
97920599cf9Sstsp ba->ba_winstart = (ba->ba_winstart + 1) & 0xfff;
98020599cf9Sstsp skipped++;
98120599cf9Sstsp }
98220599cf9Sstsp if (skipped > 0)
98320599cf9Sstsp ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff;
98420599cf9Sstsp
98520599cf9Sstsp return skipped;
98620599cf9Sstsp }
98720599cf9Sstsp
98820fce2ddSstsp void
ieee80211_input_ba_gap_timeout(void * arg)98920fce2ddSstsp ieee80211_input_ba_gap_timeout(void *arg)
99020fce2ddSstsp {
99120fce2ddSstsp struct ieee80211_rx_ba *ba = arg;
99220fce2ddSstsp struct ieee80211_node *ni = ba->ba_ni;
99320fce2ddSstsp struct ieee80211com *ic = ni->ni_ic;
99420fce2ddSstsp int s, skipped;
99520fce2ddSstsp
99644fdf27cSstsp ic->ic_stats.is_ht_rx_ba_window_gap_timeout++;
99744fdf27cSstsp
99820fce2ddSstsp s = splnet();
99920fce2ddSstsp
100020599cf9Sstsp skipped = ieee80211_input_ba_gap_skip(ba);
100120599cf9Sstsp ic->ic_stats.is_ht_rx_ba_frame_lost += skipped;
100286cf434eStobhe if (skipped) {
100386cf434eStobhe struct mbuf_list ml = MBUF_LIST_INITIALIZER();
100486cf434eStobhe ieee80211_input_ba_flush(ic, ni, ba, &ml);
100586cf434eStobhe if_input(&ic->ic_if, &ml);
100686cf434eStobhe }
100720fce2ddSstsp
100820fce2ddSstsp splx(s);
100920fce2ddSstsp }
101020fce2ddSstsp
101120fce2ddSstsp
101220fce2ddSstsp /*
101345eec175Sdamien * Change the value of WinStartB (move window forward) upon reception of a
1014ec69e05aSdamien * BlockAckReq frame or an ADDBA Request (PBAC).
101545eec175Sdamien */
101645eec175Sdamien void
ieee80211_ba_move_window(struct ieee80211com * ic,struct ieee80211_node * ni,u_int8_t tid,u_int16_t ssn,struct mbuf_list * ml)101745eec175Sdamien ieee80211_ba_move_window(struct ieee80211com *ic, struct ieee80211_node *ni,
10188fbaf8a2Sstsp u_int8_t tid, u_int16_t ssn, struct mbuf_list *ml)
101945eec175Sdamien {
102045eec175Sdamien struct ifnet *ifp = &ic->ic_if;
1021ec69e05aSdamien struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
102245eec175Sdamien int count;
102345eec175Sdamien
102445eec175Sdamien /* assert(WinStartB <= SSN) */
102545eec175Sdamien
102645eec175Sdamien count = (ssn - ba->ba_winstart) & 0xfff;
102745eec175Sdamien if (count > ba->ba_winsize) /* no overlap */
102845eec175Sdamien count = ba->ba_winsize;
102945eec175Sdamien while (count-- > 0) {
103045eec175Sdamien /* gaps may exist */
103145eec175Sdamien if (ba->ba_buf[ba->ba_head].m != NULL) {
10328fbaf8a2Sstsp ieee80211_inputm(ifp, ba->ba_buf[ba->ba_head].m, ni,
10338fbaf8a2Sstsp &ba->ba_buf[ba->ba_head].rxi, ml);
103445eec175Sdamien ba->ba_buf[ba->ba_head].m = NULL;
1035e565f475Stobhe ba->ba_gapwait--;
103644fdf27cSstsp } else
103744fdf27cSstsp ic->ic_stats.is_ht_rx_ba_frame_lost++;
103845eec175Sdamien ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ;
103945eec175Sdamien }
104045eec175Sdamien /* move window forward */
104145eec175Sdamien ba->ba_winstart = ssn;
104204b94a2bSstsp ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff;
104345eec175Sdamien
10448fbaf8a2Sstsp ieee80211_input_ba_flush(ic, ni, ba, ml);
104545eec175Sdamien }
104645eec175Sdamien
1047aea3374dSdamien void
ieee80211_enqueue_data(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,int mcast,struct mbuf_list * ml)10488fbaf8a2Sstsp ieee80211_enqueue_data(struct ieee80211com *ic, struct mbuf *m,
10498fbaf8a2Sstsp struct ieee80211_node *ni, int mcast, struct mbuf_list *ml)
1050aea3374dSdamien {
1051aea3374dSdamien struct ifnet *ifp = &ic->ic_if;
1052aea3374dSdamien struct ether_header *eh;
1053aea3374dSdamien struct mbuf *m1;
1054aea3374dSdamien
1055aea3374dSdamien eh = mtod(m, struct ether_header *);
1056aea3374dSdamien
1057aea3374dSdamien if ((ic->ic_flags & IEEE80211_F_RSNON) && !ni->ni_port_valid &&
105883aa0ba6Sdlg eh->ether_type != htons(ETHERTYPE_EAPOL)) {
1059aea3374dSdamien DPRINTF(("port not valid: %s\n",
1060aea3374dSdamien ether_sprintf(eh->ether_dhost)));
1061aea3374dSdamien ic->ic_stats.is_rx_unauth++;
1062aea3374dSdamien m_freem(m);
1063aea3374dSdamien return;
1064aea3374dSdamien }
1065aea3374dSdamien
1066aea3374dSdamien /*
1067aea3374dSdamien * Perform as a bridge within the AP. Notice that we do not
1068aea3374dSdamien * bridge EAPOL frames as suggested in C.1.1 of IEEE Std 802.1X.
1069a97608f4Sstsp * And we do not forward unicast frames received on a multicast address.
1070aea3374dSdamien */
1071aea3374dSdamien m1 = NULL;
1072171ac09aSdamien #ifndef IEEE80211_STA_ONLY
1073aea3374dSdamien if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
1074534bf8f4Sstsp !(ic->ic_userflags & IEEE80211_F_NOBRIDGE) &&
107583aa0ba6Sdlg eh->ether_type != htons(ETHERTYPE_EAPOL)) {
1076171ac09aSdamien struct ieee80211_node *ni1;
1077171ac09aSdamien
1078aea3374dSdamien if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
1079dca41f6bSdlg m1 = m_dup_pkt(m, ETHER_ALIGN, M_DONTWAIT);
1080aea3374dSdamien if (m1 == NULL)
1081aea3374dSdamien ifp->if_oerrors++;
1082aea3374dSdamien else
1083aea3374dSdamien m1->m_flags |= M_MCAST;
1084a97608f4Sstsp } else if (!mcast) {
1085aea3374dSdamien ni1 = ieee80211_find_node(ic, eh->ether_dhost);
1086aea3374dSdamien if (ni1 != NULL &&
1087aea3374dSdamien ni1->ni_state == IEEE80211_STA_ASSOC) {
1088aea3374dSdamien m1 = m;
1089aea3374dSdamien m = NULL;
1090aea3374dSdamien }
1091aea3374dSdamien }
1092aea3374dSdamien if (m1 != NULL) {
1093c38eb4ffSmpi if (if_enqueue(ifp, m1))
1094aea3374dSdamien ifp->if_oerrors++;
1095aea3374dSdamien }
1096aea3374dSdamien }
1097171ac09aSdamien #endif
1098aea3374dSdamien if (m != NULL) {
1099ced35d8fSmpi if ((ic->ic_flags & IEEE80211_F_RSNON) &&
110083aa0ba6Sdlg eh->ether_type == htons(ETHERTYPE_EAPOL)) {
1101db4dc9aaSmpi ifp->if_ipackets++;
1102aea3374dSdamien #if NBPFILTER > 0
1103aea3374dSdamien /*
1104aea3374dSdamien * If we forward frame into transmitter of the AP,
1105aea3374dSdamien * we don't need to duplicate for DLT_EN10MB.
1106aea3374dSdamien */
1107aea3374dSdamien if (ifp->if_bpf && m1 == NULL)
1108aea3374dSdamien bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
1109aea3374dSdamien #endif
1110aea3374dSdamien ieee80211_eapol_key_input(ic, m, ni);
111186666264Sdlg } else {
11128fbaf8a2Sstsp ml_enqueue(ml, m);
111386666264Sdlg }
1114aea3374dSdamien }
1115aea3374dSdamien }
1116aea3374dSdamien
111745eec175Sdamien void
ieee80211_decap(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,int hdrlen,struct mbuf_list * ml)111845eec175Sdamien ieee80211_decap(struct ieee80211com *ic, struct mbuf *m,
11198fbaf8a2Sstsp struct ieee80211_node *ni, int hdrlen, struct mbuf_list *ml)
112045eec175Sdamien {
1121ef001a37Sdamien struct ether_header eh;
1122ef001a37Sdamien struct ieee80211_frame *wh;
112345eec175Sdamien struct llc *llc;
1124a97608f4Sstsp int mcast;
112545eec175Sdamien
112645eec175Sdamien if (m->m_len < hdrlen + LLC_SNAPFRAMELEN &&
112745eec175Sdamien (m = m_pullup(m, hdrlen + LLC_SNAPFRAMELEN)) == NULL) {
112845eec175Sdamien ic->ic_stats.is_rx_decap++;
112945eec175Sdamien return;
113045eec175Sdamien }
1131ef001a37Sdamien wh = mtod(m, struct ieee80211_frame *);
1132a97608f4Sstsp mcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
1133ef001a37Sdamien switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
1134ef001a37Sdamien case IEEE80211_FC1_DIR_NODS:
1135ef001a37Sdamien IEEE80211_ADDR_COPY(eh.ether_dhost, wh->i_addr1);
1136ef001a37Sdamien IEEE80211_ADDR_COPY(eh.ether_shost, wh->i_addr2);
1137ef001a37Sdamien break;
1138ef001a37Sdamien case IEEE80211_FC1_DIR_TODS:
1139ef001a37Sdamien IEEE80211_ADDR_COPY(eh.ether_dhost, wh->i_addr3);
1140ef001a37Sdamien IEEE80211_ADDR_COPY(eh.ether_shost, wh->i_addr2);
1141ef001a37Sdamien break;
1142ef001a37Sdamien case IEEE80211_FC1_DIR_FROMDS:
1143ef001a37Sdamien IEEE80211_ADDR_COPY(eh.ether_dhost, wh->i_addr1);
1144ef001a37Sdamien IEEE80211_ADDR_COPY(eh.ether_shost, wh->i_addr3);
1145ef001a37Sdamien break;
1146ef001a37Sdamien case IEEE80211_FC1_DIR_DSTODS:
1147ef001a37Sdamien IEEE80211_ADDR_COPY(eh.ether_dhost, wh->i_addr3);
1148ef001a37Sdamien IEEE80211_ADDR_COPY(eh.ether_shost,
1149ef001a37Sdamien ((struct ieee80211_frame_addr4 *)wh)->i_addr4);
1150ef001a37Sdamien break;
1151ef001a37Sdamien }
1152ef001a37Sdamien llc = (struct llc *)((caddr_t)wh + hdrlen);
115345eec175Sdamien if (llc->llc_dsap == LLC_SNAP_LSAP &&
115445eec175Sdamien llc->llc_ssap == LLC_SNAP_LSAP &&
115545eec175Sdamien llc->llc_control == LLC_UI &&
115645eec175Sdamien llc->llc_snap.org_code[0] == 0 &&
115745eec175Sdamien llc->llc_snap.org_code[1] == 0 &&
115845eec175Sdamien llc->llc_snap.org_code[2] == 0) {
1159ef001a37Sdamien eh.ether_type = llc->llc_snap.ether_type;
116045eec175Sdamien m_adj(m, hdrlen + LLC_SNAPFRAMELEN - ETHER_HDR_LEN);
116145eec175Sdamien } else {
1162ef001a37Sdamien eh.ether_type = htons(m->m_pkthdr.len - hdrlen);
116345eec175Sdamien m_adj(m, hdrlen - ETHER_HDR_LEN);
116445eec175Sdamien }
1165ef001a37Sdamien memcpy(mtod(m, caddr_t), &eh, ETHER_HDR_LEN);
116645eec175Sdamien if (!ALIGNED_POINTER(mtod(m, caddr_t) + ETHER_HDR_LEN, u_int32_t)) {
11679d147a27Sdlg struct mbuf *m0 = m;
11689d147a27Sdlg m = m_dup_pkt(m0, ETHER_ALIGN, M_NOWAIT);
11699d147a27Sdlg m_freem(m0);
11709d147a27Sdlg if (m == NULL) {
117145eec175Sdamien ic->ic_stats.is_rx_decap++;
117245eec175Sdamien return;
117345eec175Sdamien }
117491b2158bSmillert }
11758fbaf8a2Sstsp ieee80211_enqueue_data(ic, m, ni, mcast, ml);
117691b2158bSmillert }
117791b2158bSmillert
1178e12e039eSstsp int
ieee80211_amsdu_decap_validate(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)1179e12e039eSstsp ieee80211_amsdu_decap_validate(struct ieee80211com *ic, struct mbuf *m,
1180e12e039eSstsp struct ieee80211_node *ni)
1181e12e039eSstsp {
1182e12e039eSstsp struct ether_header *eh = mtod(m, struct ether_header *);
1183e12e039eSstsp const uint8_t llc_hdr_mac[ETHER_ADDR_LEN] = {
1184e12e039eSstsp /* MAC address matching the 802.2 LLC header. */
1185e12e039eSstsp LLC_SNAP_LSAP, LLC_SNAP_LSAP, LLC_UI, 0, 0, 0
1186e12e039eSstsp };
1187e12e039eSstsp
1188e12e039eSstsp /*
1189e12e039eSstsp * We are sorry, but this particular MAC address cannot be used.
1190e12e039eSstsp * This mitigates an attack where a single 802.11 frame is interpreted
1191e12e039eSstsp * as an A-MSDU because of a forged AMSDU-present bit in the 802.11
1192e12e039eSstsp * QoS frame header: https://papers.mathyvanhoef.com/usenix2021.pdf
1193e12e039eSstsp * See Section 7.2, 'Countermeasures for the design flaws'
1194e12e039eSstsp */
1195e12e039eSstsp if (ETHER_IS_EQ(eh->ether_dhost, llc_hdr_mac))
1196e12e039eSstsp return 1;
1197e12e039eSstsp
1198e12e039eSstsp switch (ic->ic_opmode) {
1199e12e039eSstsp #ifndef IEEE80211_STA_ONLY
1200e12e039eSstsp case IEEE80211_M_HOSTAP:
1201e12e039eSstsp /*
1202e12e039eSstsp * Subframes must use the source address of the node which
1203e12e039eSstsp * transmitted the A-MSDU. Prevents MAC spoofing.
1204e12e039eSstsp */
1205e12e039eSstsp if (!ETHER_IS_EQ(ni->ni_macaddr, eh->ether_shost))
1206e12e039eSstsp return 1;
1207e12e039eSstsp break;
1208e12e039eSstsp #endif
1209e12e039eSstsp case IEEE80211_M_STA:
1210e12e039eSstsp /* Subframes must be addressed to me. */
1211e12e039eSstsp if (!ETHER_IS_EQ(ic->ic_myaddr, eh->ether_dhost))
1212e12e039eSstsp return 1;
1213e12e039eSstsp break;
1214e12e039eSstsp default:
1215e12e039eSstsp /* Ignore MONITOR/IBSS modes for now. */
1216e12e039eSstsp break;
1217e12e039eSstsp }
1218e12e039eSstsp
1219e12e039eSstsp return 0;
1220e12e039eSstsp }
1221e12e039eSstsp
122245eec175Sdamien /*
122345eec175Sdamien * Decapsulate an Aggregate MSDU (see 7.2.2.2).
122445eec175Sdamien */
122545eec175Sdamien void
ieee80211_amsdu_decap(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,int hdrlen,struct mbuf_list * ml)122645eec175Sdamien ieee80211_amsdu_decap(struct ieee80211com *ic, struct mbuf *m,
12278fbaf8a2Sstsp struct ieee80211_node *ni, int hdrlen, struct mbuf_list *ml)
122845eec175Sdamien {
122945eec175Sdamien struct mbuf *n;
123045eec175Sdamien struct ether_header *eh;
123145eec175Sdamien struct llc *llc;
1232a97608f4Sstsp int len, pad, mcast;
1233a97608f4Sstsp struct ieee80211_frame *wh;
1234e12e039eSstsp struct mbuf_list subframes = MBUF_LIST_INITIALIZER();
1235a97608f4Sstsp
1236a97608f4Sstsp wh = mtod(m, struct ieee80211_frame *);
1237a97608f4Sstsp mcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
123845eec175Sdamien
123945eec175Sdamien /* strip 802.11 header */
124045eec175Sdamien m_adj(m, hdrlen);
124145eec175Sdamien
1242e9230c34Sstsp while (m->m_pkthdr.len >= ETHER_HDR_LEN + LLC_SNAPFRAMELEN) {
124345eec175Sdamien /* process an A-MSDU subframe */
124445eec175Sdamien m = m_pullup(m, ETHER_HDR_LEN + LLC_SNAPFRAMELEN);
1245e12e039eSstsp if (m == NULL)
1246e12e039eSstsp break;
124745eec175Sdamien eh = mtod(m, struct ether_header *);
124845eec175Sdamien /* examine 802.3 header */
124945eec175Sdamien len = ntohs(eh->ether_type);
125045eec175Sdamien if (len < LLC_SNAPFRAMELEN) {
125145eec175Sdamien DPRINTF(("A-MSDU subframe too short (%d)\n", len));
125245eec175Sdamien /* stop processing A-MSDU subframes */
125345eec175Sdamien ic->ic_stats.is_rx_decap++;
1254e12e039eSstsp ml_purge(&subframes);
125545eec175Sdamien m_freem(m);
1256d0faa20eSstsp return;
125745eec175Sdamien }
125845eec175Sdamien llc = (struct llc *)&eh[1];
1259e12e039eSstsp /* Examine the 802.2 LLC header after the A-MSDU header. */
126045eec175Sdamien if (llc->llc_dsap == LLC_SNAP_LSAP &&
126145eec175Sdamien llc->llc_ssap == LLC_SNAP_LSAP &&
126245eec175Sdamien llc->llc_control == LLC_UI &&
126345eec175Sdamien llc->llc_snap.org_code[0] == 0 &&
126445eec175Sdamien llc->llc_snap.org_code[1] == 0 &&
126545eec175Sdamien llc->llc_snap.org_code[2] == 0) {
126645eec175Sdamien /* convert to Ethernet II header */
126745eec175Sdamien eh->ether_type = llc->llc_snap.ether_type;
126845eec175Sdamien /* strip LLC+SNAP headers */
12698f51fbe3Sderaadt memmove((u_int8_t *)eh + LLC_SNAPFRAMELEN, eh,
127045eec175Sdamien ETHER_HDR_LEN);
127145eec175Sdamien m_adj(m, LLC_SNAPFRAMELEN);
127245eec175Sdamien len -= LLC_SNAPFRAMELEN;
127345eec175Sdamien }
127445eec175Sdamien len += ETHER_HDR_LEN;
1275a2f74cd9Sstsp if (len > m->m_pkthdr.len) {
1276a2f74cd9Sstsp /* stop processing A-MSDU subframes */
1277a2f74cd9Sstsp DPRINTF(("A-MSDU subframe too long (%d)\n", len));
1278a2f74cd9Sstsp ic->ic_stats.is_rx_decap++;
1279e12e039eSstsp ml_purge(&subframes);
1280a2f74cd9Sstsp m_freem(m);
1281d0faa20eSstsp return;
1282a2f74cd9Sstsp }
128345eec175Sdamien
128445eec175Sdamien /* "detach" our A-MSDU subframe from the others */
128545eec175Sdamien n = m_split(m, len, M_NOWAIT);
128645eec175Sdamien if (n == NULL) {
128745eec175Sdamien /* stop processing A-MSDU subframes */
128845eec175Sdamien ic->ic_stats.is_rx_decap++;
1289e12e039eSstsp ml_purge(&subframes);
129045eec175Sdamien m_freem(m);
1291d0faa20eSstsp return;
129245eec175Sdamien }
1293e12e039eSstsp
1294e12e039eSstsp if (ieee80211_amsdu_decap_validate(ic, m, ni)) {
1295e12e039eSstsp /* stop processing A-MSDU subframes */
1296e12e039eSstsp ic->ic_stats.is_rx_decap++;
1297e12e039eSstsp ml_purge(&subframes);
1298e12e039eSstsp m_freem(m);
1299e12e039eSstsp return;
1300e12e039eSstsp }
1301e12e039eSstsp
1302e12e039eSstsp ml_enqueue(&subframes, m);
130345eec175Sdamien
130445eec175Sdamien m = n;
130545eec175Sdamien /* remove padding */
130645eec175Sdamien pad = ((len + 3) & ~3) - len;
130745eec175Sdamien m_adj(m, pad);
130845eec175Sdamien }
1309e9230c34Sstsp
1310e12e039eSstsp while ((n = ml_dequeue(&subframes)) != NULL)
1311e12e039eSstsp ieee80211_enqueue_data(ic, n, ni, mcast, ml);
1312e12e039eSstsp
1313e9230c34Sstsp m_freem(m);
131445eec175Sdamien }
131545eec175Sdamien
131616b96e48Sdamien /*
131716b96e48Sdamien * Parse an EDCA Parameter Set element (see 7.3.2.27).
1318fdcb679cSdamien */
1319ab235185Sdamien int
ieee80211_parse_edca_params_body(struct ieee80211com * ic,const u_int8_t * frm)13202d5bc21cSdamien ieee80211_parse_edca_params_body(struct ieee80211com *ic, const u_int8_t *frm)
1321ab235185Sdamien {
1322ab235185Sdamien u_int updtcount;
1323ab235185Sdamien int aci;
1324ab235185Sdamien
1325ab235185Sdamien /*
1326ab235185Sdamien * Check if EDCA parameters have changed XXX if we miss more than
1327ab235185Sdamien * 15 consecutive beacons, we might not detect changes to EDCA
1328ab235185Sdamien * parameters due to wraparound of the 4-bit Update Count field.
1329ab235185Sdamien */
1330ab235185Sdamien updtcount = frm[0] & 0xf;
1331ab235185Sdamien if (updtcount == ic->ic_edca_updtcount)
1332ab235185Sdamien return 0; /* no changes to EDCA parameters, ignore */
1333ab235185Sdamien ic->ic_edca_updtcount = updtcount;
1334ab235185Sdamien
1335d813d27eSdamien frm += 2; /* skip QoS Info & Reserved fields */
1336ab235185Sdamien
1337ab235185Sdamien /* parse AC Parameter Records */
1338ab235185Sdamien for (aci = 0; aci < EDCA_NUM_AC; aci++) {
1339ab235185Sdamien struct ieee80211_edca_ac_params *ac = &ic->ic_edca_ac[aci];
1340ab235185Sdamien
1341ab235185Sdamien ac->ac_acm = (frm[0] >> 4) & 0x1;
1342ab235185Sdamien ac->ac_aifsn = frm[0] & 0xf;
1343ab235185Sdamien ac->ac_ecwmin = frm[1] & 0xf;
1344ab235185Sdamien ac->ac_ecwmax = frm[1] >> 4;
1345ab235185Sdamien ac->ac_txoplimit = LE_READ_2(frm + 2);
1346ab235185Sdamien frm += 4;
1347ab235185Sdamien }
1348ab235185Sdamien /* give drivers a chance to update their settings */
1349ab235185Sdamien if ((ic->ic_flags & IEEE80211_F_QOS) && ic->ic_updateedca != NULL)
1350ab235185Sdamien (*ic->ic_updateedca)(ic);
1351ab235185Sdamien
1352ab235185Sdamien return 0;
1353ab235185Sdamien }
1354ab235185Sdamien
1355ab235185Sdamien int
ieee80211_parse_edca_params(struct ieee80211com * ic,const u_int8_t * frm)1356ab235185Sdamien ieee80211_parse_edca_params(struct ieee80211com *ic, const u_int8_t *frm)
1357ab235185Sdamien {
1358ab235185Sdamien if (frm[1] < 18) {
1359ab235185Sdamien ic->ic_stats.is_rx_elem_toosmall++;
13604b3a03eeSdamien return IEEE80211_REASON_IE_INVALID;
1361ab235185Sdamien }
13622d5bc21cSdamien return ieee80211_parse_edca_params_body(ic, frm + 2);
1363ab235185Sdamien }
1364ab235185Sdamien
1365ab235185Sdamien int
ieee80211_parse_wmm_params(struct ieee80211com * ic,const u_int8_t * frm)1366ab235185Sdamien ieee80211_parse_wmm_params(struct ieee80211com *ic, const u_int8_t *frm)
1367ab235185Sdamien {
1368ab235185Sdamien if (frm[1] < 24) {
1369ab235185Sdamien ic->ic_stats.is_rx_elem_toosmall++;
13704b3a03eeSdamien return IEEE80211_REASON_IE_INVALID;
1371ab235185Sdamien }
13722d5bc21cSdamien return ieee80211_parse_edca_params_body(ic, frm + 8);
13732d5bc21cSdamien }
13742d5bc21cSdamien
13752d5bc21cSdamien enum ieee80211_cipher
ieee80211_parse_rsn_cipher(const u_int8_t selector[4])13762d5bc21cSdamien ieee80211_parse_rsn_cipher(const u_int8_t selector[4])
13772d5bc21cSdamien {
1378db011a80Sdamien if (memcmp(selector, MICROSOFT_OUI, 3) == 0) { /* WPA */
13792d5bc21cSdamien switch (selector[3]) {
1380db011a80Sdamien case 0: /* use group data cipher suite */
13812d5bc21cSdamien return IEEE80211_CIPHER_USEGROUP;
13822d5bc21cSdamien case 1: /* WEP-40 */
13832d5bc21cSdamien return IEEE80211_CIPHER_WEP40;
13842d5bc21cSdamien case 2: /* TKIP */
13852d5bc21cSdamien return IEEE80211_CIPHER_TKIP;
13860e5a82c2Sdamien case 4: /* CCMP (RSNA default) */
13872d5bc21cSdamien return IEEE80211_CIPHER_CCMP;
13882d5bc21cSdamien case 5: /* WEP-104 */
13892d5bc21cSdamien return IEEE80211_CIPHER_WEP104;
13902d5bc21cSdamien }
1391db011a80Sdamien } else if (memcmp(selector, IEEE80211_OUI, 3) == 0) { /* RSN */
139290af3bf7Sstsp /* see 802.11-2012 Table 8-99 */
1393db011a80Sdamien switch (selector[3]) {
1394db011a80Sdamien case 0: /* use group data cipher suite */
1395db011a80Sdamien return IEEE80211_CIPHER_USEGROUP;
1396db011a80Sdamien case 1: /* WEP-40 */
1397db011a80Sdamien return IEEE80211_CIPHER_WEP40;
1398db011a80Sdamien case 2: /* TKIP */
1399db011a80Sdamien return IEEE80211_CIPHER_TKIP;
1400db011a80Sdamien case 4: /* CCMP (RSNA default) */
1401db011a80Sdamien return IEEE80211_CIPHER_CCMP;
1402db011a80Sdamien case 5: /* WEP-104 */
1403db011a80Sdamien return IEEE80211_CIPHER_WEP104;
140445eec175Sdamien case 6: /* BIP */
140545eec175Sdamien return IEEE80211_CIPHER_BIP;
1406db011a80Sdamien }
14072d5bc21cSdamien }
14082d5bc21cSdamien return IEEE80211_CIPHER_NONE; /* ignore unknown ciphers */
14092d5bc21cSdamien }
14102d5bc21cSdamien
14112d5bc21cSdamien enum ieee80211_akm
ieee80211_parse_rsn_akm(const u_int8_t selector[4])14122d5bc21cSdamien ieee80211_parse_rsn_akm(const u_int8_t selector[4])
14132d5bc21cSdamien {
1414c7c53e5eSdamien if (memcmp(selector, MICROSOFT_OUI, 3) == 0) { /* WPA */
14152d5bc21cSdamien switch (selector[3]) {
14162d5bc21cSdamien case 1: /* IEEE 802.1X (RSNA default) */
1417c7c53e5eSdamien return IEEE80211_AKM_8021X;
14182d5bc21cSdamien case 2: /* PSK */
14192d5bc21cSdamien return IEEE80211_AKM_PSK;
14202d5bc21cSdamien }
1421c7c53e5eSdamien } else if (memcmp(selector, IEEE80211_OUI, 3) == 0) { /* RSN */
1422c7c53e5eSdamien /* from IEEE Std 802.11i-2004 - Table 20dc */
1423c7c53e5eSdamien switch (selector[3]) {
1424c7c53e5eSdamien case 1: /* IEEE 802.1X (RSNA default) */
1425c7c53e5eSdamien return IEEE80211_AKM_8021X;
1426c7c53e5eSdamien case 2: /* PSK */
1427c7c53e5eSdamien return IEEE80211_AKM_PSK;
1428c7c53e5eSdamien case 5: /* IEEE 802.1X with SHA256 KDF */
1429c7c53e5eSdamien return IEEE80211_AKM_SHA256_8021X;
1430c7c53e5eSdamien case 6: /* PSK with SHA256 KDF */
1431c7c53e5eSdamien return IEEE80211_AKM_SHA256_PSK;
1432c7c53e5eSdamien }
14332d5bc21cSdamien }
14342d5bc21cSdamien return IEEE80211_AKM_NONE; /* ignore unknown AKMs */
1435ab235185Sdamien }
1436ab235185Sdamien
143716b96e48Sdamien /*
143890af3bf7Sstsp * Parse an RSN element (see 802.11-2012 8.4.2.27)
1439fdcb679cSdamien */
1440fdcb679cSdamien int
ieee80211_parse_rsn_body(struct ieee80211com * ic,const u_int8_t * frm,u_int len,struct ieee80211_rsnparams * rsn)1441e03e709cSdamien ieee80211_parse_rsn_body(struct ieee80211com *ic, const u_int8_t *frm,
1442e03e709cSdamien u_int len, struct ieee80211_rsnparams *rsn)
1443fdcb679cSdamien {
1444fdcb679cSdamien const u_int8_t *efrm;
1445fdcb679cSdamien u_int16_t m, n, s;
1446fdcb679cSdamien
14472d5bc21cSdamien efrm = frm + len;
1448fdcb679cSdamien
1449fdcb679cSdamien /* check Version field */
1450fdcb679cSdamien if (LE_READ_2(frm) != 1)
1451e03e709cSdamien return IEEE80211_STATUS_RSN_IE_VER_UNSUP;
1452fdcb679cSdamien frm += 2;
1453fdcb679cSdamien
1454fdcb679cSdamien /* all fields after the Version field are optional */
1455fdcb679cSdamien
1456fdcb679cSdamien /* if Cipher Suite missing, default to CCMP */
1457e03e709cSdamien rsn->rsn_groupcipher = IEEE80211_CIPHER_CCMP;
1458e03e709cSdamien rsn->rsn_nciphers = 1;
1459e03e709cSdamien rsn->rsn_ciphers = IEEE80211_CIPHER_CCMP;
1460678831beSjsg /* if Group Management Cipher Suite missing, default to BIP */
146145eec175Sdamien rsn->rsn_groupmgmtcipher = IEEE80211_CIPHER_BIP;
1462fdcb679cSdamien /* if AKM Suite missing, default to 802.1X */
1463e03e709cSdamien rsn->rsn_nakms = 1;
1464c7c53e5eSdamien rsn->rsn_akms = IEEE80211_AKM_8021X;
1465e03e709cSdamien /* if RSN capabilities missing, default to 0 */
1466e03e709cSdamien rsn->rsn_caps = 0;
1467f91ff320Sdamien rsn->rsn_npmkids = 0;
1468fdcb679cSdamien
1469db011a80Sdamien /* read Group Data Cipher Suite field */
1470fdcb679cSdamien if (frm + 4 > efrm)
1471fdcb679cSdamien return 0;
1472e03e709cSdamien rsn->rsn_groupcipher = ieee80211_parse_rsn_cipher(frm);
14737fda7cd9Sstsp if (rsn->rsn_groupcipher == IEEE80211_CIPHER_NONE ||
14747fda7cd9Sstsp rsn->rsn_groupcipher == IEEE80211_CIPHER_USEGROUP ||
14757fda7cd9Sstsp rsn->rsn_groupcipher == IEEE80211_CIPHER_BIP)
1476e03e709cSdamien return IEEE80211_STATUS_BAD_GROUP_CIPHER;
1477fdcb679cSdamien frm += 4;
1478fdcb679cSdamien
1479fdcb679cSdamien /* read Pairwise Cipher Suite Count field */
1480fdcb679cSdamien if (frm + 2 > efrm)
1481fdcb679cSdamien return 0;
1482e03e709cSdamien m = rsn->rsn_nciphers = LE_READ_2(frm);
1483fdcb679cSdamien frm += 2;
1484fdcb679cSdamien
1485fdcb679cSdamien /* read Pairwise Cipher Suite List */
1486fdcb679cSdamien if (frm + m * 4 > efrm)
1487e03e709cSdamien return IEEE80211_STATUS_IE_INVALID;
1488e03e709cSdamien rsn->rsn_ciphers = IEEE80211_CIPHER_NONE;
1489fdcb679cSdamien while (m-- > 0) {
1490e03e709cSdamien rsn->rsn_ciphers |= ieee80211_parse_rsn_cipher(frm);
1491fdcb679cSdamien frm += 4;
1492fdcb679cSdamien }
1493e03e709cSdamien if (rsn->rsn_ciphers & IEEE80211_CIPHER_USEGROUP) {
1494e03e709cSdamien if (rsn->rsn_ciphers != IEEE80211_CIPHER_USEGROUP)
1495e03e709cSdamien return IEEE80211_STATUS_BAD_PAIRWISE_CIPHER;
1496e03e709cSdamien if (rsn->rsn_groupcipher == IEEE80211_CIPHER_CCMP)
1497e03e709cSdamien return IEEE80211_STATUS_BAD_PAIRWISE_CIPHER;
1498fdcb679cSdamien }
1499fdcb679cSdamien
1500fdcb679cSdamien /* read AKM Suite List Count field */
1501fdcb679cSdamien if (frm + 2 > efrm)
1502fdcb679cSdamien return 0;
1503e03e709cSdamien n = rsn->rsn_nakms = LE_READ_2(frm);
1504fdcb679cSdamien frm += 2;
1505fdcb679cSdamien
1506fdcb679cSdamien /* read AKM Suite List */
1507fdcb679cSdamien if (frm + n * 4 > efrm)
1508e03e709cSdamien return IEEE80211_STATUS_IE_INVALID;
1509e03e709cSdamien rsn->rsn_akms = IEEE80211_AKM_NONE;
1510fdcb679cSdamien while (n-- > 0) {
1511e03e709cSdamien rsn->rsn_akms |= ieee80211_parse_rsn_akm(frm);
1512fdcb679cSdamien frm += 4;
1513fdcb679cSdamien }
1514fdcb679cSdamien
1515fdcb679cSdamien /* read RSN Capabilities field */
1516fdcb679cSdamien if (frm + 2 > efrm)
1517fdcb679cSdamien return 0;
1518e03e709cSdamien rsn->rsn_caps = LE_READ_2(frm);
1519fdcb679cSdamien frm += 2;
1520fdcb679cSdamien
1521fdcb679cSdamien /* read PMKID Count field */
1522fdcb679cSdamien if (frm + 2 > efrm)
1523fdcb679cSdamien return 0;
1524f91ff320Sdamien s = rsn->rsn_npmkids = LE_READ_2(frm);
1525fdcb679cSdamien frm += 2;
1526fdcb679cSdamien
1527fdcb679cSdamien /* read PMKID List */
15284b3a03eeSdamien if (frm + s * IEEE80211_PMKID_LEN > efrm)
1529e03e709cSdamien return IEEE80211_STATUS_IE_INVALID;
1530f91ff320Sdamien if (s != 0) {
1531f91ff320Sdamien rsn->rsn_pmkids = frm;
1532f91ff320Sdamien frm += s * IEEE80211_PMKID_LEN;
1533fdcb679cSdamien }
1534fdcb679cSdamien
1535db011a80Sdamien /* read Group Management Cipher Suite field */
1536db011a80Sdamien if (frm + 4 > efrm)
1537db011a80Sdamien return 0;
1538db011a80Sdamien rsn->rsn_groupmgmtcipher = ieee80211_parse_rsn_cipher(frm);
15397fda7cd9Sstsp if (rsn->rsn_groupmgmtcipher != IEEE80211_CIPHER_BIP)
15407fda7cd9Sstsp return IEEE80211_STATUS_BAD_GROUP_CIPHER;
1541db011a80Sdamien
1542e03e709cSdamien return IEEE80211_STATUS_SUCCESS;
1543fdcb679cSdamien }
1544fdcb679cSdamien
15452d5bc21cSdamien int
ieee80211_parse_rsn(struct ieee80211com * ic,const u_int8_t * frm,struct ieee80211_rsnparams * rsn)1546e03e709cSdamien ieee80211_parse_rsn(struct ieee80211com *ic, const u_int8_t *frm,
1547e03e709cSdamien struct ieee80211_rsnparams *rsn)
1548fdcb679cSdamien {
1549ba53b1fcSdamien if (frm[1] < 2) {
15502d5bc21cSdamien ic->ic_stats.is_rx_elem_toosmall++;
1551e03e709cSdamien return IEEE80211_STATUS_IE_INVALID;
1552fdcb679cSdamien }
1553e03e709cSdamien return ieee80211_parse_rsn_body(ic, frm + 2, frm[1], rsn);
1554fdcb679cSdamien }
1555fdcb679cSdamien
15562d5bc21cSdamien int
ieee80211_parse_wpa(struct ieee80211com * ic,const u_int8_t * frm,struct ieee80211_rsnparams * rsn)1557e03e709cSdamien ieee80211_parse_wpa(struct ieee80211com *ic, const u_int8_t *frm,
1558e03e709cSdamien struct ieee80211_rsnparams *rsn)
1559fdcb679cSdamien {
1560ba53b1fcSdamien if (frm[1] < 6) {
15612d5bc21cSdamien ic->ic_stats.is_rx_elem_toosmall++;
1562e03e709cSdamien return IEEE80211_STATUS_IE_INVALID;
1563fdcb679cSdamien }
1564e03e709cSdamien return ieee80211_parse_rsn_body(ic, frm + 6, frm[1] - 4, rsn);
1565fdcb679cSdamien }
1566fdcb679cSdamien
1567b3a42803Sdamien /*
1568e03e709cSdamien * Create (or update) a copy of an information element.
1569b3a42803Sdamien */
1570b3a42803Sdamien int
ieee80211_save_ie(const u_int8_t * frm,u_int8_t ** ie)1571b3a42803Sdamien ieee80211_save_ie(const u_int8_t *frm, u_int8_t **ie)
1572b3a42803Sdamien {
1573e93df640Stb int olen = *ie ? 2 + (*ie)[1] : 0;
1574e93df640Stb int len = 2 + frm[1];
1575e93df640Stb
1576e93df640Stb if (*ie == NULL || olen != len) {
1577b3a42803Sdamien if (*ie != NULL)
1578e93df640Stb free(*ie, M_DEVBUF, olen);
1579e93df640Stb *ie = malloc(len, M_DEVBUF, M_NOWAIT);
1580b3a42803Sdamien if (*ie == NULL)
1581b3a42803Sdamien return ENOMEM;
1582b3a42803Sdamien }
1583e93df640Stb memcpy(*ie, frm, len);
1584b3a42803Sdamien return 0;
1585b3a42803Sdamien }
1586b3a42803Sdamien
1587fdcb679cSdamien /*-
15889e9787ffSdamien * Beacon/Probe response frame format:
15899e9787ffSdamien * [8] Timestamp
15909e9787ffSdamien * [2] Beacon interval
15915252db7eSdamien * [2] Capability
15925252db7eSdamien * [tlv] Service Set Identifier (SSID)
15939e9787ffSdamien * [tlv] Supported rates
159445eec175Sdamien * [tlv] DS Parameter Set (802.11g)
15959e9787ffSdamien * [tlv] ERP Information (802.11g)
15969e9787ffSdamien * [tlv] Extended Supported Rates (802.11g)
1597fdcb679cSdamien * [tlv] RSN (802.11i)
1598ab235185Sdamien * [tlv] EDCA Parameter Set (802.11e)
1599ab235185Sdamien * [tlv] QoS Capability (Beacon only, 802.11e)
160045eec175Sdamien * [tlv] HT Capabilities (802.11n)
160145eec175Sdamien * [tlv] HT Operation (802.11n)
16029e9787ffSdamien */
160391b2158bSmillert void
ieee80211_recv_probe_resp(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * rni,struct ieee80211_rxinfo * rxi,int isprobe)1604ec69e05aSdamien ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m,
1605b639b680Stobhe struct ieee80211_node *rni, struct ieee80211_rxinfo *rxi, int isprobe)
160691b2158bSmillert {
1607b639b680Stobhe struct ieee80211_node *ni;
16089e9787ffSdamien const struct ieee80211_frame *wh;
1609f22d9adcSdamien const u_int8_t *frm, *efrm;
16107b7ad45cSstsp const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie, *tim;
161150e8fe1cSstsp const u_int8_t *rsnie, *wpaie, *htcaps, *htop, *vhtcaps, *vhtop;
1612e63afc57Sdamien u_int16_t capinfo, bintval;
16137b7ad45cSstsp u_int8_t chan, bchan, erp;
16149e9787ffSdamien int is_new;
161591b2158bSmillert
16160fd4e251Sreyk /*
16170fd4e251Sreyk * We process beacon/probe response frames for:
16180fd4e251Sreyk * o station mode: to collect state
16190fd4e251Sreyk * updates such as 802.11g slot time and for passive
16200fd4e251Sreyk * scanning of APs
16210fd4e251Sreyk * o adhoc mode: to discover neighbors
16220fd4e251Sreyk * o hostap mode: for passive scanning of neighbor APs
16230fd4e251Sreyk * o when scanning
16240fd4e251Sreyk * In other words, in all modes other than monitor (which
1625361aed72Sdamien * does not process incoming frames) and adhoc-demo (which
16260fd4e251Sreyk * does not use management frames at all).
16270fd4e251Sreyk */
16280fd4e251Sreyk #ifdef DIAGNOSTIC
16290fd4e251Sreyk if (ic->ic_opmode != IEEE80211_M_STA &&
1630171ac09aSdamien #ifndef IEEE80211_STA_ONLY
16310fd4e251Sreyk ic->ic_opmode != IEEE80211_M_IBSS &&
16320fd4e251Sreyk ic->ic_opmode != IEEE80211_M_HOSTAP &&
1633171ac09aSdamien #endif
163491b2158bSmillert ic->ic_state != IEEE80211_S_SCAN) {
16350fd4e251Sreyk panic("%s: impossible operating mode", __func__);
163691b2158bSmillert }
16370fd4e251Sreyk #endif
1638361aed72Sdamien /* make sure all mandatory fixed fields are present */
1639ec69e05aSdamien if (m->m_len < sizeof(*wh) + 12) {
1640361aed72Sdamien DPRINTF(("frame too short\n"));
1641361aed72Sdamien return;
1642361aed72Sdamien }
1643ec69e05aSdamien wh = mtod(m, struct ieee80211_frame *);
16449e9787ffSdamien frm = (const u_int8_t *)&wh[1];
1645ec69e05aSdamien efrm = mtod(m, u_int8_t *) + m->m_len;
164691b2158bSmillert
164791b2158bSmillert tstamp = frm; frm += 8;
16483a65e8c8Sdamien bintval = LE_READ_2(frm); frm += 2;
16493a65e8c8Sdamien capinfo = LE_READ_2(frm); frm += 2;
1650fcdd546dSdamien
16517b7ad45cSstsp ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = tim = NULL;
165250e8fe1cSstsp htcaps = htop = vhtcaps = vhtop = NULL;
16537307575aSstsp if (rxi->rxi_chan)
16547307575aSstsp bchan = rxi->rxi_chan;
16557307575aSstsp else
165691b2158bSmillert bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
165791b2158bSmillert chan = bchan;
165891b2158bSmillert erp = 0;
1659df33438bSdamien while (frm + 2 <= efrm) {
1660df33438bSdamien if (frm + 2 + frm[1] > efrm) {
1661df33438bSdamien ic->ic_stats.is_rx_elem_toosmall++;
1662b2985342Sdamien break;
1663df33438bSdamien }
1664df33438bSdamien switch (frm[0]) {
166591b2158bSmillert case IEEE80211_ELEMID_SSID:
166691b2158bSmillert ssid = frm;
166791b2158bSmillert break;
166891b2158bSmillert case IEEE80211_ELEMID_RATES:
166991b2158bSmillert rates = frm;
167091b2158bSmillert break;
167191b2158bSmillert case IEEE80211_ELEMID_DSPARMS:
1672df33438bSdamien if (frm[1] < 1) {
1673df33438bSdamien ic->ic_stats.is_rx_elem_toosmall++;
1674df33438bSdamien break;
1675df33438bSdamien }
167691b2158bSmillert chan = frm[2];
167791b2158bSmillert break;
167891b2158bSmillert case IEEE80211_ELEMID_XRATES:
167991b2158bSmillert xrates = frm;
168091b2158bSmillert break;
168191b2158bSmillert case IEEE80211_ELEMID_ERP:
1682df33438bSdamien if (frm[1] < 1) {
1683df33438bSdamien ic->ic_stats.is_rx_elem_toosmall++;
168491b2158bSmillert break;
168591b2158bSmillert }
168691b2158bSmillert erp = frm[2];
168791b2158bSmillert break;
1688fdcb679cSdamien case IEEE80211_ELEMID_RSN:
1689e03e709cSdamien rsnie = frm;
1690fdcb679cSdamien break;
1691ab235185Sdamien case IEEE80211_ELEMID_EDCAPARMS:
1692e03e709cSdamien edcaie = frm;
1693ab235185Sdamien break;
169445eec175Sdamien case IEEE80211_ELEMID_HTCAPS:
169545eec175Sdamien htcaps = frm;
169645eec175Sdamien break;
169745eec175Sdamien case IEEE80211_ELEMID_HTOP:
1698b5c5cec8Sstsp if (frm[1] < 22) {
1699b5c5cec8Sstsp ic->ic_stats.is_rx_elem_toosmall++;
1700b5c5cec8Sstsp break;
1701b5c5cec8Sstsp }
170245eec175Sdamien htop = frm;
1703b5c5cec8Sstsp chan = frm[2];
170445eec175Sdamien break;
170550e8fe1cSstsp case IEEE80211_ELEMID_VHTCAPS:
170650e8fe1cSstsp vhtcaps = frm;
170750e8fe1cSstsp break;
170850e8fe1cSstsp case IEEE80211_ELEMID_VHTOP:
170950e8fe1cSstsp vhtop = frm;
171050e8fe1cSstsp break;
1711bacd9707Sstsp case IEEE80211_ELEMID_TIM:
17127b7ad45cSstsp if (frm[1] < 4) {
17137b7ad45cSstsp ic->ic_stats.is_rx_elem_toosmall++;
17147b7ad45cSstsp break;
1715bacd9707Sstsp }
17167b7ad45cSstsp tim = frm;
1717bacd9707Sstsp break;
1718ba56bee9Sdamien case IEEE80211_ELEMID_VENDOR:
1719ba56bee9Sdamien if (frm[1] < 4) {
1720ba56bee9Sdamien ic->ic_stats.is_rx_elem_toosmall++;
1721ba56bee9Sdamien break;
1722ba56bee9Sdamien }
17237c7d9786Sdamien if (memcmp(frm + 2, MICROSOFT_OUI, 3) == 0) {
17244b3a03eeSdamien if (frm[5] == 1)
1725e03e709cSdamien wpaie = frm;
17264b3a03eeSdamien else if (frm[1] >= 5 &&
17274b3a03eeSdamien frm[5] == 2 && frm[6] == 1)
1728e03e709cSdamien wmmie = frm;
1729ba56bee9Sdamien }
1730ba56bee9Sdamien break;
173191b2158bSmillert }
1732df33438bSdamien frm += 2 + frm[1];
173391b2158bSmillert }
1734fcdd546dSdamien /* supported rates element is mandatory */
1735fcdd546dSdamien if (rates == NULL || rates[1] > IEEE80211_RATE_MAXSIZE) {
1736932b9027Sdamien DPRINTF(("invalid supported rates element\n"));
1737fcdd546dSdamien return;
1738fcdd546dSdamien }
1739fcdd546dSdamien /* SSID element is mandatory */
1740fcdd546dSdamien if (ssid == NULL || ssid[1] > IEEE80211_NWID_LEN) {
1741932b9027Sdamien DPRINTF(("invalid SSID element\n"));
1742fcdd546dSdamien return;
1743fcdd546dSdamien }
1744171ac09aSdamien
174591b2158bSmillert if (
174691b2158bSmillert #if IEEE80211_CHAN_MAX < 255
174791b2158bSmillert chan > IEEE80211_CHAN_MAX ||
174891b2158bSmillert #endif
1749ef1d73a0Sstsp (isclr(ic->ic_chan_active, chan) &&
1750ef1d73a0Sstsp ((ic->ic_caps & IEEE80211_C_SCANALL) == 0 ||
1751ef1d73a0Sstsp (ic->ic_flags & IEEE80211_F_BGSCAN) == 0))) {
1752932b9027Sdamien DPRINTF(("ignore %s with invalid channel %u\n",
1753932b9027Sdamien isprobe ? "probe response" : "beacon", chan));
175491b2158bSmillert ic->ic_stats.is_rx_badchan++;
175591b2158bSmillert return;
175691b2158bSmillert }
17577307575aSstsp if ((rxi->rxi_chan != 0 && chan != rxi->rxi_chan) ||
17587307575aSstsp ((ic->ic_state != IEEE80211_S_SCAN ||
17592c86ad7bSdamien !(ic->ic_caps & IEEE80211_C_SCANALL)) &&
17607307575aSstsp chan != bchan)) {
176191b2158bSmillert /*
176291b2158bSmillert * Frame was received on a channel different from the
176391b2158bSmillert * one indicated in the DS params element id;
176491b2158bSmillert * silently discard it.
176591b2158bSmillert *
176691b2158bSmillert * NB: this can happen due to signal leakage.
176791b2158bSmillert */
1768932b9027Sdamien DPRINTF(("ignore %s on channel %u marked for channel %u\n",
1769932b9027Sdamien isprobe ? "probe response" : "beacon", bchan, chan));
177091b2158bSmillert ic->ic_stats.is_rx_chanmismatch++;
177191b2158bSmillert return;
177291b2158bSmillert }
17733ce67372Sreyk
177491b2158bSmillert #ifdef IEEE80211_DEBUG
17751edcfd58Sstsp if (ieee80211_debug > 1 &&
177606e069b5Sstsp (ni == NULL || ic->ic_state == IEEE80211_S_SCAN ||
177706e069b5Sstsp (ic->ic_flags & IEEE80211_F_BGSCAN))) {
177891b2158bSmillert printf("%s: %s%s on chan %u (bss chan %u) ",
177991b2158bSmillert __func__, (ni == NULL ? "new " : ""),
1780e03e709cSdamien isprobe ? "probe response" : "beacon",
178191b2158bSmillert chan, bchan);
178291b2158bSmillert ieee80211_print_essid(ssid + 2, ssid[1]);
1783d813d27eSdamien printf(" from %s\n", ether_sprintf((u_int8_t *)wh->i_addr2));
178491b2158bSmillert printf("%s: caps 0x%x bintval %u erp 0x%x\n",
17853a65e8c8Sdamien __func__, capinfo, bintval, erp);
178691b2158bSmillert }
178791b2158bSmillert #endif
17883ce67372Sreyk
17893ce67372Sreyk if ((ni = ieee80211_find_node(ic, wh->i_addr2)) == NULL) {
179091b2158bSmillert ni = ieee80211_alloc_node(ic, wh->i_addr2);
179191b2158bSmillert if (ni == NULL)
179291b2158bSmillert return;
17930fd4e251Sreyk is_new = 1;
17940fd4e251Sreyk } else
17950fd4e251Sreyk is_new = 0;
17960fd4e251Sreyk
17977307575aSstsp ni->ni_chan = &ic->ic_channels[chan];
17987307575aSstsp
1799cb2109bfSstsp if (htcaps)
1800cb2109bfSstsp ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
1801f9214ef6Sstsp if (htop && !ieee80211_setup_htop(ni, htop + 2, htop[1], 1))
18027a831903Sstsp htop = NULL; /* invalid HTOP */
18037307575aSstsp if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) {
180450e8fe1cSstsp ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
180550e8fe1cSstsp if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
180650e8fe1cSstsp vhtop = NULL; /* invalid VHTOP */
180750e8fe1cSstsp }
1808cb2109bfSstsp
18097b7ad45cSstsp if (tim) {
18107b7ad45cSstsp ni->ni_dtimcount = tim[2];
18117b7ad45cSstsp ni->ni_dtimperiod = tim[3];
18127b7ad45cSstsp }
1813bacd9707Sstsp
18141bb78573Sdamien /*
18151bb78573Sdamien * When operating in station mode, check for state updates
1816cb2109bfSstsp * while we're associated.
18171bb78573Sdamien */
18181bb78573Sdamien if (ic->ic_opmode == IEEE80211_M_STA &&
1819e03e709cSdamien ic->ic_state == IEEE80211_S_RUN &&
18207ff1e41dSdamien ni->ni_state == IEEE80211_STA_BSS) {
182109268e1fSstsp int updateprot = 0;
18221bb78573Sdamien /*
1823e03e709cSdamien * Check if protection mode has changed since last beacon.
18241bb78573Sdamien */
18251bb78573Sdamien if (ni->ni_erp != erp) {
1826932b9027Sdamien DPRINTF(("[%s] erp change: was 0x%x, now 0x%x\n",
1827d813d27eSdamien ether_sprintf((u_int8_t *)wh->i_addr2),
1828d813d27eSdamien ni->ni_erp, erp));
1829a3e15932Sstsp if ((ic->ic_curmode == IEEE80211_MODE_11G ||
1830a3e15932Sstsp (ic->ic_curmode == IEEE80211_MODE_11N &&
1831a3e15932Sstsp IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))) &&
18321bb78573Sdamien (erp & IEEE80211_ERP_USE_PROTECTION))
18331bb78573Sdamien ic->ic_flags |= IEEE80211_F_USEPROT;
18341bb78573Sdamien else
18351bb78573Sdamien ic->ic_flags &= ~IEEE80211_F_USEPROT;
18367ff1e41dSdamien ic->ic_bss->ni_erp = erp;
183709268e1fSstsp updateprot = 1;
18381bb78573Sdamien }
18397a831903Sstsp if (htop && (ic->ic_bss->ni_flags & IEEE80211_NODE_HT)) {
1840cb2109bfSstsp enum ieee80211_htprot htprot_last, htprot;
1841cb2109bfSstsp htprot_last =
1842cb2109bfSstsp ((ic->ic_bss->ni_htop1 & IEEE80211_HTOP1_PROT_MASK)
1843cb2109bfSstsp >> IEEE80211_HTOP1_PROT_SHIFT);
1844cb2109bfSstsp htprot = ((ni->ni_htop1 & IEEE80211_HTOP1_PROT_MASK) >>
1845cb2109bfSstsp IEEE80211_HTOP1_PROT_SHIFT);
1846cb2109bfSstsp if (htprot_last != htprot) {
1847cb2109bfSstsp DPRINTF(("[%s] htprot change: was %d, now %d\n",
1848cb2109bfSstsp ether_sprintf((u_int8_t *)wh->i_addr2),
1849cb2109bfSstsp htprot_last, htprot));
185044fdf27cSstsp ic->ic_stats.is_ht_prot_change++;
1851cb2109bfSstsp ic->ic_bss->ni_htop1 = ni->ni_htop1;
185209268e1fSstsp updateprot = 1;
1853cb2109bfSstsp }
1854cb2109bfSstsp }
185509268e1fSstsp if (updateprot && ic->ic_updateprot != NULL)
185609268e1fSstsp ic->ic_updateprot(ic);
1857cb2109bfSstsp
18581bb78573Sdamien /*
185988290e74Sstsp * Check if 40MHz channel mode has changed since last beacon.
186088290e74Sstsp */
186188290e74Sstsp if (htop && (ic->ic_bss->ni_flags & IEEE80211_NODE_HT) &&
186288290e74Sstsp (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40)) {
186388290e74Sstsp uint8_t chw_last, chw, sco_last, sco;
186488290e74Sstsp chw_last = (ic->ic_bss->ni_htop0 & IEEE80211_HTOP0_CHW);
186588290e74Sstsp chw = (ni->ni_htop0 & IEEE80211_HTOP0_CHW);
186688290e74Sstsp sco_last =
186788290e74Sstsp ((ic->ic_bss->ni_htop0 & IEEE80211_HTOP0_SCO_MASK)
186888290e74Sstsp >> IEEE80211_HTOP0_SCO_SHIFT);
186988290e74Sstsp sco = ((ni->ni_htop0 & IEEE80211_HTOP0_SCO_MASK) >>
187088290e74Sstsp IEEE80211_HTOP0_SCO_SHIFT);
187188290e74Sstsp ic->ic_bss->ni_htop0 = ni->ni_htop0;
187288290e74Sstsp if (chw_last != chw || sco_last != sco) {
187388290e74Sstsp if (ic->ic_updatechan != NULL)
187488290e74Sstsp ic->ic_updatechan(ic);
187588290e74Sstsp }
187688290e74Sstsp } else if (htop)
187788290e74Sstsp ic->ic_bss->ni_htop0 = ni->ni_htop0;
187888290e74Sstsp
187988290e74Sstsp /*
18801bb78573Sdamien * Check if AP short slot time setting has changed
18811bb78573Sdamien * since last beacon and give the driver a chance to
18821bb78573Sdamien * update the hardware.
18831bb78573Sdamien */
18843a65e8c8Sdamien if ((ni->ni_capinfo ^ capinfo) &
18851bb78573Sdamien IEEE80211_CAPINFO_SHORT_SLOTTIME) {
18861bb78573Sdamien ieee80211_set_shortslottime(ic,
18871bb78573Sdamien ic->ic_curmode == IEEE80211_MODE_11A ||
18883a65e8c8Sdamien (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
18891bb78573Sdamien }
189069759a96Sstsp
18917b7ad45cSstsp if (tim && ic->ic_bss->ni_dtimperiod != ni->ni_dtimperiod) {
18927b7ad45cSstsp ic->ic_bss->ni_dtimperiod = ni->ni_dtimperiod;
18937b7ad45cSstsp ic->ic_bss->ni_dtimcount = ni->ni_dtimcount;
18947b7ad45cSstsp
18957b7ad45cSstsp if (ic->ic_updatedtim != NULL)
18967b7ad45cSstsp ic->ic_updatedtim(ic);
18977b7ad45cSstsp }
18987b7ad45cSstsp
189969759a96Sstsp /*
190069759a96Sstsp * Reset management timer. If it is non-zero in RUN state, the
190169759a96Sstsp * driver sent a probe request after a missed beacon event.
190269759a96Sstsp * This probe response indicates the AP is still serving us
190369759a96Sstsp * so don't allow ieee80211_watchdog() to move us into SCAN.
190469759a96Sstsp */
190506e069b5Sstsp if ((ic->ic_flags & IEEE80211_F_BGSCAN) == 0)
190669759a96Sstsp ic->ic_mgt_timer = 0;
19071bb78573Sdamien }
1908e03e709cSdamien /*
1909e03e709cSdamien * We do not try to update EDCA parameters if QoS was not negotiated
1910e03e709cSdamien * with the AP at association time.
1911e03e709cSdamien */
1912ba56bee9Sdamien if (ni->ni_flags & IEEE80211_NODE_QOS) {
1913e03e709cSdamien /* always prefer EDCA IE over Wi-Fi Alliance WMM IE */
19147e37070fSstsp if ((edcaie != NULL &&
19157e37070fSstsp ieee80211_parse_edca_params(ic, edcaie) == 0) ||
19167e37070fSstsp (wmmie != NULL &&
19177e37070fSstsp ieee80211_parse_wmm_params(ic, wmmie) == 0))
19187e37070fSstsp ni->ni_flags |= IEEE80211_NODE_QOS;
19197e37070fSstsp else
19207e37070fSstsp ni->ni_flags &= ~IEEE80211_NODE_QOS;
1921ba56bee9Sdamien }
1922e03e709cSdamien
192306e069b5Sstsp if (ic->ic_state == IEEE80211_S_SCAN ||
192406e069b5Sstsp (ic->ic_flags & IEEE80211_F_BGSCAN)) {
1925b2709672Sstsp struct ieee80211_rsnparams rsn, wpa;
1926b2709672Sstsp
1927b2709672Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
1928b2709672Sstsp ni->ni_supported_rsnprotos = IEEE80211_PROTO_NONE;
1929b2709672Sstsp ni->ni_rsnakms = 0;
1930b2709672Sstsp ni->ni_supported_rsnakms = 0;
1931b2709672Sstsp ni->ni_rsnciphers = 0;
1932b2709672Sstsp ni->ni_rsngroupcipher = 0;
1933b2709672Sstsp ni->ni_rsngroupmgmtcipher = 0;
1934b2709672Sstsp ni->ni_rsncaps = 0;
1935b2709672Sstsp
1936b2709672Sstsp if (rsnie != NULL &&
1937b2709672Sstsp ieee80211_parse_rsn(ic, rsnie, &rsn) == 0) {
1938b2709672Sstsp ni->ni_supported_rsnprotos |= IEEE80211_PROTO_RSN;
1939b2709672Sstsp ni->ni_supported_rsnakms |= rsn.rsn_akms;
1940b2709672Sstsp }
1941b2709672Sstsp if (wpaie != NULL &&
1942b2709672Sstsp ieee80211_parse_wpa(ic, wpaie, &wpa) == 0) {
1943b2709672Sstsp ni->ni_supported_rsnprotos |= IEEE80211_PROTO_WPA;
1944b2709672Sstsp ni->ni_supported_rsnakms |= wpa.rsn_akms;
1945b2709672Sstsp }
1946b2709672Sstsp
1947e03e709cSdamien /*
1948b2709672Sstsp * If the AP advertises both WPA and RSN IEs (WPA1+WPA2),
1949b2709672Sstsp * we only use the highest protocol version we support.
1950e03e709cSdamien */
1951e03e709cSdamien if (rsnie != NULL &&
1952b2709672Sstsp (ni->ni_supported_rsnprotos & IEEE80211_PROTO_RSN) &&
19530deee38cSstsp (ic->ic_caps & IEEE80211_C_RSN)) {
195412672b37Sstsp if (ieee80211_save_ie(rsnie, &ni->ni_rsnie) == 0
195512672b37Sstsp #ifndef IEEE80211_STA_ONLY
195612672b37Sstsp && ic->ic_opmode != IEEE80211_M_HOSTAP
195712672b37Sstsp #endif
195812672b37Sstsp ) {
1959e03e709cSdamien ni->ni_rsnprotos = IEEE80211_PROTO_RSN;
1960e03e709cSdamien ni->ni_rsnakms = rsn.rsn_akms;
1961e03e709cSdamien ni->ni_rsnciphers = rsn.rsn_ciphers;
1962e03e709cSdamien ni->ni_rsngroupcipher = rsn.rsn_groupcipher;
1963b2709672Sstsp ni->ni_rsngroupmgmtcipher =
1964b2709672Sstsp rsn.rsn_groupmgmtcipher;
1965e03e709cSdamien ni->ni_rsncaps = rsn.rsn_caps;
1966b2709672Sstsp }
1967b2709672Sstsp } else if (wpaie != NULL &&
1968b2709672Sstsp (ni->ni_supported_rsnprotos & IEEE80211_PROTO_WPA) &&
19690deee38cSstsp (ic->ic_caps & IEEE80211_C_RSN)) {
197012672b37Sstsp if (ieee80211_save_ie(wpaie, &ni->ni_rsnie) == 0
197112672b37Sstsp #ifndef IEEE80211_STA_ONLY
197212672b37Sstsp && ic->ic_opmode != IEEE80211_M_HOSTAP
197312672b37Sstsp #endif
197412672b37Sstsp ) {
1975b2709672Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_WPA;
1976b2709672Sstsp ni->ni_rsnakms = wpa.rsn_akms;
1977b2709672Sstsp ni->ni_rsnciphers = wpa.rsn_ciphers;
1978b2709672Sstsp ni->ni_rsngroupcipher = wpa.rsn_groupcipher;
1979b2709672Sstsp ni->ni_rsngroupmgmtcipher =
1980b2709672Sstsp wpa.rsn_groupmgmtcipher;
1981b2709672Sstsp ni->ni_rsncaps = wpa.rsn_caps;
1982b2709672Sstsp }
1983b2709672Sstsp }
1984a3353d28Sstsp }
1985e03e709cSdamien
19867c94704cSstsp /*
19877c94704cSstsp * Set our SSID if we do not know it yet.
19887c94704cSstsp * If we are doing a directed scan for an AP with a hidden SSID
19897c94704cSstsp * we must collect the SSID from a probe response to override
19907c94704cSstsp * a non-zero-length SSID filled with zeroes that we may have
19917c94704cSstsp * received earlier in a beacon.
19927c94704cSstsp */
1993466498d2Sstsp if (ssid[1] != 0 && ni->ni_essid[0] == '\0') {
199491b2158bSmillert ni->ni_esslen = ssid[1];
199591b2158bSmillert memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
19962c86ad7bSdamien /* we know that ssid[1] <= IEEE80211_NWID_LEN */
19972c86ad7bSdamien memcpy(ni->ni_essid, &ssid[2], ssid[1]);
19980fd4e251Sreyk }
199991b2158bSmillert IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
20000981e7bcSstsp if (ic->ic_state == IEEE80211_S_SCAN &&
20010981e7bcSstsp IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) {
20020981e7bcSstsp /*
20030981e7bcSstsp * During a scan on 5Ghz, prefer RSSI measured for probe
20040981e7bcSstsp * response frames. i.e. don't allow beacons to lower the
20050981e7bcSstsp * measured RSSI. Some 5GHz APs send beacons with much
20060981e7bcSstsp * less Tx power than they use for probe responses.
20070981e7bcSstsp */
200814e9f9eeSpatrick if (isprobe || ni->ni_rssi == 0)
20090981e7bcSstsp ni->ni_rssi = rxi->rxi_rssi;
20100981e7bcSstsp else if (ni->ni_rssi < rxi->rxi_rssi)
20110981e7bcSstsp ni->ni_rssi = rxi->rxi_rssi;
20120981e7bcSstsp } else
201330d05aacSdamien ni->ni_rssi = rxi->rxi_rssi;
201430d05aacSdamien ni->ni_rstamp = rxi->rxi_tstamp;
201591b2158bSmillert memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp));
20163a65e8c8Sdamien ni->ni_intval = bintval;
20173a65e8c8Sdamien ni->ni_capinfo = capinfo;
201891b2158bSmillert ni->ni_erp = erp;
201991b2158bSmillert /* NB: must be after ni_chan is setup */
20209e9787ffSdamien ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT);
2021171ac09aSdamien #ifndef IEEE80211_STA_ONLY
20229f0d6530Sstsp if (ic->ic_opmode == IEEE80211_M_IBSS && is_new && isprobe) {
202391b2158bSmillert /*
2024a671ffa0Sguenther * Fake an association so the driver can setup its
202591b2158bSmillert * private state. The rate set has been setup above;
202691b2158bSmillert * there is no handshake as in ap/station operation.
202791b2158bSmillert */
202891b2158bSmillert if (ic->ic_newassoc)
202991b2158bSmillert (*ic->ic_newassoc)(ic, ni, 1);
203091b2158bSmillert }
20319f0d6530Sstsp #endif
203291b2158bSmillert }
203391b2158bSmillert
2034171ac09aSdamien #ifndef IEEE80211_STA_ONLY
20359e9787ffSdamien /*-
20369e9787ffSdamien * Probe request frame format:
20379e9787ffSdamien * [tlv] SSID
20389e9787ffSdamien * [tlv] Supported rates
20399e9787ffSdamien * [tlv] Extended Supported Rates (802.11g)
204045eec175Sdamien * [tlv] HT Capabilities (802.11n)
20419e9787ffSdamien */
20429e9787ffSdamien void
ieee80211_recv_probe_req(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,struct ieee80211_rxinfo * rxi)2043ec69e05aSdamien ieee80211_recv_probe_req(struct ieee80211com *ic, struct mbuf *m,
204430d05aacSdamien struct ieee80211_node *ni, struct ieee80211_rxinfo *rxi)
20459e9787ffSdamien {
20469e9787ffSdamien const struct ieee80211_frame *wh;
20479e9787ffSdamien const u_int8_t *frm, *efrm;
204850e8fe1cSstsp const u_int8_t *ssid, *rates, *xrates, *htcaps, *vhtcaps;
204991b2158bSmillert u_int8_t rate;
205091b2158bSmillert
20511bb78573Sdamien if (ic->ic_opmode == IEEE80211_M_STA ||
20521bb78573Sdamien ic->ic_state != IEEE80211_S_RUN)
205391b2158bSmillert return;
205491b2158bSmillert
2055ec69e05aSdamien wh = mtod(m, struct ieee80211_frame *);
20569e9787ffSdamien frm = (const u_int8_t *)&wh[1];
2057ec69e05aSdamien efrm = mtod(m, u_int8_t *) + m->m_len;
20589e9787ffSdamien
205950e8fe1cSstsp ssid = rates = xrates = htcaps = vhtcaps = NULL;
2060df33438bSdamien while (frm + 2 <= efrm) {
2061df33438bSdamien if (frm + 2 + frm[1] > efrm) {
2062df33438bSdamien ic->ic_stats.is_rx_elem_toosmall++;
2063b2985342Sdamien break;
2064df33438bSdamien }
2065df33438bSdamien switch (frm[0]) {
206691b2158bSmillert case IEEE80211_ELEMID_SSID:
206791b2158bSmillert ssid = frm;
206891b2158bSmillert break;
206991b2158bSmillert case IEEE80211_ELEMID_RATES:
207091b2158bSmillert rates = frm;
207191b2158bSmillert break;
207291b2158bSmillert case IEEE80211_ELEMID_XRATES:
207391b2158bSmillert xrates = frm;
207491b2158bSmillert break;
207545eec175Sdamien case IEEE80211_ELEMID_HTCAPS:
207645eec175Sdamien htcaps = frm;
207745eec175Sdamien break;
207850e8fe1cSstsp case IEEE80211_ELEMID_VHTCAPS:
207950e8fe1cSstsp vhtcaps = frm;
208050e8fe1cSstsp break;
208191b2158bSmillert }
2082df33438bSdamien frm += 2 + frm[1];
208391b2158bSmillert }
2084fcdd546dSdamien /* supported rates element is mandatory */
2085fcdd546dSdamien if (rates == NULL || rates[1] > IEEE80211_RATE_MAXSIZE) {
2086932b9027Sdamien DPRINTF(("invalid supported rates element\n"));
2087fcdd546dSdamien return;
2088fcdd546dSdamien }
2089fcdd546dSdamien /* SSID element is mandatory */
2090fcdd546dSdamien if (ssid == NULL || ssid[1] > IEEE80211_NWID_LEN) {
2091932b9027Sdamien DPRINTF(("invalid SSID element\n"));
2092fcdd546dSdamien return;
2093fcdd546dSdamien }
2094fcdd546dSdamien /* check that the specified SSID (if not wildcard) matches ours */
20952c86ad7bSdamien if (ssid[1] != 0 && (ssid[1] != ic->ic_bss->ni_esslen ||
20962c86ad7bSdamien memcmp(&ssid[2], ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen))) {
2097932b9027Sdamien DPRINTF(("SSID mismatch\n"));
2098fcdd546dSdamien ic->ic_stats.is_rx_ssidmismatch++;
2099fcdd546dSdamien return;
2100fcdd546dSdamien }
2101fcdd546dSdamien /* refuse wildcard SSID if we're hiding our SSID in beacons */
2102eb9f82c0Sstsp if (ssid[1] == 0 && (ic->ic_userflags & IEEE80211_F_HIDENWID)) {
2103932b9027Sdamien DPRINTF(("wildcard SSID rejected"));
210475175d43Sreyk ic->ic_stats.is_rx_ssidmismatch++;
210575175d43Sreyk return;
210675175d43Sreyk }
210791b2158bSmillert
210891b2158bSmillert if (ni == ic->ic_bss) {
2109d4dcdec6Sstsp ni = ieee80211_find_node(ic, wh->i_addr2);
2110d4dcdec6Sstsp if (ni == NULL)
211191b2158bSmillert ni = ieee80211_dup_bss(ic, wh->i_addr2);
211291b2158bSmillert if (ni == NULL)
211391b2158bSmillert return;
2114932b9027Sdamien DPRINTF(("new probe req from %s\n",
2115932b9027Sdamien ether_sprintf((u_int8_t *)wh->i_addr2)));
21160fd4e251Sreyk }
211730d05aacSdamien ni->ni_rssi = rxi->rxi_rssi;
211830d05aacSdamien ni->ni_rstamp = rxi->rxi_tstamp;
211991b2158bSmillert rate = ieee80211_setup_rates(ic, ni, rates, xrates,
212016b96e48Sdamien IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
212116b96e48Sdamien IEEE80211_F_DODEL);
212291b2158bSmillert if (rate & IEEE80211_RATE_BASIC) {
2123932b9027Sdamien DPRINTF(("rate mismatch for %s\n",
2124932b9027Sdamien ether_sprintf((u_int8_t *)wh->i_addr2)));
2125e03e709cSdamien return;
212691b2158bSmillert }
2127fe8f5243Sstsp if (htcaps)
2128fe8f5243Sstsp ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
21292040b0e1Sstsp else
21302040b0e1Sstsp ieee80211_clear_htcaps(ni);
213150e8fe1cSstsp if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
213250e8fe1cSstsp ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
213350e8fe1cSstsp else
213450e8fe1cSstsp ieee80211_clear_vhtcaps(ni);
2135e03e709cSdamien IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
213691b2158bSmillert }
2137171ac09aSdamien #endif /* IEEE80211_STA_ONLY */
213891b2158bSmillert
21399e9787ffSdamien /*-
21409e9787ffSdamien * Authentication frame format:
21419e9787ffSdamien * [2] Authentication algorithm number
21429e9787ffSdamien * [2] Authentication transaction sequence number
21439e9787ffSdamien * [2] Status code
214491b2158bSmillert */
21459e9787ffSdamien void
ieee80211_recv_auth(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,struct ieee80211_rxinfo * rxi)2146ec69e05aSdamien ieee80211_recv_auth(struct ieee80211com *ic, struct mbuf *m,
214730d05aacSdamien struct ieee80211_node *ni, struct ieee80211_rxinfo *rxi)
21489e9787ffSdamien {
21499e9787ffSdamien const struct ieee80211_frame *wh;
2150361aed72Sdamien const u_int8_t *frm;
21519e9787ffSdamien u_int16_t algo, seq, status;
21529e9787ffSdamien
2153fcdd546dSdamien /* make sure all mandatory fixed fields are present */
2154ec69e05aSdamien if (m->m_len < sizeof(*wh) + 6) {
2155932b9027Sdamien DPRINTF(("frame too short\n"));
2156fcdd546dSdamien return;
2157fcdd546dSdamien }
2158ec69e05aSdamien wh = mtod(m, struct ieee80211_frame *);
2159361aed72Sdamien frm = (const u_int8_t *)&wh[1];
2160361aed72Sdamien
21615252db7eSdamien algo = LE_READ_2(frm); frm += 2;
21625252db7eSdamien seq = LE_READ_2(frm); frm += 2;
21635252db7eSdamien status = LE_READ_2(frm); frm += 2;
2164932b9027Sdamien DPRINTF(("auth %d seq %d from %s\n", algo, seq,
2165932b9027Sdamien ether_sprintf((u_int8_t *)wh->i_addr2)));
216691b2158bSmillert
2167fcdd546dSdamien /* only "open" auth mode is supported */
2168fcdd546dSdamien if (algo != IEEE80211_AUTH_ALG_OPEN) {
2169932b9027Sdamien DPRINTF(("unsupported auth algorithm %d from %s\n",
2170932b9027Sdamien algo, ether_sprintf((u_int8_t *)wh->i_addr2)));
217191b2158bSmillert ic->ic_stats.is_rx_auth_unsupported++;
2172171ac09aSdamien #ifndef IEEE80211_STA_ONLY
21738490403bSmillert if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
21748490403bSmillert /* XXX hack to workaround calling convention */
21758490403bSmillert IEEE80211_SEND_MGMT(ic, ni,
21768490403bSmillert IEEE80211_FC0_SUBTYPE_AUTH,
217701c40890Sstsp IEEE80211_STATUS_ALG << 16 | ((seq + 1) & 0xfff));
21788490403bSmillert }
2179171ac09aSdamien #endif
2180fcdd546dSdamien return;
218191b2158bSmillert }
218230d05aacSdamien ieee80211_auth_open(ic, wh, ni, rxi, seq, status);
218391b2158bSmillert }
218491b2158bSmillert
2185171ac09aSdamien #ifndef IEEE80211_STA_ONLY
21869e9787ffSdamien /*-
21879e9787ffSdamien * (Re)Association request frame format:
21889e9787ffSdamien * [2] Capability information
21899e9787ffSdamien * [2] Listen interval
21909e9787ffSdamien * [6*] Current AP address (Reassociation only)
21919e9787ffSdamien * [tlv] SSID
21929e9787ffSdamien * [tlv] Supported rates
21939e9787ffSdamien * [tlv] Extended Supported Rates (802.11g)
2194fdcb679cSdamien * [tlv] RSN (802.11i)
2195ab235185Sdamien * [tlv] QoS Capability (802.11e)
219645eec175Sdamien * [tlv] HT Capabilities (802.11n)
21979e9787ffSdamien */
21989e9787ffSdamien void
ieee80211_recv_assoc_req(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,struct ieee80211_rxinfo * rxi,int reassoc)2199ec69e05aSdamien ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m,
220030d05aacSdamien struct ieee80211_node *ni, struct ieee80211_rxinfo *rxi, int reassoc)
22019e9787ffSdamien {
22029e9787ffSdamien const struct ieee80211_frame *wh;
22039e9787ffSdamien const u_int8_t *frm, *efrm;
220450e8fe1cSstsp const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *wmeie;
220550e8fe1cSstsp const u_int8_t *htcaps, *vhtcaps;
220691b2158bSmillert u_int16_t capinfo, bintval;
2207e03e709cSdamien int resp, status = 0;
2208e03e709cSdamien struct ieee80211_rsnparams rsn;
220916b96e48Sdamien u_int8_t rate;
221012672b37Sstsp const u_int8_t *saveie = NULL;
221191b2158bSmillert
221291b2158bSmillert if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
22131bb78573Sdamien ic->ic_state != IEEE80211_S_RUN)
221491b2158bSmillert return;
221591b2158bSmillert
2216361aed72Sdamien /* make sure all mandatory fixed fields are present */
2217ec69e05aSdamien if (m->m_len < sizeof(*wh) + (reassoc ? 10 : 4)) {
2218361aed72Sdamien DPRINTF(("frame too short\n"));
2219361aed72Sdamien return;
2220361aed72Sdamien }
2221ec69e05aSdamien wh = mtod(m, struct ieee80211_frame *);
22229e9787ffSdamien frm = (const u_int8_t *)&wh[1];
2223ec69e05aSdamien efrm = mtod(m, u_int8_t *) + m->m_len;
22249e9787ffSdamien
222591b2158bSmillert if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
2226932b9027Sdamien DPRINTF(("ignore other bss from %s\n",
2227932b9027Sdamien ether_sprintf((u_int8_t *)wh->i_addr2)));
222891b2158bSmillert ic->ic_stats.is_rx_assoc_bss++;
222991b2158bSmillert return;
223091b2158bSmillert }
22315252db7eSdamien capinfo = LE_READ_2(frm); frm += 2;
22325252db7eSdamien bintval = LE_READ_2(frm); frm += 2;
223345eec175Sdamien if (reassoc) {
22345252db7eSdamien frm += IEEE80211_ADDR_LEN; /* skip current AP address */
223545eec175Sdamien resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
223645eec175Sdamien } else
223745eec175Sdamien resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
2238fcdd546dSdamien
223950e8fe1cSstsp ssid = rates = xrates = rsnie = wpaie = wmeie = htcaps = vhtcaps = NULL;
2240df33438bSdamien while (frm + 2 <= efrm) {
2241df33438bSdamien if (frm + 2 + frm[1] > efrm) {
2242df33438bSdamien ic->ic_stats.is_rx_elem_toosmall++;
2243b2985342Sdamien break;
2244df33438bSdamien }
2245df33438bSdamien switch (frm[0]) {
224691b2158bSmillert case IEEE80211_ELEMID_SSID:
224791b2158bSmillert ssid = frm;
224891b2158bSmillert break;
224991b2158bSmillert case IEEE80211_ELEMID_RATES:
225091b2158bSmillert rates = frm;
225191b2158bSmillert break;
225291b2158bSmillert case IEEE80211_ELEMID_XRATES:
225391b2158bSmillert xrates = frm;
225491b2158bSmillert break;
2255fdcb679cSdamien case IEEE80211_ELEMID_RSN:
2256e03e709cSdamien rsnie = frm;
2257fdcb679cSdamien break;
2258ab235185Sdamien case IEEE80211_ELEMID_QOS_CAP:
2259ab235185Sdamien break;
226045eec175Sdamien case IEEE80211_ELEMID_HTCAPS:
226145eec175Sdamien htcaps = frm;
226245eec175Sdamien break;
226350e8fe1cSstsp case IEEE80211_ELEMID_VHTCAPS:
226450e8fe1cSstsp vhtcaps = frm;
226550e8fe1cSstsp break;
22664b3a03eeSdamien case IEEE80211_ELEMID_VENDOR:
22674b3a03eeSdamien if (frm[1] < 4) {
22684b3a03eeSdamien ic->ic_stats.is_rx_elem_toosmall++;
22694b3a03eeSdamien break;
22704b3a03eeSdamien }
22714b3a03eeSdamien if (memcmp(frm + 2, MICROSOFT_OUI, 3) == 0) {
22724b3a03eeSdamien if (frm[5] == 1)
2273e03e709cSdamien wpaie = frm;
2274aefc44daSstsp /* WME info IE: len=7 type=2 subtype=0 */
2275aefc44daSstsp if (frm[1] == 7 && frm[5] == 2 && frm[6] == 0)
2276aefc44daSstsp wmeie = frm;
22774b3a03eeSdamien }
22784b3a03eeSdamien break;
227991b2158bSmillert }
2280df33438bSdamien frm += 2 + frm[1];
228191b2158bSmillert }
2282fcdd546dSdamien /* supported rates element is mandatory */
2283fcdd546dSdamien if (rates == NULL || rates[1] > IEEE80211_RATE_MAXSIZE) {
2284932b9027Sdamien DPRINTF(("invalid supported rates element\n"));
2285fcdd546dSdamien return;
2286fcdd546dSdamien }
2287fcdd546dSdamien /* SSID element is mandatory */
2288fcdd546dSdamien if (ssid == NULL || ssid[1] > IEEE80211_NWID_LEN) {
2289932b9027Sdamien DPRINTF(("invalid SSID element\n"));
2290fcdd546dSdamien return;
2291fcdd546dSdamien }
2292e26cab71Sdamien /* check that the specified SSID matches ours */
22932c86ad7bSdamien if (ssid[1] != ic->ic_bss->ni_esslen ||
22942c86ad7bSdamien memcmp(&ssid[2], ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen)) {
2295932b9027Sdamien DPRINTF(("SSID mismatch\n"));
2296fcdd546dSdamien ic->ic_stats.is_rx_ssidmismatch++;
2297fcdd546dSdamien return;
2298fcdd546dSdamien }
2299fcdd546dSdamien
23000fd4e251Sreyk if (ni->ni_state != IEEE80211_STA_AUTH &&
23010fd4e251Sreyk ni->ni_state != IEEE80211_STA_ASSOC) {
2302932b9027Sdamien DPRINTF(("deny %sassoc from %s, not authenticated\n",
2303932b9027Sdamien reassoc ? "re" : "",
2304d813d27eSdamien ether_sprintf((u_int8_t *)wh->i_addr2)));
2305d4dcdec6Sstsp ni = ieee80211_find_node(ic, wh->i_addr2);
2306d4dcdec6Sstsp if (ni == NULL)
230791b2158bSmillert ni = ieee80211_dup_bss(ic, wh->i_addr2);
230891b2158bSmillert if (ni != NULL) {
230991b2158bSmillert IEEE80211_SEND_MGMT(ic, ni,
231091b2158bSmillert IEEE80211_FC0_SUBTYPE_DEAUTH,
231191b2158bSmillert IEEE80211_REASON_ASSOC_NOT_AUTHED);
231291b2158bSmillert }
231391b2158bSmillert ic->ic_stats.is_rx_assoc_notauth++;
231491b2158bSmillert return;
231591b2158bSmillert }
2316e03e709cSdamien
231745eec175Sdamien if (ni->ni_state == IEEE80211_STA_ASSOC &&
231845eec175Sdamien (ni->ni_flags & IEEE80211_NODE_MFP)) {
231945eec175Sdamien if (ni->ni_flags & IEEE80211_NODE_SA_QUERY_FAILED) {
232045eec175Sdamien /* send a protected Disassociate frame */
232145eec175Sdamien IEEE80211_SEND_MGMT(ic, ni,
232245eec175Sdamien IEEE80211_FC0_SUBTYPE_DISASSOC,
232345eec175Sdamien IEEE80211_REASON_AUTH_EXPIRE);
232445eec175Sdamien /* terminate the old SA */
232545eec175Sdamien ieee80211_node_leave(ic, ni);
232645eec175Sdamien } else {
232745eec175Sdamien /* reject the (Re)Association Request temporarily */
232845eec175Sdamien IEEE80211_SEND_MGMT(ic, ni, resp,
232945eec175Sdamien IEEE80211_STATUS_TRY_AGAIN_LATER);
233045eec175Sdamien /* start SA Query procedure if not already engaged */
233145eec175Sdamien if (!(ni->ni_flags & IEEE80211_NODE_SA_QUERY))
233245eec175Sdamien ieee80211_sa_query_request(ic, ni);
233345eec175Sdamien /* do not modify association state */
233445eec175Sdamien }
233545eec175Sdamien return;
233645eec175Sdamien }
233745eec175Sdamien
23389ff893c9Sdamien if (!(capinfo & IEEE80211_CAPINFO_ESS)) {
233991b2158bSmillert ic->ic_stats.is_rx_assoc_capmismatch++;
2340e03e709cSdamien status = IEEE80211_STATUS_CAPINFO;
2341e03e709cSdamien goto end;
234291b2158bSmillert }
234316b96e48Sdamien rate = ieee80211_setup_rates(ic, ni, rates, xrates,
234416b96e48Sdamien IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
234516b96e48Sdamien IEEE80211_F_DODEL);
234616b96e48Sdamien if (rate & IEEE80211_RATE_BASIC) {
234791b2158bSmillert ic->ic_stats.is_rx_assoc_norate++;
2348e03e709cSdamien status = IEEE80211_STATUS_BASIC_RATE;
2349e03e709cSdamien goto end;
235091b2158bSmillert }
2351e03e709cSdamien
2352b2709672Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
2353b2709672Sstsp ni->ni_supported_rsnprotos = IEEE80211_PROTO_NONE;
2354b2709672Sstsp ni->ni_rsnakms = 0;
2355b2709672Sstsp ni->ni_supported_rsnakms = 0;
2356b2709672Sstsp ni->ni_rsnciphers = 0;
2357b2709672Sstsp ni->ni_rsngroupcipher = 0;
2358b2709672Sstsp ni->ni_rsngroupmgmtcipher = 0;
2359b2709672Sstsp ni->ni_rsncaps = 0;
2360b2709672Sstsp
2361e03e709cSdamien /*
2362e03e709cSdamien * A station should never include both a WPA and an RSN IE
2363e03e709cSdamien * in its (Re)Association Requests, but if it does, we only
2364e03e709cSdamien * consider the IE of the highest version of the protocol
2365e03e709cSdamien * that is allowed (ie RSN over WPA).
2366e03e709cSdamien */
236712672b37Sstsp if (rsnie != NULL) {
2368e03e709cSdamien status = ieee80211_parse_rsn(ic, rsnie, &rsn);
2369e03e709cSdamien if (status != 0)
2370e03e709cSdamien goto end;
2371b2709672Sstsp ni->ni_supported_rsnprotos = IEEE80211_PROTO_RSN;
237212672b37Sstsp ni->ni_supported_rsnakms = rsn.rsn_akms;
237312672b37Sstsp if ((ic->ic_flags & IEEE80211_F_RSNON) &&
237412672b37Sstsp (ic->ic_rsnprotos & IEEE80211_PROTO_RSN)) {
237512672b37Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_RSN;
2376e03e709cSdamien saveie = rsnie;
237712672b37Sstsp }
237812672b37Sstsp } else if (wpaie != NULL) {
2379e03e709cSdamien status = ieee80211_parse_wpa(ic, wpaie, &rsn);
2380e03e709cSdamien if (status != 0)
2381e03e709cSdamien goto end;
2382b2709672Sstsp ni->ni_supported_rsnprotos = IEEE80211_PROTO_WPA;
238312672b37Sstsp ni->ni_supported_rsnakms = rsn.rsn_akms;
238412672b37Sstsp if ((ic->ic_flags & IEEE80211_F_RSNON) &&
238512672b37Sstsp (ic->ic_rsnprotos & IEEE80211_PROTO_WPA)) {
238612672b37Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_WPA;
2387e03e709cSdamien saveie = wpaie;
238812672b37Sstsp }
238912672b37Sstsp }
239012672b37Sstsp
2391aefc44daSstsp if (ic->ic_flags & IEEE80211_F_QOS) {
2392aefc44daSstsp if (wmeie != NULL)
2393aefc44daSstsp ni->ni_flags |= IEEE80211_NODE_QOS;
2394aefc44daSstsp else /* for Reassociation */
2395aefc44daSstsp ni->ni_flags &= ~IEEE80211_NODE_QOS;
2396aefc44daSstsp }
2397aefc44daSstsp
239812672b37Sstsp if (ic->ic_flags & IEEE80211_F_RSNON) {
239912672b37Sstsp if (ni->ni_rsnprotos == IEEE80211_PROTO_NONE) {
2400e03e709cSdamien /*
2401e03e709cSdamien * In an RSN, an AP shall not associate with STAs
2402e03e709cSdamien * that fail to include the RSN IE in the
2403e03e709cSdamien * (Re)Association Request.
2404e03e709cSdamien */
2405e03e709cSdamien status = IEEE80211_STATUS_IE_INVALID;
2406e03e709cSdamien goto end;
2407e03e709cSdamien }
2408e03e709cSdamien /*
2409e03e709cSdamien * The initiating STA's RSN IE shall include one authentication
2410e03e709cSdamien * and pairwise cipher suite among those advertised by the
2411e03e709cSdamien * targeted AP. It shall also specify the group cipher suite
2412e03e709cSdamien * specified by the targeted AP.
2413e03e709cSdamien */
2414e03e709cSdamien if (rsn.rsn_nakms != 1 ||
2415e03e709cSdamien !(rsn.rsn_akms & ic->ic_bss->ni_rsnakms)) {
2416e03e709cSdamien status = IEEE80211_STATUS_BAD_AKMP;
241712672b37Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
2418e03e709cSdamien goto end;
2419e03e709cSdamien }
2420e03e709cSdamien if (rsn.rsn_nciphers != 1 ||
2421e03e709cSdamien !(rsn.rsn_ciphers & ic->ic_bss->ni_rsnciphers)) {
2422e03e709cSdamien status = IEEE80211_STATUS_BAD_PAIRWISE_CIPHER;
242312672b37Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
2424e03e709cSdamien goto end;
2425e03e709cSdamien }
2426e03e709cSdamien if (rsn.rsn_groupcipher != ic->ic_bss->ni_rsngroupcipher) {
2427e03e709cSdamien status = IEEE80211_STATUS_BAD_GROUP_CIPHER;
242812672b37Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
2429e03e709cSdamien goto end;
2430e03e709cSdamien }
2431db011a80Sdamien
243245eec175Sdamien if ((ic->ic_bss->ni_rsncaps & IEEE80211_RSNCAP_MFPR) &&
243345eec175Sdamien !(rsn.rsn_caps & IEEE80211_RSNCAP_MFPC)) {
243445eec175Sdamien status = IEEE80211_STATUS_MFP_POLICY;
243512672b37Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
2436db011a80Sdamien goto end;
2437db011a80Sdamien }
2438db011a80Sdamien if ((ic->ic_bss->ni_rsncaps & IEEE80211_RSNCAP_MFPC) &&
2439db011a80Sdamien (rsn.rsn_caps & (IEEE80211_RSNCAP_MFPC |
2440db011a80Sdamien IEEE80211_RSNCAP_MFPR)) == IEEE80211_RSNCAP_MFPR) {
2441db011a80Sdamien /* STA advertises an invalid setting */
244245eec175Sdamien status = IEEE80211_STATUS_MFP_POLICY;
244312672b37Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
2444db011a80Sdamien goto end;
2445db011a80Sdamien }
244645eec175Sdamien /*
244745eec175Sdamien * A STA that has associated with Management Frame Protection
244845eec175Sdamien * enabled shall not use cipher suite pairwise selector WEP40,
244945eec175Sdamien * WEP104, TKIP, or "Use Group cipher suite".
245045eec175Sdamien */
2451db011a80Sdamien if ((rsn.rsn_caps & IEEE80211_RSNCAP_MFPC) &&
245245eec175Sdamien (rsn.rsn_ciphers != IEEE80211_CIPHER_CCMP ||
2453db011a80Sdamien rsn.rsn_groupmgmtcipher !=
245445eec175Sdamien ic->ic_bss->ni_rsngroupmgmtcipher)) {
245545eec175Sdamien status = IEEE80211_STATUS_MFP_POLICY;
245612672b37Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
2457db011a80Sdamien goto end;
2458db011a80Sdamien }
2459db011a80Sdamien
2460e03e709cSdamien /*
2461e03e709cSdamien * Disallow new associations using TKIP if countermeasures
2462e03e709cSdamien * are active.
2463e03e709cSdamien */
2464e03e709cSdamien if ((ic->ic_flags & IEEE80211_F_COUNTERM) &&
2465e03e709cSdamien (rsn.rsn_ciphers == IEEE80211_CIPHER_TKIP ||
2466e03e709cSdamien rsn.rsn_groupcipher == IEEE80211_CIPHER_TKIP)) {
2467e03e709cSdamien status = IEEE80211_STATUS_CIPHER_REJ_POLICY;
246812672b37Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
2469e03e709cSdamien goto end;
2470e03e709cSdamien }
2471e03e709cSdamien
2472e03e709cSdamien /* everything looks fine, save IE and parameters */
247312672b37Sstsp if (saveie == NULL ||
247412672b37Sstsp ieee80211_save_ie(saveie, &ni->ni_rsnie) != 0) {
2475e03e709cSdamien status = IEEE80211_STATUS_TOOMANY;
247612672b37Sstsp ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
2477e03e709cSdamien goto end;
2478e03e709cSdamien }
2479e03e709cSdamien ni->ni_rsnakms = rsn.rsn_akms;
2480e03e709cSdamien ni->ni_rsnciphers = rsn.rsn_ciphers;
2481e03e709cSdamien ni->ni_rsngroupcipher = ic->ic_bss->ni_rsngroupcipher;
24827afadc6eSdamien ni->ni_rsngroupmgmtcipher = ic->ic_bss->ni_rsngroupmgmtcipher;
2483e03e709cSdamien ni->ni_rsncaps = rsn.rsn_caps;
2484f91ff320Sdamien
2485f91ff320Sdamien if (ieee80211_is_8021x_akm(ni->ni_rsnakms)) {
2486f91ff320Sdamien struct ieee80211_pmk *pmk = NULL;
2487f91ff320Sdamien const u_int8_t *pmkid = rsn.rsn_pmkids;
2488f91ff320Sdamien /*
2489f91ff320Sdamien * Check if we have a cached PMK entry matching one
2490f91ff320Sdamien * of the PMKIDs specified in the RSN IE.
2491f91ff320Sdamien */
2492f91ff320Sdamien while (rsn.rsn_npmkids-- > 0) {
2493f91ff320Sdamien pmk = ieee80211_pmksa_find(ic, ni, pmkid);
2494f91ff320Sdamien if (pmk != NULL)
2495f91ff320Sdamien break;
2496f91ff320Sdamien pmkid += IEEE80211_PMKID_LEN;
2497f91ff320Sdamien }
2498f91ff320Sdamien if (pmk != NULL) {
2499f91ff320Sdamien memcpy(ni->ni_pmk, pmk->pmk_key,
2500f91ff320Sdamien IEEE80211_PMK_LEN);
2501f91ff320Sdamien memcpy(ni->ni_pmkid, pmk->pmk_pmkid,
2502f91ff320Sdamien IEEE80211_PMKID_LEN);
2503f91ff320Sdamien ni->ni_flags |= IEEE80211_NODE_PMK;
2504f91ff320Sdamien }
2505f91ff320Sdamien }
2506b2709672Sstsp }
2507e03e709cSdamien
250830d05aacSdamien ni->ni_rssi = rxi->rxi_rssi;
250930d05aacSdamien ni->ni_rstamp = rxi->rxi_tstamp;
251091b2158bSmillert ni->ni_intval = bintval;
251191b2158bSmillert ni->ni_capinfo = capinfo;
251291b2158bSmillert ni->ni_chan = ic->ic_bss->ni_chan;
2513fe8f5243Sstsp if (htcaps)
2514fe8f5243Sstsp ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
25152040b0e1Sstsp else
25162040b0e1Sstsp ieee80211_clear_htcaps(ni);
251750e8fe1cSstsp if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
251850e8fe1cSstsp ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
251950e8fe1cSstsp else
252050e8fe1cSstsp ieee80211_clear_vhtcaps(ni);
2521e03e709cSdamien end:
2522962c982dSdamien if (status != 0) {
2523e03e709cSdamien IEEE80211_SEND_MGMT(ic, ni, resp, status);
2524e03e709cSdamien ieee80211_node_leave(ic, ni);
2525e03e709cSdamien } else
25260fd4e251Sreyk ieee80211_node_join(ic, ni, resp);
252791b2158bSmillert }
2528171ac09aSdamien #endif /* IEEE80211_STA_ONLY */
252991b2158bSmillert
25309e9787ffSdamien /*-
25319e9787ffSdamien * (Re)Association response frame format:
25329e9787ffSdamien * [2] Capability information
25339e9787ffSdamien * [2] Status code
25349e9787ffSdamien * [2] Association ID (AID)
25359e9787ffSdamien * [tlv] Supported rates
25369e9787ffSdamien * [tlv] Extended Supported Rates (802.11g)
2537ab235185Sdamien * [tlv] EDCA Parameter Set (802.11e)
253845eec175Sdamien * [tlv] HT Capabilities (802.11n)
253945eec175Sdamien * [tlv] HT Operation (802.11n)
25409e9787ffSdamien */
25419e9787ffSdamien void
ieee80211_recv_assoc_resp(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,int reassoc)2542ec69e05aSdamien ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m,
2543e03e709cSdamien struct ieee80211_node *ni, int reassoc)
25449e9787ffSdamien {
25459e9787ffSdamien struct ifnet *ifp = &ic->ic_if;
25469e9787ffSdamien const struct ieee80211_frame *wh;
25479e9787ffSdamien const u_int8_t *frm, *efrm;
254845eec175Sdamien const u_int8_t *rates, *xrates, *edcaie, *wmmie, *htcaps, *htop;
254950e8fe1cSstsp const u_int8_t *vhtcaps, *vhtop;
2550c890af98Sdamien u_int16_t capinfo, status, associd;
255116b96e48Sdamien u_int8_t rate;
255291b2158bSmillert
255391b2158bSmillert if (ic->ic_opmode != IEEE80211_M_STA ||
25540fd4e251Sreyk ic->ic_state != IEEE80211_S_ASSOC) {
25550fd4e251Sreyk ic->ic_stats.is_rx_mgtdiscard++;
255691b2158bSmillert return;
25570fd4e251Sreyk }
255891b2158bSmillert
2559361aed72Sdamien /* make sure all mandatory fixed fields are present */
2560ec69e05aSdamien if (m->m_len < sizeof(*wh) + 6) {
2561962c982dSdamien DPRINTF(("frame too short\n"));
2562361aed72Sdamien return;
2563361aed72Sdamien }
2564ec69e05aSdamien wh = mtod(m, struct ieee80211_frame *);
25659e9787ffSdamien frm = (const u_int8_t *)&wh[1];
2566ec69e05aSdamien efrm = mtod(m, u_int8_t *) + m->m_len;
25679e9787ffSdamien
2568c890af98Sdamien capinfo = LE_READ_2(frm); frm += 2;
2569c890af98Sdamien status = LE_READ_2(frm); frm += 2;
2570fcdd546dSdamien if (status != IEEE80211_STATUS_SUCCESS) {
257191b2158bSmillert if (ifp->if_flags & IFF_DEBUG)
2572a0981291Sdamien printf("%s: %sassociation failed (status %d)"
2573a0068c42Sjsg " for %s\n", ifp->if_xname,
2574e03e709cSdamien reassoc ? "re" : "",
25759e9787ffSdamien status, ether_sprintf((u_int8_t *)wh->i_addr3));
257691b2158bSmillert if (ni != ic->ic_bss)
257791b2158bSmillert ni->ni_fails++;
257891b2158bSmillert ic->ic_stats.is_rx_auth_fail++;
257991b2158bSmillert return;
258091b2158bSmillert }
2581c890af98Sdamien associd = LE_READ_2(frm); frm += 2;
258291b2158bSmillert
258345eec175Sdamien rates = xrates = edcaie = wmmie = htcaps = htop = NULL;
258450e8fe1cSstsp vhtcaps = vhtop = NULL;
2585df33438bSdamien while (frm + 2 <= efrm) {
2586df33438bSdamien if (frm + 2 + frm[1] > efrm) {
2587df33438bSdamien ic->ic_stats.is_rx_elem_toosmall++;
2588b2985342Sdamien break;
2589df33438bSdamien }
2590df33438bSdamien switch (frm[0]) {
259191b2158bSmillert case IEEE80211_ELEMID_RATES:
259291b2158bSmillert rates = frm;
259391b2158bSmillert break;
259491b2158bSmillert case IEEE80211_ELEMID_XRATES:
259591b2158bSmillert xrates = frm;
259691b2158bSmillert break;
2597ab235185Sdamien case IEEE80211_ELEMID_EDCAPARMS:
2598e03e709cSdamien edcaie = frm;
2599ab235185Sdamien break;
260045eec175Sdamien case IEEE80211_ELEMID_HTCAPS:
260145eec175Sdamien htcaps = frm;
260245eec175Sdamien break;
260345eec175Sdamien case IEEE80211_ELEMID_HTOP:
260445eec175Sdamien htop = frm;
260545eec175Sdamien break;
260650e8fe1cSstsp case IEEE80211_ELEMID_VHTCAPS:
260750e8fe1cSstsp vhtcaps = frm;
260850e8fe1cSstsp break;
260950e8fe1cSstsp case IEEE80211_ELEMID_VHTOP:
261050e8fe1cSstsp vhtop = frm;
261150e8fe1cSstsp break;
2612ba56bee9Sdamien case IEEE80211_ELEMID_VENDOR:
2613ba56bee9Sdamien if (frm[1] < 4) {
2614ba56bee9Sdamien ic->ic_stats.is_rx_elem_toosmall++;
2615ba56bee9Sdamien break;
2616ba56bee9Sdamien }
26177c7d9786Sdamien if (memcmp(frm + 2, MICROSOFT_OUI, 3) == 0) {
26184b3a03eeSdamien if (frm[1] >= 5 && frm[5] == 2 && frm[6] == 1)
2619e03e709cSdamien wmmie = frm;
2620ba56bee9Sdamien }
2621ba56bee9Sdamien break;
262291b2158bSmillert }
2623df33438bSdamien frm += 2 + frm[1];
262491b2158bSmillert }
2625fcdd546dSdamien /* supported rates element is mandatory */
2626fcdd546dSdamien if (rates == NULL || rates[1] > IEEE80211_RATE_MAXSIZE) {
2627932b9027Sdamien DPRINTF(("invalid supported rates element\n"));
2628fcdd546dSdamien return;
2629fcdd546dSdamien }
263016b96e48Sdamien rate = ieee80211_setup_rates(ic, ni, rates, xrates,
263116b96e48Sdamien IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
263216b96e48Sdamien IEEE80211_F_DODEL);
263316b96e48Sdamien if (rate & IEEE80211_RATE_BASIC) {
2634932b9027Sdamien DPRINTF(("rate mismatch for %s\n",
2635932b9027Sdamien ether_sprintf((u_int8_t *)wh->i_addr2)));
263616b96e48Sdamien ic->ic_stats.is_rx_assoc_norate++;
26379e9787ffSdamien return;
263816b96e48Sdamien }
2639c890af98Sdamien ni->ni_capinfo = capinfo;
2640c890af98Sdamien ni->ni_associd = associd;
2641e03e709cSdamien if (edcaie != NULL || wmmie != NULL) {
2642ab235185Sdamien /* force update of EDCA parameters */
2643ab235185Sdamien ic->ic_edca_updtcount = -1;
2644ab235185Sdamien
2645e03e709cSdamien if ((edcaie != NULL &&
2646e03e709cSdamien ieee80211_parse_edca_params(ic, edcaie) == 0) ||
2647e03e709cSdamien (wmmie != NULL &&
2648e03e709cSdamien ieee80211_parse_wmm_params(ic, wmmie) == 0))
2649ab235185Sdamien ni->ni_flags |= IEEE80211_NODE_QOS;
2650ab235185Sdamien else /* for Reassociation */
2651ab235185Sdamien ni->ni_flags &= ~IEEE80211_NODE_QOS;
2652ab235185Sdamien }
2653e52a7d1aSstsp if (htcaps)
2654e52a7d1aSstsp ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
2655e52a7d1aSstsp if (htop)
2656f9214ef6Sstsp ieee80211_setup_htop(ni, htop + 2, htop[1], 0);
2657e52a7d1aSstsp ieee80211_ht_negotiate(ic, ni);
2658e52a7d1aSstsp
26597307575aSstsp if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) {
266050e8fe1cSstsp ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
266150e8fe1cSstsp if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
266250e8fe1cSstsp vhtop = NULL; /* invalid VHTOP */
266350e8fe1cSstsp }
266450e8fe1cSstsp ieee80211_vht_negotiate(ic, ni);
266550e8fe1cSstsp
266650e8fe1cSstsp /* Hop into 11n/11ac modes after associating to a HT/VHT AP. */
266750e8fe1cSstsp if (ni->ni_flags & IEEE80211_NODE_VHT)
266850e8fe1cSstsp ieee80211_setmode(ic, IEEE80211_MODE_11AC);
266950e8fe1cSstsp else if (ni->ni_flags & IEEE80211_NODE_HT)
2670e52a7d1aSstsp ieee80211_setmode(ic, IEEE80211_MODE_11N);
2671e52a7d1aSstsp else
26727b907937Sstsp ieee80211_setmode(ic, ieee80211_chan2mode(ic, ni->ni_chan));
26737b907937Sstsp /*
26747b907937Sstsp * Reset the erp state (mostly the slot time) now that
26757b907937Sstsp * our operating mode has been nailed down.
26767b907937Sstsp */
26777b907937Sstsp ieee80211_reset_erp(ic);
26787b907937Sstsp
26791bb78573Sdamien /*
26801bb78573Sdamien * Configure state now that we are associated.
26811bb78573Sdamien */
26821bb78573Sdamien if (ic->ic_curmode == IEEE80211_MODE_11A ||
26831bb78573Sdamien (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE))
26841bb78573Sdamien ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
26851bb78573Sdamien else
26861bb78573Sdamien ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
26871bb78573Sdamien
26881bb78573Sdamien ieee80211_set_shortslottime(ic,
26891bb78573Sdamien ic->ic_curmode == IEEE80211_MODE_11A ||
26901bb78573Sdamien (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
26911bb78573Sdamien /*
26921bb78573Sdamien * Honor ERP protection.
26931bb78573Sdamien */
26940126ded1Sstsp if ((ic->ic_curmode == IEEE80211_MODE_11G ||
26950126ded1Sstsp (ic->ic_curmode == IEEE80211_MODE_11N &&
26960126ded1Sstsp IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))) &&
26971bb78573Sdamien (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
26981bb78573Sdamien ic->ic_flags |= IEEE80211_F_USEPROT;
26991bb78573Sdamien else
27001bb78573Sdamien ic->ic_flags &= ~IEEE80211_F_USEPROT;
2701e03e709cSdamien /*
2702e03e709cSdamien * If not an RSNA, mark the port as valid, otherwise wait for
2703e03e709cSdamien * 802.1X authentication and 4-way handshake to complete..
2704e03e709cSdamien */
2705e03e709cSdamien if (ic->ic_flags & IEEE80211_F_RSNON) {
2706e03e709cSdamien /* XXX ic->ic_mgt_timer = 5; */
270798998980Sstsp ni->ni_rsn_supp_state = RSNA_SUPP_PTKSTART;
27088fc1098aSdamien } else if (ic->ic_flags & IEEE80211_F_WEPON)
27098fc1098aSdamien ni->ni_flags |= IEEE80211_NODE_TXRXPROT;
27108fc1098aSdamien
271191b2158bSmillert ieee80211_new_state(ic, IEEE80211_S_RUN,
2712e03e709cSdamien IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
271391b2158bSmillert }
271491b2158bSmillert
27159e9787ffSdamien /*-
27169e9787ffSdamien * Deauthentication frame format:
27179e9787ffSdamien * [2] Reason code
271891b2158bSmillert */
27199e9787ffSdamien void
ieee80211_recv_deauth(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)2720ec69e05aSdamien ieee80211_recv_deauth(struct ieee80211com *ic, struct mbuf *m,
2721e03e709cSdamien struct ieee80211_node *ni)
27229e9787ffSdamien {
27239e9787ffSdamien const struct ieee80211_frame *wh;
2724361aed72Sdamien const u_int8_t *frm;
27259e9787ffSdamien u_int16_t reason;
27269e9787ffSdamien
2727fcdd546dSdamien /* make sure all mandatory fixed fields are present */
2728ec69e05aSdamien if (m->m_len < sizeof(*wh) + 2) {
2729932b9027Sdamien DPRINTF(("frame too short\n"));
2730fcdd546dSdamien return;
2731fcdd546dSdamien }
2732ec69e05aSdamien wh = mtod(m, struct ieee80211_frame *);
2733361aed72Sdamien frm = (const u_int8_t *)&wh[1];
2734361aed72Sdamien
27355252db7eSdamien reason = LE_READ_2(frm);
2736fcdd546dSdamien
273791b2158bSmillert ic->ic_stats.is_rx_deauth++;
273891b2158bSmillert switch (ic->ic_opmode) {
273906e069b5Sstsp case IEEE80211_M_STA: {
274006e069b5Sstsp int bgscan = ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
274106e069b5Sstsp ic->ic_state == IEEE80211_S_RUN);
2742534bf8f4Sstsp int stay_auth = ((ic->ic_userflags & IEEE80211_F_STAYAUTH) &&
2743534bf8f4Sstsp ic->ic_state >= IEEE80211_S_AUTH);
2744534bf8f4Sstsp if (!(bgscan || stay_auth))
274591b2158bSmillert ieee80211_new_state(ic, IEEE80211_S_AUTH,
2746e03e709cSdamien IEEE80211_FC0_SUBTYPE_DEAUTH);
274706e069b5Sstsp }
274891b2158bSmillert break;
2749171ac09aSdamien #ifndef IEEE80211_STA_ONLY
275091b2158bSmillert case IEEE80211_M_HOSTAP:
275191b2158bSmillert if (ni != ic->ic_bss) {
2752534bf8f4Sstsp int stay_auth =
2753534bf8f4Sstsp ((ic->ic_userflags & IEEE80211_F_STAYAUTH) &&
2754534bf8f4Sstsp (ni->ni_state == IEEE80211_STA_AUTH ||
2755534bf8f4Sstsp ni->ni_state == IEEE80211_STA_ASSOC));
2756171ac09aSdamien if (ic->ic_if.if_flags & IFF_DEBUG)
2757a0068c42Sjsg printf("%s: station %s deauthenticated "
275891b2158bSmillert "by peer (reason %d)\n",
2759171ac09aSdamien ic->ic_if.if_xname,
27600fd4e251Sreyk ether_sprintf(ni->ni_macaddr),
27610fd4e251Sreyk reason);
2762534bf8f4Sstsp if (!stay_auth)
27630fd4e251Sreyk ieee80211_node_leave(ic, ni);
276491b2158bSmillert }
276591b2158bSmillert break;
2766171ac09aSdamien #endif
276791b2158bSmillert default:
276891b2158bSmillert break;
276991b2158bSmillert }
277091b2158bSmillert }
277191b2158bSmillert
27729e9787ffSdamien /*-
27739e9787ffSdamien * Disassociation frame format:
27749e9787ffSdamien * [2] Reason code
277591b2158bSmillert */
27769e9787ffSdamien void
ieee80211_recv_disassoc(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)2777ec69e05aSdamien ieee80211_recv_disassoc(struct ieee80211com *ic, struct mbuf *m,
2778e03e709cSdamien struct ieee80211_node *ni)
27799e9787ffSdamien {
27809e9787ffSdamien const struct ieee80211_frame *wh;
2781361aed72Sdamien const u_int8_t *frm;
27829e9787ffSdamien u_int16_t reason;
27839e9787ffSdamien
2784fcdd546dSdamien /* make sure all mandatory fixed fields are present */
2785ec69e05aSdamien if (m->m_len < sizeof(*wh) + 2) {
2786932b9027Sdamien DPRINTF(("frame too short\n"));
2787fcdd546dSdamien return;
2788fcdd546dSdamien }
2789ec69e05aSdamien wh = mtod(m, struct ieee80211_frame *);
2790361aed72Sdamien frm = (const u_int8_t *)&wh[1];
2791361aed72Sdamien
27925252db7eSdamien reason = LE_READ_2(frm);
2793fcdd546dSdamien
279491b2158bSmillert ic->ic_stats.is_rx_disassoc++;
279591b2158bSmillert switch (ic->ic_opmode) {
279606e069b5Sstsp case IEEE80211_M_STA: {
279706e069b5Sstsp int bgscan = ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
279806e069b5Sstsp ic->ic_state == IEEE80211_S_RUN);
279906e069b5Sstsp if (!bgscan) /* ignore disassoc during bgscan */
280091b2158bSmillert ieee80211_new_state(ic, IEEE80211_S_ASSOC,
2801e03e709cSdamien IEEE80211_FC0_SUBTYPE_DISASSOC);
280206e069b5Sstsp }
280391b2158bSmillert break;
2804171ac09aSdamien #ifndef IEEE80211_STA_ONLY
280591b2158bSmillert case IEEE80211_M_HOSTAP:
280691b2158bSmillert if (ni != ic->ic_bss) {
2807171ac09aSdamien if (ic->ic_if.if_flags & IFF_DEBUG)
2808a0068c42Sjsg printf("%s: station %s disassociated "
280991b2158bSmillert "by peer (reason %d)\n",
2810171ac09aSdamien ic->ic_if.if_xname,
28110fd4e251Sreyk ether_sprintf(ni->ni_macaddr),
28120fd4e251Sreyk reason);
28130fd4e251Sreyk ieee80211_node_leave(ic, ni);
281491b2158bSmillert }
281591b2158bSmillert break;
2816171ac09aSdamien #endif
281791b2158bSmillert default:
281891b2158bSmillert break;
281991b2158bSmillert }
282091b2158bSmillert }
28219e9787ffSdamien
282245eec175Sdamien /*-
282345eec175Sdamien * ADDBA Request frame format:
282445eec175Sdamien * [1] Category
282545eec175Sdamien * [1] Action
282645eec175Sdamien * [1] Dialog Token
282745eec175Sdamien * [2] Block Ack Parameter Set
282845eec175Sdamien * [2] Block Ack Timeout Value
282945eec175Sdamien * [2] Block Ack Starting Sequence Control
283045eec175Sdamien */
283145eec175Sdamien void
ieee80211_recv_addba_req(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)283245eec175Sdamien ieee80211_recv_addba_req(struct ieee80211com *ic, struct mbuf *m,
283345eec175Sdamien struct ieee80211_node *ni)
283445eec175Sdamien {
283545eec175Sdamien const struct ieee80211_frame *wh;
283645eec175Sdamien const u_int8_t *frm;
2837ec69e05aSdamien struct ieee80211_rx_ba *ba;
283871c11c14Sstsp u_int16_t params, ssn, bufsz, timeout;
283945eec175Sdamien u_int8_t token, tid;
2840935facd7Sstsp int err = 0;
284145eec175Sdamien
28424c1980a1Sstsp /* Ignore if we are not ready to receive data frames. */
28434c1980a1Sstsp if (ic->ic_state != IEEE80211_S_RUN ||
28444c1980a1Sstsp ((ic->ic_flags & IEEE80211_F_RSNON) && !ni->ni_port_valid))
28454c1980a1Sstsp return;
28464c1980a1Sstsp
284745eec175Sdamien if (!(ni->ni_flags & IEEE80211_NODE_HT)) {
284845eec175Sdamien DPRINTF(("received ADDBA req from non-HT STA %s\n",
284945eec175Sdamien ether_sprintf(ni->ni_macaddr)));
285045eec175Sdamien return;
285145eec175Sdamien }
285245eec175Sdamien if (m->m_len < sizeof(*wh) + 9) {
285345eec175Sdamien DPRINTF(("frame too short\n"));
285445eec175Sdamien return;
285545eec175Sdamien }
285645eec175Sdamien /* MLME-ADDBA.indication */
285745eec175Sdamien wh = mtod(m, struct ieee80211_frame *);
285845eec175Sdamien frm = (const u_int8_t *)&wh[1];
285945eec175Sdamien
286045eec175Sdamien token = frm[2];
286145eec175Sdamien params = LE_READ_2(&frm[3]);
2862e8518fa6Sstsp tid = ((params & IEEE80211_ADDBA_TID_MASK) >>
2863e8518fa6Sstsp IEEE80211_ADDBA_TID_SHIFT);
2864e8518fa6Sstsp bufsz = (params & IEEE80211_ADDBA_BUFSZ_MASK) >>
2865e8518fa6Sstsp IEEE80211_ADDBA_BUFSZ_SHIFT;
286645eec175Sdamien timeout = LE_READ_2(&frm[5]);
286745eec175Sdamien ssn = LE_READ_2(&frm[7]) >> 4;
286845eec175Sdamien
2869ec69e05aSdamien ba = &ni->ni_rx_ba[tid];
287019817d19Stobhe /* The driver is still processing an ADDBA request for this tid. */
287119817d19Stobhe if (ba->ba_state == IEEE80211_BA_REQUESTED)
287219817d19Stobhe return;
287366df67e6Sstsp /* If we are in the process of roaming between APs, ignore. */
287466df67e6Sstsp if ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
287566df67e6Sstsp (ic->ic_xflags & IEEE80211_F_TX_MGMT_ONLY))
287666df67e6Sstsp return;
2877ec69e05aSdamien /* check if we already have a Block Ack agreement for this RA/TID */
287845eec175Sdamien if (ba->ba_state == IEEE80211_BA_AGREED) {
287945eec175Sdamien /* XXX should we update the timeout value? */
2880ec69e05aSdamien /* reset Block Ack inactivity timer */
28815c7c7275Sstsp if (ba->ba_timeout_val != 0)
288245eec175Sdamien timeout_add_usec(&ba->ba_to, ba->ba_timeout_val);
288345eec175Sdamien
288445eec175Sdamien /* check if it's a Protected Block Ack agreement */
288545eec175Sdamien if (!(ni->ni_flags & IEEE80211_NODE_MFP) ||
288645eec175Sdamien !(ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC))
288745eec175Sdamien return; /* not a PBAC, ignore */
288845eec175Sdamien
2889ec69e05aSdamien /* PBAC: treat the ADDBA Request like a BlockAckReq */
28908fbaf8a2Sstsp if (SEQ_LT(ba->ba_winstart, ssn)) {
28918fbaf8a2Sstsp struct mbuf_list ml = MBUF_LIST_INITIALIZER();
28928fbaf8a2Sstsp ieee80211_ba_move_window(ic, ni, tid, ssn, &ml);
28938fbaf8a2Sstsp if_input(&ic->ic_if, &ml);
28948fbaf8a2Sstsp }
289545eec175Sdamien return;
289645eec175Sdamien }
289771c11c14Sstsp
289845eec175Sdamien /* if PBAC required but RA does not support it, refuse request */
289945eec175Sdamien if ((ic->ic_flags & IEEE80211_F_PBAR) &&
290045eec175Sdamien (!(ni->ni_flags & IEEE80211_NODE_MFP) ||
290171c11c14Sstsp !(ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC)))
290271c11c14Sstsp goto refuse;
290345eec175Sdamien /*
290445eec175Sdamien * If the TID for which the Block Ack agreement is requested is
290545eec175Sdamien * configured with a no-ACK policy, refuse the agreement.
290645eec175Sdamien */
290771c11c14Sstsp if (ic->ic_tid_noack & (1 << tid))
290871c11c14Sstsp goto refuse;
290971c11c14Sstsp
291045eec175Sdamien /* check that we support the requested Block Ack Policy */
291145eec175Sdamien if (!(ic->ic_htcaps & IEEE80211_HTCAP_DELAYEDBA) &&
291271c11c14Sstsp !(params & IEEE80211_ADDBA_BA_POLICY))
291371c11c14Sstsp goto refuse;
291445eec175Sdamien
291545eec175Sdamien /* setup Block Ack agreement */
291619817d19Stobhe ba->ba_state = IEEE80211_BA_REQUESTED;
291745eec175Sdamien ba->ba_timeout_val = timeout * IEEE80211_DUR_TU;
2918633cd82cSstsp ba->ba_ni = ni;
291971c11c14Sstsp ba->ba_token = token;
2920ec69e05aSdamien timeout_set(&ba->ba_to, ieee80211_rx_ba_timeout, ba);
292120fce2ddSstsp timeout_set(&ba->ba_gap_to, ieee80211_input_ba_gap_timeout, ba);
292220599cf9Sstsp ba->ba_gapwait = 0;
292345eec175Sdamien ba->ba_winsize = bufsz;
292445eec175Sdamien if (ba->ba_winsize == 0 || ba->ba_winsize > IEEE80211_BA_MAX_WINSZ)
292545eec175Sdamien ba->ba_winsize = IEEE80211_BA_MAX_WINSZ;
29263367136cSstsp ba->ba_params = (params & IEEE80211_ADDBA_BA_POLICY);
29273367136cSstsp ba->ba_params |= ((ba->ba_winsize << IEEE80211_ADDBA_BUFSZ_SHIFT) |
2928aefc44daSstsp (tid << IEEE80211_ADDBA_TID_SHIFT));
2929aefc44daSstsp ba->ba_params |= IEEE80211_ADDBA_AMSDU;
293045eec175Sdamien ba->ba_winstart = ssn;
293145eec175Sdamien ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff;
293245eec175Sdamien /* allocate and setup our reordering buffer */
2933265a1ec5Sdhill ba->ba_buf = malloc(IEEE80211_BA_MAX_WINSZ * sizeof(*ba->ba_buf),
293445eec175Sdamien M_DEVBUF, M_NOWAIT | M_ZERO);
293571c11c14Sstsp if (ba->ba_buf == NULL)
293671c11c14Sstsp goto refuse;
293771c11c14Sstsp
293845eec175Sdamien ba->ba_head = 0;
293945eec175Sdamien
294045eec175Sdamien /* notify drivers of this new Block Ack agreement */
2941935facd7Sstsp if (ic->ic_ampdu_rx_start != NULL)
294271c11c14Sstsp err = ic->ic_ampdu_rx_start(ic, ni, tid);
294371c11c14Sstsp if (err == EBUSY) {
294471c11c14Sstsp /* driver will accept or refuse agreement when done */
294571c11c14Sstsp return;
294671c11c14Sstsp } else if (err) {
294745eec175Sdamien /* driver failed to setup, rollback */
294871c11c14Sstsp ieee80211_addba_req_refuse(ic, ni, tid);
294971c11c14Sstsp } else
295071c11c14Sstsp ieee80211_addba_req_accept(ic, ni, tid);
295171c11c14Sstsp return;
295271c11c14Sstsp
295371c11c14Sstsp refuse:
295471c11c14Sstsp /* MLME-ADDBA.response */
295571c11c14Sstsp IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA,
295671c11c14Sstsp IEEE80211_ACTION_ADDBA_RESP,
295771c11c14Sstsp IEEE80211_STATUS_REFUSED << 16 | token << 8 | tid);
295845eec175Sdamien }
295971c11c14Sstsp
296071c11c14Sstsp void
ieee80211_addba_req_accept(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid)296171c11c14Sstsp ieee80211_addba_req_accept(struct ieee80211com *ic, struct ieee80211_node *ni,
296271c11c14Sstsp uint8_t tid)
296371c11c14Sstsp {
296471c11c14Sstsp struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
296571c11c14Sstsp
296645eec175Sdamien ba->ba_state = IEEE80211_BA_AGREED;
296744fdf27cSstsp ic->ic_stats.is_ht_rx_ba_agreements++;
296845eec175Sdamien /* start Block Ack inactivity timer */
29695c7c7275Sstsp if (ba->ba_timeout_val != 0)
297045eec175Sdamien timeout_add_usec(&ba->ba_to, ba->ba_timeout_val);
297171c11c14Sstsp
297245eec175Sdamien /* MLME-ADDBA.response */
297345eec175Sdamien IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA,
297471c11c14Sstsp IEEE80211_ACTION_ADDBA_RESP,
297571c11c14Sstsp IEEE80211_STATUS_SUCCESS << 16 | ba->ba_token << 8 | tid);
297671c11c14Sstsp }
297771c11c14Sstsp
297871c11c14Sstsp void
ieee80211_addba_req_refuse(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid)297971c11c14Sstsp ieee80211_addba_req_refuse(struct ieee80211com *ic, struct ieee80211_node *ni,
298071c11c14Sstsp uint8_t tid)
298171c11c14Sstsp {
298271c11c14Sstsp struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
298371c11c14Sstsp
29844adcc1c9Stb free(ba->ba_buf, M_DEVBUF,
29854adcc1c9Stb IEEE80211_BA_MAX_WINSZ * sizeof(*ba->ba_buf));
298671c11c14Sstsp ba->ba_buf = NULL;
298719817d19Stobhe ba->ba_state = IEEE80211_BA_INIT;
298871c11c14Sstsp
298971c11c14Sstsp /* MLME-ADDBA.response */
299071c11c14Sstsp IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA,
299171c11c14Sstsp IEEE80211_ACTION_ADDBA_RESP,
299271c11c14Sstsp IEEE80211_STATUS_REFUSED << 16 | ba->ba_token << 8 | tid);
299345eec175Sdamien }
299445eec175Sdamien
299545eec175Sdamien /*-
299645eec175Sdamien * ADDBA Response frame format:
299745eec175Sdamien * [1] Category
299845eec175Sdamien * [1] Action
299945eec175Sdamien * [1] Dialog Token
300045eec175Sdamien * [2] Status Code
300145eec175Sdamien * [2] Block Ack Parameter Set
300245eec175Sdamien * [2] Block Ack Timeout Value
300345eec175Sdamien */
300445eec175Sdamien void
ieee80211_recv_addba_resp(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)300545eec175Sdamien ieee80211_recv_addba_resp(struct ieee80211com *ic, struct mbuf *m,
300645eec175Sdamien struct ieee80211_node *ni)
300745eec175Sdamien {
300845eec175Sdamien const struct ieee80211_frame *wh;
300945eec175Sdamien const u_int8_t *frm;
3010ec69e05aSdamien struct ieee80211_tx_ba *ba;
301145eec175Sdamien u_int16_t status, params, bufsz, timeout;
301245eec175Sdamien u_int8_t token, tid;
3013aefc44daSstsp int err = 0;
301445eec175Sdamien
301545eec175Sdamien if (m->m_len < sizeof(*wh) + 9) {
301645eec175Sdamien DPRINTF(("frame too short\n"));
301745eec175Sdamien return;
301845eec175Sdamien }
301945eec175Sdamien wh = mtod(m, struct ieee80211_frame *);
302045eec175Sdamien frm = (const u_int8_t *)&wh[1];
302145eec175Sdamien
302245eec175Sdamien token = frm[2];
302345eec175Sdamien status = LE_READ_2(&frm[3]);
302445eec175Sdamien params = LE_READ_2(&frm[5]);
302545eec175Sdamien tid = (params >> 2) & 0xf;
302645eec175Sdamien bufsz = (params >> 6) & 0x3ff;
302745eec175Sdamien timeout = LE_READ_2(&frm[7]);
302845eec175Sdamien
302945eec175Sdamien DPRINTF(("received ADDBA resp from %s, TID %d, status %d\n",
303045eec175Sdamien ether_sprintf(ni->ni_macaddr), tid, status));
303145eec175Sdamien
303245eec175Sdamien /*
303345eec175Sdamien * Ignore if no ADDBA request has been sent for this RA/TID or
303445eec175Sdamien * if we already have a Block Ack agreement.
303545eec175Sdamien */
3036ec69e05aSdamien ba = &ni->ni_tx_ba[tid];
303745eec175Sdamien if (ba->ba_state != IEEE80211_BA_REQUESTED) {
303845eec175Sdamien DPRINTF(("no matching ADDBA req found\n"));
303945eec175Sdamien return;
304045eec175Sdamien }
304145eec175Sdamien if (token != ba->ba_token) {
304245eec175Sdamien DPRINTF(("ignoring ADDBA resp from %s: token %x!=%x\n",
304345eec175Sdamien ether_sprintf(ni->ni_macaddr), token, ba->ba_token));
304445eec175Sdamien return;
304545eec175Sdamien }
304645eec175Sdamien /* we got an ADDBA Response matching our request, stop timeout */
304745eec175Sdamien timeout_del(&ba->ba_to);
304845eec175Sdamien
304945eec175Sdamien if (status != IEEE80211_STATUS_SUCCESS) {
3050aefc44daSstsp if (ni->ni_addba_req_intval[tid] <
3051aefc44daSstsp IEEE80211_ADDBA_REQ_INTVAL_MAX)
3052aefc44daSstsp ni->ni_addba_req_intval[tid]++;
3053aefc44daSstsp
3054aefc44daSstsp ieee80211_addba_resp_refuse(ic, ni, tid, status);
3055aefc44daSstsp
3056aefc44daSstsp /*
3057aefc44daSstsp * In case the peer believes there is an existing
3058aefc44daSstsp * block ack agreement with us, try to delete it.
3059aefc44daSstsp */
3060aefc44daSstsp IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA,
3061aefc44daSstsp IEEE80211_ACTION_DELBA,
3062aefc44daSstsp IEEE80211_REASON_SETUP_REQUIRED << 16 | 1 << 8 | tid);
306345eec175Sdamien return;
306445eec175Sdamien }
3065aefc44daSstsp
3066aefc44daSstsp /* notify drivers of this new Block Ack agreement */
3067aefc44daSstsp if (ic->ic_ampdu_tx_start != NULL)
3068aefc44daSstsp err = ic->ic_ampdu_tx_start(ic, ni, tid);
3069aefc44daSstsp
3070aefc44daSstsp if (err == EBUSY) {
3071aefc44daSstsp /* driver will accept or refuse agreement when done */
3072aefc44daSstsp return;
3073aefc44daSstsp } else if (err) {
3074aefc44daSstsp /* driver failed to setup, rollback */
3075aefc44daSstsp ieee80211_addba_resp_refuse(ic, ni, tid,
3076aefc44daSstsp IEEE80211_STATUS_UNSPECIFIED);
3077aefc44daSstsp } else
3078aefc44daSstsp ieee80211_addba_resp_accept(ic, ni, tid);
3079aefc44daSstsp }
3080aefc44daSstsp
3081aefc44daSstsp void
ieee80211_addba_resp_accept(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid)3082aefc44daSstsp ieee80211_addba_resp_accept(struct ieee80211com *ic,
3083aefc44daSstsp struct ieee80211_node *ni, uint8_t tid)
3084aefc44daSstsp {
3085aefc44daSstsp struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
3086aefc44daSstsp
308745eec175Sdamien /* MLME-ADDBA.confirm(Success) */
308845eec175Sdamien ba->ba_state = IEEE80211_BA_AGREED;
308944fdf27cSstsp ic->ic_stats.is_ht_tx_ba_agreements++;
309045eec175Sdamien
3091aefc44daSstsp /* Reset ADDBA request interval. */
3092aefc44daSstsp ni->ni_addba_req_intval[tid] = 1;
309345eec175Sdamien
3094bc7d1191Sstsp ni->ni_qos_txseqs[tid] = ba->ba_winstart;
3095bc7d1191Sstsp
309645eec175Sdamien /* start Block Ack inactivity timeout */
3097ec69e05aSdamien if (ba->ba_timeout_val != 0)
309845eec175Sdamien timeout_add_usec(&ba->ba_to, ba->ba_timeout_val);
309945eec175Sdamien }
310045eec175Sdamien
3101aefc44daSstsp void
ieee80211_addba_resp_refuse(struct ieee80211com * ic,struct ieee80211_node * ni,uint8_t tid,uint16_t status)3102aefc44daSstsp ieee80211_addba_resp_refuse(struct ieee80211com *ic,
3103aefc44daSstsp struct ieee80211_node *ni, uint8_t tid, uint16_t status)
3104aefc44daSstsp {
3105aefc44daSstsp struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
3106aefc44daSstsp
3107aefc44daSstsp /* MLME-ADDBA.confirm(Failure) */
3108aefc44daSstsp ba->ba_state = IEEE80211_BA_INIT;
3109aefc44daSstsp }
3110aefc44daSstsp
311145eec175Sdamien /*-
311245eec175Sdamien * DELBA frame format:
311345eec175Sdamien * [1] Category
311445eec175Sdamien * [1] Action
311545eec175Sdamien * [2] DELBA Parameter Set
311645eec175Sdamien * [2] Reason Code
311745eec175Sdamien */
311845eec175Sdamien void
ieee80211_recv_delba(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)311945eec175Sdamien ieee80211_recv_delba(struct ieee80211com *ic, struct mbuf *m,
312045eec175Sdamien struct ieee80211_node *ni)
312145eec175Sdamien {
312245eec175Sdamien const struct ieee80211_frame *wh;
312345eec175Sdamien const u_int8_t *frm;
312445eec175Sdamien u_int16_t params, reason;
312545eec175Sdamien u_int8_t tid;
312645eec175Sdamien int i;
312745eec175Sdamien
312845eec175Sdamien if (m->m_len < sizeof(*wh) + 6) {
312945eec175Sdamien DPRINTF(("frame too short\n"));
313045eec175Sdamien return;
313145eec175Sdamien }
313245eec175Sdamien wh = mtod(m, struct ieee80211_frame *);
313345eec175Sdamien frm = (const u_int8_t *)&wh[1];
313445eec175Sdamien
313545eec175Sdamien params = LE_READ_2(&frm[2]);
313645eec175Sdamien reason = LE_READ_2(&frm[4]);
313745eec175Sdamien tid = params >> 12;
313845eec175Sdamien
313945eec175Sdamien DPRINTF(("received DELBA from %s, TID %d, reason %d\n",
314045eec175Sdamien ether_sprintf(ni->ni_macaddr), tid, reason));
314145eec175Sdamien
3142ec69e05aSdamien if (params & IEEE80211_DELBA_INITIATOR) {
3143ec69e05aSdamien /* MLME-DELBA.indication(Originator) */
3144ec69e05aSdamien struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
3145ec69e05aSdamien
3146ec69e05aSdamien if (ba->ba_state != IEEE80211_BA_AGREED) {
3147ec69e05aSdamien DPRINTF(("no matching Block Ack agreement\n"));
314845eec175Sdamien return;
314945eec175Sdamien }
315045eec175Sdamien /* notify drivers of the end of the Block Ack agreement */
3151ec69e05aSdamien if (ic->ic_ampdu_rx_stop != NULL)
3152ec69e05aSdamien ic->ic_ampdu_rx_stop(ic, ni, tid);
315345eec175Sdamien
315445eec175Sdamien ba->ba_state = IEEE80211_BA_INIT;
315545eec175Sdamien /* stop Block Ack inactivity timer */
315645eec175Sdamien timeout_del(&ba->ba_to);
315720fce2ddSstsp timeout_del(&ba->ba_gap_to);
315820599cf9Sstsp ba->ba_gapwait = 0;
3159ec69e05aSdamien
316045eec175Sdamien if (ba->ba_buf != NULL) {
316145eec175Sdamien /* free all MSDUs stored in reordering buffer */
316245eec175Sdamien for (i = 0; i < IEEE80211_BA_MAX_WINSZ; i++)
316345eec175Sdamien m_freem(ba->ba_buf[i].m);
316445eec175Sdamien /* free reordering buffer */
31654adcc1c9Stb free(ba->ba_buf, M_DEVBUF,
31664adcc1c9Stb IEEE80211_BA_MAX_WINSZ * sizeof(*ba->ba_buf));
316745eec175Sdamien ba->ba_buf = NULL;
316845eec175Sdamien }
3169ec69e05aSdamien } else {
3170ec69e05aSdamien /* MLME-DELBA.indication(Recipient) */
3171ec69e05aSdamien struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid];
3172ec69e05aSdamien
3173ec69e05aSdamien if (ba->ba_state != IEEE80211_BA_AGREED) {
3174ec69e05aSdamien DPRINTF(("no matching Block Ack agreement\n"));
3175ec69e05aSdamien return;
3176ec69e05aSdamien }
3177ec69e05aSdamien /* notify drivers of the end of the Block Ack agreement */
3178ec69e05aSdamien if (ic->ic_ampdu_tx_stop != NULL)
3179ec69e05aSdamien ic->ic_ampdu_tx_stop(ic, ni, tid);
3180ec69e05aSdamien
3181ec69e05aSdamien ba->ba_state = IEEE80211_BA_INIT;
3182ec69e05aSdamien /* stop Block Ack inactivity timer */
3183ec69e05aSdamien timeout_del(&ba->ba_to);
3184ec69e05aSdamien }
318545eec175Sdamien }
318645eec175Sdamien
318745eec175Sdamien /*-
318845eec175Sdamien * SA Query Request frame format:
318945eec175Sdamien * [1] Category
319045eec175Sdamien * [1] Action
3191643bf791Sdamien * [2] Transaction Identifier
319245eec175Sdamien */
319345eec175Sdamien void
ieee80211_recv_sa_query_req(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)319445eec175Sdamien ieee80211_recv_sa_query_req(struct ieee80211com *ic, struct mbuf *m,
319545eec175Sdamien struct ieee80211_node *ni)
319645eec175Sdamien {
319745eec175Sdamien const struct ieee80211_frame *wh;
319845eec175Sdamien const u_int8_t *frm;
319945eec175Sdamien
320045eec175Sdamien if (ic->ic_opmode != IEEE80211_M_STA ||
320145eec175Sdamien !(ni->ni_flags & IEEE80211_NODE_MFP)) {
320245eec175Sdamien DPRINTF(("unexpected SA Query req from %s\n",
320345eec175Sdamien ether_sprintf(ni->ni_macaddr)));
320445eec175Sdamien return;
320545eec175Sdamien }
3206199d5af3Sdamien if (m->m_len < sizeof(*wh) + 4) {
320745eec175Sdamien DPRINTF(("frame too short\n"));
320845eec175Sdamien return;
320945eec175Sdamien }
321045eec175Sdamien wh = mtod(m, struct ieee80211_frame *);
321145eec175Sdamien frm = (const u_int8_t *)&wh[1];
321245eec175Sdamien
321345eec175Sdamien /* MLME-SAQuery.indication */
321445eec175Sdamien
321545eec175Sdamien /* save Transaction Identifier for SA Query Response */
3216199d5af3Sdamien ni->ni_sa_query_trid = LE_READ_2(&frm[2]);
321745eec175Sdamien
321845eec175Sdamien /* MLME-SAQuery.response */
321945eec175Sdamien IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_SA_QUERY,
322045eec175Sdamien IEEE80211_ACTION_SA_QUERY_RESP, 0);
322145eec175Sdamien }
322245eec175Sdamien
322345eec175Sdamien #ifndef IEEE80211_STA_ONLY
322445eec175Sdamien /*-
322545eec175Sdamien * SA Query Response frame format:
322645eec175Sdamien * [1] Category
322745eec175Sdamien * [1] Action
3228643bf791Sdamien * [2] Transaction Identifier
322945eec175Sdamien */
323045eec175Sdamien void
ieee80211_recv_sa_query_resp(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)323145eec175Sdamien ieee80211_recv_sa_query_resp(struct ieee80211com *ic, struct mbuf *m,
323245eec175Sdamien struct ieee80211_node *ni)
323345eec175Sdamien {
323445eec175Sdamien const struct ieee80211_frame *wh;
323545eec175Sdamien const u_int8_t *frm;
323645eec175Sdamien
323745eec175Sdamien /* ignore if we're not engaged in an SA Query with that STA */
323845eec175Sdamien if (!(ni->ni_flags & IEEE80211_NODE_SA_QUERY)) {
323945eec175Sdamien DPRINTF(("unexpected SA Query resp from %s\n",
324045eec175Sdamien ether_sprintf(ni->ni_macaddr)));
324145eec175Sdamien return;
324245eec175Sdamien }
3243199d5af3Sdamien if (m->m_len < sizeof(*wh) + 4) {
324445eec175Sdamien DPRINTF(("frame too short\n"));
324545eec175Sdamien return;
324645eec175Sdamien }
324745eec175Sdamien wh = mtod(m, struct ieee80211_frame *);
324845eec175Sdamien frm = (const u_int8_t *)&wh[1];
324945eec175Sdamien
325045eec175Sdamien /* check that Transaction Identifier matches */
3251199d5af3Sdamien if (ni->ni_sa_query_trid != LE_READ_2(&frm[2])) {
325245eec175Sdamien DPRINTF(("transaction identifier does not match\n"));
325345eec175Sdamien return;
325445eec175Sdamien }
325545eec175Sdamien /* MLME-SAQuery.confirm */
325645eec175Sdamien timeout_del(&ni->ni_sa_query_to);
325745eec175Sdamien ni->ni_flags &= ~IEEE80211_NODE_SA_QUERY;
325845eec175Sdamien }
325945eec175Sdamien #endif
326045eec175Sdamien
3261fcdd546dSdamien /*-
3262fcdd546dSdamien * Action frame format:
3263f2b57c48Sdamien * [1] Category
3264fcdd546dSdamien * [1] Action
3265fcdd546dSdamien */
3266fcdd546dSdamien void
ieee80211_recv_action(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)326745eec175Sdamien ieee80211_recv_action(struct ieee80211com *ic, struct mbuf *m,
3268fcdd546dSdamien struct ieee80211_node *ni)
3269fcdd546dSdamien {
3270f2b57c48Sdamien const struct ieee80211_frame *wh;
3271f2b57c48Sdamien const u_int8_t *frm;
3272f2b57c48Sdamien
327345eec175Sdamien if (m->m_len < sizeof(*wh) + 2) {
3274f2b57c48Sdamien DPRINTF(("frame too short\n"));
3275f2b57c48Sdamien return;
3276f2b57c48Sdamien }
327745eec175Sdamien wh = mtod(m, struct ieee80211_frame *);
3278f2b57c48Sdamien frm = (const u_int8_t *)&wh[1];
3279f2b57c48Sdamien
3280f2b57c48Sdamien switch (frm[0]) {
3281f2b57c48Sdamien case IEEE80211_CATEG_BA:
3282f2b57c48Sdamien switch (frm[1]) {
3283f2b57c48Sdamien case IEEE80211_ACTION_ADDBA_REQ:
328445eec175Sdamien ieee80211_recv_addba_req(ic, m, ni);
3285f2b57c48Sdamien break;
3286f2b57c48Sdamien case IEEE80211_ACTION_ADDBA_RESP:
328745eec175Sdamien ieee80211_recv_addba_resp(ic, m, ni);
3288f2b57c48Sdamien break;
3289f2b57c48Sdamien case IEEE80211_ACTION_DELBA:
329045eec175Sdamien ieee80211_recv_delba(ic, m, ni);
3291f2b57c48Sdamien break;
3292f2b57c48Sdamien }
3293f2b57c48Sdamien break;
329445eec175Sdamien case IEEE80211_CATEG_SA_QUERY:
3295f2b57c48Sdamien switch (frm[1]) {
329645eec175Sdamien case IEEE80211_ACTION_SA_QUERY_REQ:
329745eec175Sdamien ieee80211_recv_sa_query_req(ic, m, ni);
3298f2b57c48Sdamien break;
329945eec175Sdamien #ifndef IEEE80211_STA_ONLY
330045eec175Sdamien case IEEE80211_ACTION_SA_QUERY_RESP:
330145eec175Sdamien ieee80211_recv_sa_query_resp(ic, m, ni);
3302f2b57c48Sdamien break;
330345eec175Sdamien #endif
3304f2b57c48Sdamien }
3305f2b57c48Sdamien break;
3306f2b57c48Sdamien default:
3307acbef1c8Sdamien DPRINTF(("action frame category %d not handled\n", frm[0]));
3308f2b57c48Sdamien break;
3309f2b57c48Sdamien }
3310fcdd546dSdamien }
3311fcdd546dSdamien
33129e9787ffSdamien void
ieee80211_recv_mgmt(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni,struct ieee80211_rxinfo * rxi,int subtype)331345eec175Sdamien ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
331430d05aacSdamien struct ieee80211_node *ni, struct ieee80211_rxinfo *rxi, int subtype)
33159e9787ffSdamien {
33169e9787ffSdamien switch (subtype) {
33179e9787ffSdamien case IEEE80211_FC0_SUBTYPE_BEACON:
331845eec175Sdamien ieee80211_recv_probe_resp(ic, m, ni, rxi, 0);
3319e03e709cSdamien break;
3320e03e709cSdamien case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
332145eec175Sdamien ieee80211_recv_probe_resp(ic, m, ni, rxi, 1);
33229e9787ffSdamien break;
3323171ac09aSdamien #ifndef IEEE80211_STA_ONLY
33249e9787ffSdamien case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
332545eec175Sdamien ieee80211_recv_probe_req(ic, m, ni, rxi);
33269e9787ffSdamien break;
3327171ac09aSdamien #endif
33289e9787ffSdamien case IEEE80211_FC0_SUBTYPE_AUTH:
332945eec175Sdamien ieee80211_recv_auth(ic, m, ni, rxi);
33309e9787ffSdamien break;
3331171ac09aSdamien #ifndef IEEE80211_STA_ONLY
33329e9787ffSdamien case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
333345eec175Sdamien ieee80211_recv_assoc_req(ic, m, ni, rxi, 0);
3334e03e709cSdamien break;
33359e9787ffSdamien case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
333645eec175Sdamien ieee80211_recv_assoc_req(ic, m, ni, rxi, 1);
33379e9787ffSdamien break;
3338171ac09aSdamien #endif
33399e9787ffSdamien case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
334045eec175Sdamien ieee80211_recv_assoc_resp(ic, m, ni, 0);
3341e03e709cSdamien break;
33429e9787ffSdamien case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
334345eec175Sdamien ieee80211_recv_assoc_resp(ic, m, ni, 1);
33449e9787ffSdamien break;
33459e9787ffSdamien case IEEE80211_FC0_SUBTYPE_DEAUTH:
334645eec175Sdamien ieee80211_recv_deauth(ic, m, ni);
33479e9787ffSdamien break;
33489e9787ffSdamien case IEEE80211_FC0_SUBTYPE_DISASSOC:
334945eec175Sdamien ieee80211_recv_disassoc(ic, m, ni);
33509e9787ffSdamien break;
3351fcdd546dSdamien case IEEE80211_FC0_SUBTYPE_ACTION:
335245eec175Sdamien ieee80211_recv_action(ic, m, ni);
3353fcdd546dSdamien break;
335491b2158bSmillert default:
3355932b9027Sdamien DPRINTF(("mgmt frame with subtype 0x%x not handled\n",
3356932b9027Sdamien subtype));
335791b2158bSmillert ic->ic_stats.is_rx_badsubtype++;
335891b2158bSmillert break;
335991b2158bSmillert }
336091b2158bSmillert }
336191b2158bSmillert
3362171ac09aSdamien #ifndef IEEE80211_STA_ONLY
33637afadc6eSdamien /*
33647afadc6eSdamien * Process an incoming PS-Poll control frame (see 11.2).
33657afadc6eSdamien */
3366250085e6Sdamien void
ieee80211_recv_pspoll(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)33673945a2e1Sdamien ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m,
33683945a2e1Sdamien struct ieee80211_node *ni)
336991b2158bSmillert {
337091b2158bSmillert struct ifnet *ifp = &ic->ic_if;
3371361aed72Sdamien struct ieee80211_frame_pspoll *psp;
337291b2158bSmillert struct ieee80211_frame *wh;
337391b2158bSmillert u_int16_t aid;
337491b2158bSmillert
337593d01030Sdamien if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
337693d01030Sdamien !(ic->ic_caps & IEEE80211_C_APPMGT) ||
33773945a2e1Sdamien ni->ni_state != IEEE80211_STA_ASSOC)
337891b2158bSmillert return;
337991b2158bSmillert
3380361aed72Sdamien if (m->m_len < sizeof(*psp)) {
3381361aed72Sdamien DPRINTF(("frame too short, len %u\n", m->m_len));
3382361aed72Sdamien ic->ic_stats.is_rx_tooshort++;
338391b2158bSmillert return;
338491b2158bSmillert }
3385361aed72Sdamien psp = mtod(m, struct ieee80211_frame_pspoll *);
3386361aed72Sdamien if (!IEEE80211_ADDR_EQ(psp->i_bssid, ic->ic_bss->ni_bssid)) {
3387361aed72Sdamien DPRINTF(("discard pspoll frame to BSS %s\n",
3388361aed72Sdamien ether_sprintf(psp->i_bssid)));
3389361aed72Sdamien ic->ic_stats.is_rx_wrongbss++;
339091b2158bSmillert return;
339191b2158bSmillert }
3392361aed72Sdamien aid = letoh16(*(u_int16_t *)psp->i_aid);
339391b2158bSmillert if (aid != ni->ni_associd) {
3394361aed72Sdamien DPRINTF(("invalid pspoll aid %x from %s\n", aid,
3395962c982dSdamien ether_sprintf(psp->i_ta)));
339691b2158bSmillert return;
339791b2158bSmillert }
339891b2158bSmillert
3399361aed72Sdamien /* take the first queued frame and put it out.. */
3400351e1934Sdlg m = mq_dequeue(&ni->ni_savedq);
3401361aed72Sdamien if (m == NULL)
340291b2158bSmillert return;
3403351e1934Sdlg if (mq_empty(&ni->ni_savedq)) {
3404361aed72Sdamien /* last queued frame, turn off the TIM bit */
3405158c4605Sdamien (*ic->ic_set_tim)(ic, ni->ni_associd, 0);
3406361aed72Sdamien } else {
3407361aed72Sdamien /* more queued frames, set the more data bit */
3408361aed72Sdamien wh = mtod(m, struct ieee80211_frame *);
340991b2158bSmillert wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
3410361aed72Sdamien }
3411351e1934Sdlg mq_enqueue(&ic->ic_pwrsaveq, m);
341267de757dSmpi if_start(ifp);
341391b2158bSmillert }
3414171ac09aSdamien #endif /* IEEE80211_STA_ONLY */
341545eec175Sdamien
341645eec175Sdamien /*
3417ec69e05aSdamien * Process an incoming BlockAckReq control frame (see 7.2.1.7).
341845eec175Sdamien */
341945eec175Sdamien void
ieee80211_recv_bar(struct ieee80211com * ic,struct mbuf * m,struct ieee80211_node * ni)342045eec175Sdamien ieee80211_recv_bar(struct ieee80211com *ic, struct mbuf *m,
342145eec175Sdamien struct ieee80211_node *ni)
342245eec175Sdamien {
342345eec175Sdamien const struct ieee80211_frame_min *wh;
342445eec175Sdamien const u_int8_t *frm;
342545eec175Sdamien u_int16_t ctl, ssn;
342645eec175Sdamien u_int8_t tid, ntids;
342745eec175Sdamien
342845eec175Sdamien if (!(ni->ni_flags & IEEE80211_NODE_HT)) {
3429ec69e05aSdamien DPRINTF(("received BlockAckReq from non-HT STA %s\n",
343045eec175Sdamien ether_sprintf(ni->ni_macaddr)));
343145eec175Sdamien return;
343245eec175Sdamien }
343345eec175Sdamien if (m->m_len < sizeof(*wh) + 4) {
343445eec175Sdamien DPRINTF(("frame too short\n"));
343545eec175Sdamien return;
343645eec175Sdamien }
343745eec175Sdamien wh = mtod(m, struct ieee80211_frame_min *);
343845eec175Sdamien frm = (const u_int8_t *)&wh[1];
343945eec175Sdamien
3440ec69e05aSdamien /* read BlockAckReq Control field */
344145eec175Sdamien ctl = LE_READ_2(&frm[0]);
344245eec175Sdamien tid = ctl >> 12;
344345eec175Sdamien
344445eec175Sdamien /* determine BlockAckReq frame variant */
344545eec175Sdamien if (ctl & IEEE80211_BA_MULTI_TID) {
344645eec175Sdamien /* Multi-TID BlockAckReq variant (PSMP only) */
344745eec175Sdamien ntids = tid + 1;
344845eec175Sdamien
344945eec175Sdamien if (m->m_len < sizeof(*wh) + 2 + 4 * ntids) {
345045eec175Sdamien DPRINTF(("MTBAR frame too short\n"));
345145eec175Sdamien return;
345245eec175Sdamien }
3453ec69e05aSdamien frm += 2; /* skip BlockAckReq Control field */
345445eec175Sdamien while (ntids-- > 0) {
345545eec175Sdamien /* read MTBAR Information field */
345645eec175Sdamien tid = LE_READ_2(&frm[0]) >> 12;
345745eec175Sdamien ssn = LE_READ_2(&frm[2]) >> 4;
345845eec175Sdamien ieee80211_bar_tid(ic, ni, tid, ssn);
345945eec175Sdamien frm += 4;
346045eec175Sdamien }
346145eec175Sdamien } else {
346245eec175Sdamien /* Basic or Compressed BlockAckReq variants */
346345eec175Sdamien ssn = LE_READ_2(&frm[2]) >> 4;
346445eec175Sdamien ieee80211_bar_tid(ic, ni, tid, ssn);
346545eec175Sdamien }
346645eec175Sdamien }
346745eec175Sdamien
346845eec175Sdamien /*
3469ec69e05aSdamien * Process a BlockAckReq for a specific TID (see 9.10.7.6.3).
347045eec175Sdamien * This is the common back-end for all BlockAckReq frame variants.
347145eec175Sdamien */
347245eec175Sdamien void
ieee80211_bar_tid(struct ieee80211com * ic,struct ieee80211_node * ni,u_int8_t tid,u_int16_t ssn)347345eec175Sdamien ieee80211_bar_tid(struct ieee80211com *ic, struct ieee80211_node *ni,
347445eec175Sdamien u_int8_t tid, u_int16_t ssn)
347545eec175Sdamien {
3476ec69e05aSdamien struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid];
347745eec175Sdamien
347845eec175Sdamien /* check if we have a Block Ack agreement for RA/TID */
347945eec175Sdamien if (ba->ba_state != IEEE80211_BA_AGREED) {
348045eec175Sdamien /* XXX not sure in PBAC case */
348145eec175Sdamien /* send a DELBA with reason code UNKNOWN-BA */
348245eec175Sdamien IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA,
348345eec175Sdamien IEEE80211_ACTION_DELBA,
348445eec175Sdamien IEEE80211_REASON_SETUP_REQUIRED << 16 | tid);
348545eec175Sdamien return;
348645eec175Sdamien }
348745eec175Sdamien /* check if it is a Protected Block Ack agreement */
348845eec175Sdamien if ((ni->ni_flags & IEEE80211_NODE_MFP) &&
348945eec175Sdamien (ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC)) {
349045eec175Sdamien /* ADDBA Requests must be used in PBAC case */
349145eec175Sdamien if (SEQ_LT(ssn, ba->ba_winstart) ||
349245eec175Sdamien SEQ_LT(ba->ba_winend, ssn))
349345eec175Sdamien ic->ic_stats.is_pbac_errs++;
349445eec175Sdamien return; /* PBAC, do not move window */
349545eec175Sdamien }
349645eec175Sdamien /* reset Block Ack inactivity timer */
34975c7c7275Sstsp if (ba->ba_timeout_val != 0)
349845eec175Sdamien timeout_add_usec(&ba->ba_to, ba->ba_timeout_val);
349945eec175Sdamien
35008fbaf8a2Sstsp if (SEQ_LT(ba->ba_winstart, ssn)) {
35018fbaf8a2Sstsp struct mbuf_list ml = MBUF_LIST_INITIALIZER();
35028fbaf8a2Sstsp ieee80211_ba_move_window(ic, ni, tid, ssn, &ml);
35038fbaf8a2Sstsp if_input(&ic->ic_if, &ml);
35048fbaf8a2Sstsp }
350545eec175Sdamien }
3506