1b032f27cSSam Leffler /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3fe267a55SPedro F. Giffuni * 4b032f27cSSam Leffler * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting 5b032f27cSSam Leffler * All rights reserved. 6b032f27cSSam Leffler * 7b032f27cSSam Leffler * Redistribution and use in source and binary forms, with or without 8b032f27cSSam Leffler * modification, are permitted provided that the following conditions 9b032f27cSSam Leffler * are met: 10b032f27cSSam Leffler * 1. Redistributions of source code must retain the above copyright 11b032f27cSSam Leffler * notice, this list of conditions and the following disclaimer. 12b032f27cSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 13b032f27cSSam Leffler * notice, this list of conditions and the following disclaimer in the 14b032f27cSSam Leffler * documentation and/or other materials provided with the distribution. 15b032f27cSSam Leffler * 16b032f27cSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17b032f27cSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18b032f27cSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19b032f27cSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20b032f27cSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21b032f27cSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22b032f27cSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23b032f27cSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24b032f27cSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25b032f27cSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26b032f27cSSam Leffler */ 27b032f27cSSam Leffler 28b032f27cSSam Leffler /* 29b032f27cSSam Leffler * IEEE 802.11 HOSTAP mode support. 30b032f27cSSam Leffler */ 31b032f27cSSam Leffler #include "opt_inet.h" 32b032f27cSSam Leffler #include "opt_wlan.h" 33b032f27cSSam Leffler 34b032f27cSSam Leffler #include <sys/param.h> 35b032f27cSSam Leffler #include <sys/systm.h> 36b032f27cSSam Leffler #include <sys/mbuf.h> 37b032f27cSSam Leffler #include <sys/malloc.h> 38b032f27cSSam Leffler #include <sys/kernel.h> 39b032f27cSSam Leffler 40b032f27cSSam Leffler #include <sys/socket.h> 41b032f27cSSam Leffler #include <sys/sockio.h> 42b032f27cSSam Leffler #include <sys/endian.h> 43b032f27cSSam Leffler #include <sys/errno.h> 44b032f27cSSam Leffler #include <sys/proc.h> 45b032f27cSSam Leffler #include <sys/sysctl.h> 46b032f27cSSam Leffler 47b032f27cSSam Leffler #include <net/if.h> 4876039bc8SGleb Smirnoff #include <net/if_var.h> 49b032f27cSSam Leffler #include <net/if_media.h> 50b032f27cSSam Leffler #include <net/if_llc.h> 513d0d5b21SJustin Hibbits #include <net/if_private.h> 52b032f27cSSam Leffler #include <net/ethernet.h> 53b032f27cSSam Leffler 54b032f27cSSam Leffler #include <net/bpf.h> 55b032f27cSSam Leffler 56b032f27cSSam Leffler #include <net80211/ieee80211_var.h> 57b032f27cSSam Leffler #include <net80211/ieee80211_hostap.h> 58b032f27cSSam Leffler #include <net80211/ieee80211_input.h> 59616190d0SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 60616190d0SSam Leffler #include <net80211/ieee80211_superg.h> 61616190d0SSam Leffler #endif 62b032f27cSSam Leffler #include <net80211/ieee80211_wds.h> 6351172f62SAdrian Chadd #include <net80211/ieee80211_vht.h> 648379e8dbSAdrian Chadd #include <net80211/ieee80211_sta.h> /* for parse_wmeie */ 65b032f27cSSam Leffler 66b032f27cSSam Leffler #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) 67b032f27cSSam Leffler 68b032f27cSSam Leffler static void hostap_vattach(struct ieee80211vap *); 69b032f27cSSam Leffler static int hostap_newstate(struct ieee80211vap *, enum ieee80211_state, int); 70b032f27cSSam Leffler static int hostap_input(struct ieee80211_node *ni, struct mbuf *m, 71c79f192cSAdrian Chadd const struct ieee80211_rx_stats *, 725463c4a4SSam Leffler int rssi, int nf); 73b032f27cSSam Leffler static void hostap_deliver_data(struct ieee80211vap *, 74b032f27cSSam Leffler struct ieee80211_node *, struct mbuf *); 75b032f27cSSam Leffler static void hostap_recv_mgmt(struct ieee80211_node *, struct mbuf *, 76c79f192cSAdrian Chadd int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf); 7749eae5f7SSam Leffler static void hostap_recv_ctl(struct ieee80211_node *, struct mbuf *, int); 78b032f27cSSam Leffler 79b032f27cSSam Leffler void 80b032f27cSSam Leffler ieee80211_hostap_attach(struct ieee80211com *ic) 81b032f27cSSam Leffler { 82b032f27cSSam Leffler ic->ic_vattach[IEEE80211_M_HOSTAP] = hostap_vattach; 83b032f27cSSam Leffler } 84b032f27cSSam Leffler 85b032f27cSSam Leffler void 86b032f27cSSam Leffler ieee80211_hostap_detach(struct ieee80211com *ic) 87b032f27cSSam Leffler { 88b032f27cSSam Leffler } 89b032f27cSSam Leffler 90b032f27cSSam Leffler static void 91b032f27cSSam Leffler hostap_vdetach(struct ieee80211vap *vap) 92b032f27cSSam Leffler { 93b032f27cSSam Leffler } 94b032f27cSSam Leffler 95b032f27cSSam Leffler static void 96b032f27cSSam Leffler hostap_vattach(struct ieee80211vap *vap) 97b032f27cSSam Leffler { 98b032f27cSSam Leffler vap->iv_newstate = hostap_newstate; 99b032f27cSSam Leffler vap->iv_input = hostap_input; 100b032f27cSSam Leffler vap->iv_recv_mgmt = hostap_recv_mgmt; 10149eae5f7SSam Leffler vap->iv_recv_ctl = hostap_recv_ctl; 102b032f27cSSam Leffler vap->iv_opdetach = hostap_vdetach; 103b032f27cSSam Leffler vap->iv_deliver_data = hostap_deliver_data; 104e7f0d7cfSAdrian Chadd vap->iv_recv_pspoll = ieee80211_recv_pspoll; 105b032f27cSSam Leffler } 106b032f27cSSam Leffler 107b032f27cSSam Leffler static void 108b032f27cSSam Leffler sta_disassoc(void *arg, struct ieee80211_node *ni) 109b032f27cSSam Leffler { 110b032f27cSSam Leffler 1117db788c6SAndriy Voskoboinyk if (ni->ni_associd != 0) { 112b032f27cSSam Leffler IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC, 113b032f27cSSam Leffler IEEE80211_REASON_ASSOC_LEAVE); 114b032f27cSSam Leffler ieee80211_node_leave(ni); 115b032f27cSSam Leffler } 116b032f27cSSam Leffler } 117b032f27cSSam Leffler 1184e150988SSam Leffler static void 1194e150988SSam Leffler sta_csa(void *arg, struct ieee80211_node *ni) 1204e150988SSam Leffler { 1217db788c6SAndriy Voskoboinyk struct ieee80211vap *vap = ni->ni_vap; 1224e150988SSam Leffler 1237db788c6SAndriy Voskoboinyk if (ni->ni_associd != 0) 1244e150988SSam Leffler if (ni->ni_inact > vap->iv_inact_init) { 1254e150988SSam Leffler ni->ni_inact = vap->iv_inact_init; 1264e150988SSam Leffler IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, 1274e150988SSam Leffler "%s: inact %u", __func__, ni->ni_inact); 1284e150988SSam Leffler } 1294e150988SSam Leffler } 1304e150988SSam Leffler 1317131987dSSam Leffler static void 1327131987dSSam Leffler sta_drop(void *arg, struct ieee80211_node *ni) 1337131987dSSam Leffler { 1347131987dSSam Leffler 1357db788c6SAndriy Voskoboinyk if (ni->ni_associd != 0) 1367131987dSSam Leffler ieee80211_node_leave(ni); 1377131987dSSam Leffler } 1387131987dSSam Leffler 1397131987dSSam Leffler /* 1407131987dSSam Leffler * Does a channel change require associated stations to re-associate 1417131987dSSam Leffler * so protocol state is correct. This is used when doing CSA across 1427131987dSSam Leffler * bands or similar (e.g. HT -> legacy). 1437131987dSSam Leffler */ 1447131987dSSam Leffler static int 1457131987dSSam Leffler isbandchange(struct ieee80211com *ic) 1467131987dSSam Leffler { 1477131987dSSam Leffler return ((ic->ic_bsschan->ic_flags ^ ic->ic_csa_newchan->ic_flags) & 1487131987dSSam Leffler (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_HALF | 1497131987dSSam Leffler IEEE80211_CHAN_QUARTER | IEEE80211_CHAN_HT)) != 0; 1507131987dSSam Leffler } 1517131987dSSam Leffler 152b032f27cSSam Leffler /* 153b032f27cSSam Leffler * IEEE80211_M_HOSTAP vap state machine handler. 154b032f27cSSam Leffler */ 155b032f27cSSam Leffler static int 156b032f27cSSam Leffler hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 157b032f27cSSam Leffler { 158b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 159b032f27cSSam Leffler enum ieee80211_state ostate; 160b032f27cSSam Leffler 161b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 162b032f27cSSam Leffler 163b032f27cSSam Leffler ostate = vap->iv_state; 164b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 165b032f27cSSam Leffler __func__, ieee80211_state_name[ostate], 166b032f27cSSam Leffler ieee80211_state_name[nstate], arg); 167b032f27cSSam Leffler vap->iv_state = nstate; /* state transition */ 168b032f27cSSam Leffler if (ostate != IEEE80211_S_SCAN) 169b032f27cSSam Leffler ieee80211_cancel_scan(vap); /* background scan */ 170b032f27cSSam Leffler switch (nstate) { 171b032f27cSSam Leffler case IEEE80211_S_INIT: 172b032f27cSSam Leffler switch (ostate) { 173b032f27cSSam Leffler case IEEE80211_S_SCAN: 174b032f27cSSam Leffler ieee80211_cancel_scan(vap); 175b032f27cSSam Leffler break; 176b032f27cSSam Leffler case IEEE80211_S_CAC: 177b032f27cSSam Leffler ieee80211_dfs_cac_stop(vap); 178b032f27cSSam Leffler break; 179b032f27cSSam Leffler case IEEE80211_S_RUN: 1807db788c6SAndriy Voskoboinyk ieee80211_iterate_nodes_vap(&ic->ic_sta, vap, 1817db788c6SAndriy Voskoboinyk sta_disassoc, NULL); 182b032f27cSSam Leffler break; 183b032f27cSSam Leffler default: 184b032f27cSSam Leffler break; 185b032f27cSSam Leffler } 186b032f27cSSam Leffler if (ostate != IEEE80211_S_INIT) { 187b032f27cSSam Leffler /* NB: optimize INIT -> INIT case */ 188b032f27cSSam Leffler ieee80211_reset_bss(vap); 189b032f27cSSam Leffler } 190b032f27cSSam Leffler if (vap->iv_auth->ia_detach != NULL) 191b032f27cSSam Leffler vap->iv_auth->ia_detach(vap); 192b032f27cSSam Leffler break; 193b032f27cSSam Leffler case IEEE80211_S_SCAN: 194b032f27cSSam Leffler switch (ostate) { 195b032f27cSSam Leffler case IEEE80211_S_CSA: 196b032f27cSSam Leffler case IEEE80211_S_RUN: 1977db788c6SAndriy Voskoboinyk ieee80211_iterate_nodes_vap(&ic->ic_sta, vap, 1987db788c6SAndriy Voskoboinyk sta_disassoc, NULL); 199b032f27cSSam Leffler /* 200b032f27cSSam Leffler * Clear overlapping BSS state; the beacon frame 201b032f27cSSam Leffler * will be reconstructed on transition to the RUN 202b032f27cSSam Leffler * state and the timeout routines check if the flag 203b032f27cSSam Leffler * is set before doing anything so this is sufficient. 204b032f27cSSam Leffler */ 205f1481c8dSAdrian Chadd vap->iv_flags_ext &= ~IEEE80211_FEXT_NONERP_PR; 206f1481c8dSAdrian Chadd vap->iv_flags_ht &= ~IEEE80211_FHT_NONHT_PR; 207f1481c8dSAdrian Chadd /* XXX TODO: schedule deferred update? */ 208b032f27cSSam Leffler /* fall thru... */ 209b032f27cSSam Leffler case IEEE80211_S_CAC: 210b032f27cSSam Leffler /* 211b032f27cSSam Leffler * NB: We may get here because of a manual channel 212b032f27cSSam Leffler * change in which case we need to stop CAC 213b032f27cSSam Leffler * XXX no need to stop if ostate RUN but it's ok 214b032f27cSSam Leffler */ 215b032f27cSSam Leffler ieee80211_dfs_cac_stop(vap); 216b032f27cSSam Leffler /* fall thru... */ 217b032f27cSSam Leffler case IEEE80211_S_INIT: 218b032f27cSSam Leffler if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && 219b032f27cSSam Leffler !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { 220b032f27cSSam Leffler /* 221b032f27cSSam Leffler * Already have a channel; bypass the 222b032f27cSSam Leffler * scan and startup immediately. 223b032f27cSSam Leffler * ieee80211_create_ibss will call back to 224b032f27cSSam Leffler * move us to RUN state. 225b032f27cSSam Leffler */ 226b032f27cSSam Leffler ieee80211_create_ibss(vap, vap->iv_des_chan); 227b032f27cSSam Leffler break; 228b032f27cSSam Leffler } 229b032f27cSSam Leffler /* 230b032f27cSSam Leffler * Initiate a scan. We can come here as a result 231b032f27cSSam Leffler * of an IEEE80211_IOC_SCAN_REQ too in which case 232b032f27cSSam Leffler * the vap will be marked with IEEE80211_FEXT_SCANREQ 233b032f27cSSam Leffler * and the scan request parameters will be present 234b032f27cSSam Leffler * in iv_scanreq. Otherwise we do the default. 235b032f27cSSam Leffler */ 236b032f27cSSam Leffler if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 237b032f27cSSam Leffler ieee80211_check_scan(vap, 238b032f27cSSam Leffler vap->iv_scanreq_flags, 239b032f27cSSam Leffler vap->iv_scanreq_duration, 240b032f27cSSam Leffler vap->iv_scanreq_mindwell, 241b032f27cSSam Leffler vap->iv_scanreq_maxdwell, 242b032f27cSSam Leffler vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 243b032f27cSSam Leffler vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 244b032f27cSSam Leffler } else 245b032f27cSSam Leffler ieee80211_check_scan_current(vap); 246b032f27cSSam Leffler break; 247b032f27cSSam Leffler case IEEE80211_S_SCAN: 248b032f27cSSam Leffler /* 249b032f27cSSam Leffler * A state change requires a reset; scan. 250b032f27cSSam Leffler */ 251b032f27cSSam Leffler ieee80211_check_scan_current(vap); 252b032f27cSSam Leffler break; 253b032f27cSSam Leffler default: 254b032f27cSSam Leffler break; 255b032f27cSSam Leffler } 256b032f27cSSam Leffler break; 257b032f27cSSam Leffler case IEEE80211_S_CAC: 258b032f27cSSam Leffler /* 259b032f27cSSam Leffler * Start CAC on a DFS channel. We come here when starting 260b032f27cSSam Leffler * a bss on a DFS channel (see ieee80211_create_ibss). 261b032f27cSSam Leffler */ 262b032f27cSSam Leffler ieee80211_dfs_cac_start(vap); 263b032f27cSSam Leffler break; 264b032f27cSSam Leffler case IEEE80211_S_RUN: 265b032f27cSSam Leffler if (vap->iv_flags & IEEE80211_F_WPA) { 266b032f27cSSam Leffler /* XXX validate prerequisites */ 267b032f27cSSam Leffler } 268b032f27cSSam Leffler switch (ostate) { 269b032f27cSSam Leffler case IEEE80211_S_INIT: 270b032f27cSSam Leffler /* 271b032f27cSSam Leffler * Already have a channel; bypass the 272b032f27cSSam Leffler * scan and startup immediately. 273b032f27cSSam Leffler * Note that ieee80211_create_ibss will call 274b032f27cSSam Leffler * back to do a RUN->RUN state change. 275b032f27cSSam Leffler */ 276b032f27cSSam Leffler ieee80211_create_ibss(vap, 277b032f27cSSam Leffler ieee80211_ht_adjust_channel(ic, 2782bfc8a91SSam Leffler ic->ic_curchan, vap->iv_flags_ht)); 279b032f27cSSam Leffler /* NB: iv_bss is changed on return */ 280b032f27cSSam Leffler break; 281b032f27cSSam Leffler case IEEE80211_S_CAC: 282b032f27cSSam Leffler /* 283b032f27cSSam Leffler * NB: This is the normal state change when CAC 284b032f27cSSam Leffler * expires and no radar was detected; no need to 285b032f27cSSam Leffler * clear the CAC timer as it's already expired. 286b032f27cSSam Leffler */ 287b032f27cSSam Leffler /* fall thru... */ 288b032f27cSSam Leffler case IEEE80211_S_CSA: 289b032f27cSSam Leffler /* 2904e150988SSam Leffler * Shorten inactivity timer of associated stations 2914e150988SSam Leffler * to weed out sta's that don't follow a CSA. 2924e150988SSam Leffler */ 2937db788c6SAndriy Voskoboinyk ieee80211_iterate_nodes_vap(&ic->ic_sta, vap, 2947db788c6SAndriy Voskoboinyk sta_csa, NULL); 2954e150988SSam Leffler /* 296b032f27cSSam Leffler * Update bss node channel to reflect where 297b032f27cSSam Leffler * we landed after CSA. 298b032f27cSSam Leffler */ 299b032f27cSSam Leffler ieee80211_node_set_chan(vap->iv_bss, 300b032f27cSSam Leffler ieee80211_ht_adjust_channel(ic, ic->ic_curchan, 301b032f27cSSam Leffler ieee80211_htchanflags(vap->iv_bss->ni_chan))); 302b032f27cSSam Leffler /* XXX bypass debug msgs */ 303b032f27cSSam Leffler break; 304b032f27cSSam Leffler case IEEE80211_S_SCAN: 305b032f27cSSam Leffler case IEEE80211_S_RUN: 306b032f27cSSam Leffler #ifdef IEEE80211_DEBUG 307b032f27cSSam Leffler if (ieee80211_msg_debug(vap)) { 308b032f27cSSam Leffler struct ieee80211_node *ni = vap->iv_bss; 309b032f27cSSam Leffler ieee80211_note(vap, 310b032f27cSSam Leffler "synchronized with %s ssid ", 311b032f27cSSam Leffler ether_sprintf(ni->ni_bssid)); 312b032f27cSSam Leffler ieee80211_print_essid(ni->ni_essid, 313b032f27cSSam Leffler ni->ni_esslen); 314b032f27cSSam Leffler /* XXX MCS/HT */ 315b032f27cSSam Leffler printf(" channel %d start %uMb\n", 316b032f27cSSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan), 317b032f27cSSam Leffler IEEE80211_RATE2MBS(ni->ni_txrate)); 318b032f27cSSam Leffler } 319b032f27cSSam Leffler #endif 320b032f27cSSam Leffler break; 321b032f27cSSam Leffler default: 322b032f27cSSam Leffler break; 323b032f27cSSam Leffler } 324b032f27cSSam Leffler /* 325b032f27cSSam Leffler * Start/stop the authenticator. We delay until here 326b032f27cSSam Leffler * to allow configuration to happen out of order. 327b032f27cSSam Leffler */ 328b032f27cSSam Leffler if (vap->iv_auth->ia_attach != NULL) { 329b032f27cSSam Leffler /* XXX check failure */ 330b032f27cSSam Leffler vap->iv_auth->ia_attach(vap); 331b032f27cSSam Leffler } else if (vap->iv_auth->ia_detach != NULL) { 332b032f27cSSam Leffler vap->iv_auth->ia_detach(vap); 333b032f27cSSam Leffler } 334b032f27cSSam Leffler ieee80211_node_authorize(vap->iv_bss); 335b032f27cSSam Leffler break; 3367131987dSSam Leffler case IEEE80211_S_CSA: 3377131987dSSam Leffler if (ostate == IEEE80211_S_RUN && isbandchange(ic)) { 3387131987dSSam Leffler /* 3397131987dSSam Leffler * On a ``band change'' silently drop associated 3407131987dSSam Leffler * stations as they must re-associate before they 3417131987dSSam Leffler * can pass traffic (as otherwise protocol state 3427131987dSSam Leffler * such as capabilities and the negotiated rate 3437131987dSSam Leffler * set may/will be wrong). 3447131987dSSam Leffler */ 3457db788c6SAndriy Voskoboinyk ieee80211_iterate_nodes_vap(&ic->ic_sta, vap, 3467db788c6SAndriy Voskoboinyk sta_drop, NULL); 3477131987dSSam Leffler } 3487131987dSSam Leffler break; 349b032f27cSSam Leffler default: 350b032f27cSSam Leffler break; 351b032f27cSSam Leffler } 352b032f27cSSam Leffler return 0; 353b032f27cSSam Leffler } 354b032f27cSSam Leffler 355b032f27cSSam Leffler static void 356b032f27cSSam Leffler hostap_deliver_data(struct ieee80211vap *vap, 357b032f27cSSam Leffler struct ieee80211_node *ni, struct mbuf *m) 358b032f27cSSam Leffler { 359b032f27cSSam Leffler struct ether_header *eh = mtod(m, struct ether_header *); 360b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ifp; 361b032f27cSSam Leffler 362e1cfcbcbSSam Leffler /* clear driver/net80211 flags before passing up */ 36386bd0491SAndre Oppermann m->m_flags &= ~(M_MCAST | M_BCAST); 36486bd0491SAndre Oppermann m_clrprotoflags(m); 365e1cfcbcbSSam Leffler 366b032f27cSSam Leffler KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP, 367b032f27cSSam Leffler ("gack, opmode %d", vap->iv_opmode)); 368b032f27cSSam Leffler /* 369b032f27cSSam Leffler * Do accounting. 370b032f27cSSam Leffler */ 371dea45121SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 372b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_data); 373b032f27cSSam Leffler IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len); 374b032f27cSSam Leffler if (ETHER_IS_MULTICAST(eh->ether_dhost)) { 375b032f27cSSam Leffler m->m_flags |= M_MCAST; /* XXX M_BCAST? */ 376b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_mcast); 377b032f27cSSam Leffler } else 378b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_ucast); 379b032f27cSSam Leffler 380b032f27cSSam Leffler /* perform as a bridge within the AP */ 381b032f27cSSam Leffler if ((vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0) { 382b032f27cSSam Leffler struct mbuf *mcopy = NULL; 383b032f27cSSam Leffler 384b032f27cSSam Leffler if (m->m_flags & M_MCAST) { 385bd29f817SBjoern A. Zeeb mcopy = m_dup(m, IEEE80211_M_NOWAIT); 386b032f27cSSam Leffler if (mcopy == NULL) 387dea45121SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 388b032f27cSSam Leffler else 389b032f27cSSam Leffler mcopy->m_flags |= M_MCAST; 390b032f27cSSam Leffler } else { 391b032f27cSSam Leffler /* 392b032f27cSSam Leffler * Check if the destination is associated with the 393b032f27cSSam Leffler * same vap and authorized to receive traffic. 394b032f27cSSam Leffler * Beware of traffic destined for the vap itself; 395b032f27cSSam Leffler * sending it will not work; just let it be delivered 396b032f27cSSam Leffler * normally. 397b032f27cSSam Leffler */ 398b032f27cSSam Leffler struct ieee80211_node *sta = ieee80211_find_vap_node( 399b032f27cSSam Leffler &vap->iv_ic->ic_sta, vap, eh->ether_dhost); 400b032f27cSSam Leffler if (sta != NULL) { 401b032f27cSSam Leffler if (ieee80211_node_is_authorized(sta)) { 402b032f27cSSam Leffler /* 403b032f27cSSam Leffler * Beware of sending to ourself; this 404b032f27cSSam Leffler * needs to happen via the normal 405b032f27cSSam Leffler * input path. 406b032f27cSSam Leffler */ 407b032f27cSSam Leffler if (sta != vap->iv_bss) { 408b032f27cSSam Leffler mcopy = m; 409b032f27cSSam Leffler m = NULL; 410b032f27cSSam Leffler } 411b032f27cSSam Leffler } else { 412b032f27cSSam Leffler vap->iv_stats.is_rx_unauth++; 413b032f27cSSam Leffler IEEE80211_NODE_STAT(sta, rx_unauth); 414b032f27cSSam Leffler } 415b032f27cSSam Leffler ieee80211_free_node(sta); 416b032f27cSSam Leffler } 417b032f27cSSam Leffler } 4184d4d5e25SAndriy Voskoboinyk if (mcopy != NULL) 4194d4d5e25SAndriy Voskoboinyk (void) ieee80211_vap_xmitpkt(vap, mcopy); 420b032f27cSSam Leffler } 421b032f27cSSam Leffler if (m != NULL) { 422de607e3cSBjoern A. Zeeb struct epoch_tracker et; 423de607e3cSBjoern A. Zeeb 424b032f27cSSam Leffler /* 425b032f27cSSam Leffler * Mark frame as coming from vap's interface. 426b032f27cSSam Leffler */ 427b032f27cSSam Leffler m->m_pkthdr.rcvif = ifp; 428b032f27cSSam Leffler if (m->m_flags & M_MCAST) { 429b032f27cSSam Leffler /* 430b032f27cSSam Leffler * Spam DWDS vap's w/ multicast traffic. 431b032f27cSSam Leffler */ 432b032f27cSSam Leffler /* XXX only if dwds in use? */ 433b032f27cSSam Leffler ieee80211_dwds_mcast(vap, m); 434b032f27cSSam Leffler } 435b032f27cSSam Leffler if (ni->ni_vlan != 0) { 436b032f27cSSam Leffler /* attach vlan tag */ 437b032f27cSSam Leffler m->m_pkthdr.ether_vtag = ni->ni_vlan; 438b032f27cSSam Leffler m->m_flags |= M_VLANTAG; 439b032f27cSSam Leffler } 440de607e3cSBjoern A. Zeeb NET_EPOCH_ENTER(et); 441b032f27cSSam Leffler ifp->if_input(ifp, m); 442de607e3cSBjoern A. Zeeb NET_EPOCH_EXIT(et); 443b032f27cSSam Leffler } 444b032f27cSSam Leffler } 445b032f27cSSam Leffler 446b032f27cSSam Leffler /* 447b032f27cSSam Leffler * Decide if a received management frame should be 448b032f27cSSam Leffler * printed when debugging is enabled. This filters some 449b032f27cSSam Leffler * of the less interesting frames that come frequently 450b032f27cSSam Leffler * (e.g. beacons). 451b032f27cSSam Leffler */ 452b032f27cSSam Leffler static __inline int 453b032f27cSSam Leffler doprint(struct ieee80211vap *vap, int subtype) 454b032f27cSSam Leffler { 455b032f27cSSam Leffler switch (subtype) { 456b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_BEACON: 457b032f27cSSam Leffler return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); 458b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 459b032f27cSSam Leffler return 0; 460b032f27cSSam Leffler } 461b032f27cSSam Leffler return 1; 462b032f27cSSam Leffler } 463b032f27cSSam Leffler 464b032f27cSSam Leffler /* 465b032f27cSSam Leffler * Process a received frame. The node associated with the sender 466b032f27cSSam Leffler * should be supplied. If nothing was found in the node table then 467b032f27cSSam Leffler * the caller is assumed to supply a reference to iv_bss instead. 468b032f27cSSam Leffler * The RSSI and a timestamp are also supplied. The RSSI data is used 469b032f27cSSam Leffler * during AP scanning to select a AP to associate with; it can have 470b032f27cSSam Leffler * any units so long as values have consistent units and higher values 471b032f27cSSam Leffler * mean ``better signal''. The receive timestamp is currently not used 472b032f27cSSam Leffler * by the 802.11 layer. 473b032f27cSSam Leffler */ 474b032f27cSSam Leffler static int 475c79f192cSAdrian Chadd hostap_input(struct ieee80211_node *ni, struct mbuf *m, 476c79f192cSAdrian Chadd const struct ieee80211_rx_stats *rxs, int rssi, int nf) 477b032f27cSSam Leffler { 478b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 479b032f27cSSam Leffler struct ieee80211com *ic = ni->ni_ic; 480b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ifp; 481b032f27cSSam Leffler struct ieee80211_frame *wh; 482b032f27cSSam Leffler struct ieee80211_key *key; 483b032f27cSSam Leffler struct ether_header *eh; 4842b80a340SRui Paulo int hdrspace, need_tap = 1; /* mbuf need to be tapped. */ 485b032f27cSSam Leffler uint8_t dir, type, subtype, qos; 486b032f27cSSam Leffler uint8_t *bssid; 487fe75b452SAdrian Chadd int is_hw_decrypted = 0; 488fe75b452SAdrian Chadd int has_decrypted = 0; 489fe75b452SAdrian Chadd 490fe75b452SAdrian Chadd /* 491fe75b452SAdrian Chadd * Some devices do hardware decryption all the way through 492fe75b452SAdrian Chadd * to pretending the frame wasn't encrypted in the first place. 493fe75b452SAdrian Chadd * So, tag it appropriately so it isn't discarded inappropriately. 494fe75b452SAdrian Chadd */ 495fe75b452SAdrian Chadd if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED)) 496fe75b452SAdrian Chadd is_hw_decrypted = 1; 497b032f27cSSam Leffler 49845f856e3SSam Leffler if (m->m_flags & M_AMPDU_MPDU) { 499b032f27cSSam Leffler /* 500b032f27cSSam Leffler * Fastpath for A-MPDU reorder q resubmission. Frames 50145f856e3SSam Leffler * w/ M_AMPDU_MPDU marked have already passed through 50245f856e3SSam Leffler * here but were received out of order and been held on 50345f856e3SSam Leffler * the reorder queue. When resubmitted they are marked 50445f856e3SSam Leffler * with the M_AMPDU_MPDU flag and we can bypass most of 50545f856e3SSam Leffler * the normal processing. 506b032f27cSSam Leffler */ 507b032f27cSSam Leffler wh = mtod(m, struct ieee80211_frame *); 508b032f27cSSam Leffler type = IEEE80211_FC0_TYPE_DATA; 509b032f27cSSam Leffler dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 510c9b7e9dfSBjoern A. Zeeb subtype = IEEE80211_FC0_SUBTYPE_QOS_DATA; 511b032f27cSSam Leffler hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ 512b032f27cSSam Leffler goto resubmit_ampdu; 513b032f27cSSam Leffler } 514b032f27cSSam Leffler 515b032f27cSSam Leffler KASSERT(ni != NULL, ("null node")); 516b032f27cSSam Leffler ni->ni_inact = ni->ni_inact_reload; 517b032f27cSSam Leffler 518b032f27cSSam Leffler type = -1; /* undefined */ 519b032f27cSSam Leffler 520b032f27cSSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { 521b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 522b032f27cSSam Leffler ni->ni_macaddr, NULL, 523b032f27cSSam Leffler "too short (1): len %u", m->m_pkthdr.len); 524b032f27cSSam Leffler vap->iv_stats.is_rx_tooshort++; 525b032f27cSSam Leffler goto out; 526b032f27cSSam Leffler } 527b032f27cSSam Leffler /* 528b032f27cSSam Leffler * Bit of a cheat here, we use a pointer for a 3-address 529b032f27cSSam Leffler * frame format but don't reference fields past outside 530b032f27cSSam Leffler * ieee80211_frame_min w/o first validating the data is 531b032f27cSSam Leffler * present. 532b032f27cSSam Leffler */ 533b032f27cSSam Leffler wh = mtod(m, struct ieee80211_frame *); 534b032f27cSSam Leffler 535*37e54466SAdrian Chadd if (!IEEE80211_IS_FC0_CHECK_VER(wh, IEEE80211_FC0_VERSION_0)) { 536b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 537dc7bf546SSam Leffler ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x", 538dc7bf546SSam Leffler wh->i_fc[0], wh->i_fc[1]); 539b032f27cSSam Leffler vap->iv_stats.is_rx_badversion++; 540b032f27cSSam Leffler goto err; 541b032f27cSSam Leffler } 542b032f27cSSam Leffler 543b032f27cSSam Leffler dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 544b032f27cSSam Leffler type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 545b032f27cSSam Leffler subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 546b032f27cSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 547b032f27cSSam Leffler if (dir != IEEE80211_FC1_DIR_NODS) 548b032f27cSSam Leffler bssid = wh->i_addr1; 549b032f27cSSam Leffler else if (type == IEEE80211_FC0_TYPE_CTL) 550b032f27cSSam Leffler bssid = wh->i_addr1; 551b032f27cSSam Leffler else { 552b032f27cSSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 553b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, 554b032f27cSSam Leffler IEEE80211_MSG_ANY, ni->ni_macaddr, 555b032f27cSSam Leffler NULL, "too short (2): len %u", 556b032f27cSSam Leffler m->m_pkthdr.len); 557b032f27cSSam Leffler vap->iv_stats.is_rx_tooshort++; 558b032f27cSSam Leffler goto out; 559b032f27cSSam Leffler } 560b032f27cSSam Leffler bssid = wh->i_addr3; 561b032f27cSSam Leffler } 562b032f27cSSam Leffler /* 563b032f27cSSam Leffler * Validate the bssid. 564b032f27cSSam Leffler */ 565b032f27cSSam Leffler if (!(type == IEEE80211_FC0_TYPE_MGT && 566b032f27cSSam Leffler subtype == IEEE80211_FC0_SUBTYPE_BEACON) && 567b032f27cSSam Leffler !IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && 568b032f27cSSam Leffler !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { 569b032f27cSSam Leffler /* not interested in */ 570b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 571b032f27cSSam Leffler bssid, NULL, "%s", "not to bss"); 572b032f27cSSam Leffler vap->iv_stats.is_rx_wrongbss++; 573b032f27cSSam Leffler goto out; 574b032f27cSSam Leffler } 575b032f27cSSam Leffler 576b032f27cSSam Leffler IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 5775463c4a4SSam Leffler ni->ni_noise = nf; 578c3ebe019SAdrian Chadd if (IEEE80211_HAS_SEQ(type, subtype)) { 579b032f27cSSam Leffler uint8_t tid = ieee80211_gettid(wh); 580b032f27cSSam Leffler if (IEEE80211_QOS_HAS_SEQ(wh) && 581b032f27cSSam Leffler TID_TO_WME_AC(tid) >= WME_AC_VI) 582b032f27cSSam Leffler ic->ic_wme.wme_hipri_traffic++; 58385c4e670SAdrian Chadd if (! ieee80211_check_rxseq(ni, wh, bssid, rxs)) 584b032f27cSSam Leffler goto out; 585b032f27cSSam Leffler } 586b032f27cSSam Leffler } 587b032f27cSSam Leffler 588b032f27cSSam Leffler switch (type) { 589b032f27cSSam Leffler case IEEE80211_FC0_TYPE_DATA: 590b032f27cSSam Leffler hdrspace = ieee80211_hdrspace(ic, wh); 591b032f27cSSam Leffler if (m->m_len < hdrspace && 592b032f27cSSam Leffler (m = m_pullup(m, hdrspace)) == NULL) { 593b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 594b032f27cSSam Leffler ni->ni_macaddr, NULL, 595b032f27cSSam Leffler "data too short: expecting %u", hdrspace); 596b032f27cSSam Leffler vap->iv_stats.is_rx_tooshort++; 597b032f27cSSam Leffler goto out; /* XXX */ 598b032f27cSSam Leffler } 599b032f27cSSam Leffler if (!(dir == IEEE80211_FC1_DIR_TODS || 600b032f27cSSam Leffler (dir == IEEE80211_FC1_DIR_DSTODS && 601b032f27cSSam Leffler (vap->iv_flags & IEEE80211_F_DWDS)))) { 602b032f27cSSam Leffler if (dir != IEEE80211_FC1_DIR_DSTODS) { 603b032f27cSSam Leffler IEEE80211_DISCARD(vap, 604b032f27cSSam Leffler IEEE80211_MSG_INPUT, wh, "data", 605b032f27cSSam Leffler "incorrect dir 0x%x", dir); 606b032f27cSSam Leffler } else { 607b032f27cSSam Leffler IEEE80211_DISCARD(vap, 608b032f27cSSam Leffler IEEE80211_MSG_INPUT | 609b032f27cSSam Leffler IEEE80211_MSG_WDS, wh, 610b032f27cSSam Leffler "4-address data", 611b032f27cSSam Leffler "%s", "DWDS not enabled"); 612b032f27cSSam Leffler } 613b032f27cSSam Leffler vap->iv_stats.is_rx_wrongdir++; 614b032f27cSSam Leffler goto out; 615b032f27cSSam Leffler } 616b032f27cSSam Leffler /* check if source STA is associated */ 617b032f27cSSam Leffler if (ni == vap->iv_bss) { 618b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 619b032f27cSSam Leffler wh, "data", "%s", "unknown src"); 620b032f27cSSam Leffler ieee80211_send_error(ni, wh->i_addr2, 621b032f27cSSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 622b032f27cSSam Leffler IEEE80211_REASON_NOT_AUTHED); 623b032f27cSSam Leffler vap->iv_stats.is_rx_notassoc++; 624b032f27cSSam Leffler goto err; 625b032f27cSSam Leffler } 626b032f27cSSam Leffler if (ni->ni_associd == 0) { 627b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 628b032f27cSSam Leffler wh, "data", "%s", "unassoc src"); 629b032f27cSSam Leffler IEEE80211_SEND_MGMT(ni, 630b032f27cSSam Leffler IEEE80211_FC0_SUBTYPE_DISASSOC, 631b032f27cSSam Leffler IEEE80211_REASON_NOT_ASSOCED); 632b032f27cSSam Leffler vap->iv_stats.is_rx_notassoc++; 633b032f27cSSam Leffler goto err; 634b032f27cSSam Leffler } 635b032f27cSSam Leffler 636b032f27cSSam Leffler /* 637b032f27cSSam Leffler * Check for power save state change. 638b032f27cSSam Leffler * XXX out-of-order A-MPDU frames? 639b032f27cSSam Leffler */ 640b032f27cSSam Leffler if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ 641b032f27cSSam Leffler (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) 642e7f0d7cfSAdrian Chadd vap->iv_node_ps(ni, 643b032f27cSSam Leffler wh->i_fc[1] & IEEE80211_FC1_PWR_MGT); 644b032f27cSSam Leffler /* 645b032f27cSSam Leffler * For 4-address packets handle WDS discovery 646b032f27cSSam Leffler * notifications. Once a WDS link is setup frames 647b032f27cSSam Leffler * are just delivered to the WDS vap (see below). 648b032f27cSSam Leffler */ 649b032f27cSSam Leffler if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap == NULL) { 650b032f27cSSam Leffler if (!ieee80211_node_is_authorized(ni)) { 651b032f27cSSam Leffler IEEE80211_DISCARD(vap, 652b032f27cSSam Leffler IEEE80211_MSG_INPUT | 653b032f27cSSam Leffler IEEE80211_MSG_WDS, wh, 654b032f27cSSam Leffler "4-address data", 655b032f27cSSam Leffler "%s", "unauthorized port"); 656b032f27cSSam Leffler vap->iv_stats.is_rx_unauth++; 657b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_unauth); 658b032f27cSSam Leffler goto err; 659b032f27cSSam Leffler } 660b032f27cSSam Leffler ieee80211_dwds_discover(ni, m); 661b032f27cSSam Leffler return type; 662b032f27cSSam Leffler } 663b032f27cSSam Leffler 664b032f27cSSam Leffler /* 66545f856e3SSam Leffler * Handle A-MPDU re-ordering. If the frame is to be 66645f856e3SSam Leffler * processed directly then ieee80211_ampdu_reorder 667b032f27cSSam Leffler * will return 0; otherwise it has consumed the mbuf 668b032f27cSSam Leffler * and we should do nothing more with it. 669b032f27cSSam Leffler */ 67045f856e3SSam Leffler if ((m->m_flags & M_AMPDU) && 67185c4e670SAdrian Chadd ieee80211_ampdu_reorder(ni, m, rxs) != 0) { 672b032f27cSSam Leffler m = NULL; 673b032f27cSSam Leffler goto out; 674b032f27cSSam Leffler } 675b032f27cSSam Leffler resubmit_ampdu: 676b032f27cSSam Leffler 677b032f27cSSam Leffler /* 678b032f27cSSam Leffler * Handle privacy requirements. Note that we 679b032f27cSSam Leffler * must not be preempted from here until after 680b032f27cSSam Leffler * we (potentially) call ieee80211_crypto_demic; 681b032f27cSSam Leffler * otherwise we may violate assumptions in the 682b032f27cSSam Leffler * crypto cipher modules used to do delayed update 683b032f27cSSam Leffler * of replay sequence numbers. 684b032f27cSSam Leffler */ 6852889cbe2SAdrian Chadd if (is_hw_decrypted || IEEE80211_IS_PROTECTED(wh)) { 686b032f27cSSam Leffler if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 687b032f27cSSam Leffler /* 688b032f27cSSam Leffler * Discard encrypted frames when privacy is off. 689b032f27cSSam Leffler */ 690b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 691b032f27cSSam Leffler wh, "WEP", "%s", "PRIVACY off"); 692b032f27cSSam Leffler vap->iv_stats.is_rx_noprivacy++; 693b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_noprivacy); 694b032f27cSSam Leffler goto out; 695b032f27cSSam Leffler } 696fe75b452SAdrian Chadd if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) { 697b032f27cSSam Leffler /* NB: stats+msgs handled in crypto_decap */ 698b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_wepfail); 699b032f27cSSam Leffler goto out; 700b032f27cSSam Leffler } 701b032f27cSSam Leffler wh = mtod(m, struct ieee80211_frame *); 7025945b5f5SKevin Lo wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; 703fe75b452SAdrian Chadd has_decrypted = 1; 704b032f27cSSam Leffler } else { 705b032f27cSSam Leffler /* XXX M_WEP and IEEE80211_F_PRIVACY */ 706b032f27cSSam Leffler key = NULL; 707b032f27cSSam Leffler } 708b032f27cSSam Leffler 709b032f27cSSam Leffler /* 710b032f27cSSam Leffler * Save QoS bits for use below--before we strip the header. 711b032f27cSSam Leffler */ 712c9b7e9dfSBjoern A. Zeeb if (subtype == IEEE80211_FC0_SUBTYPE_QOS_DATA) 713f3f08e16SAndriy Voskoboinyk qos = ieee80211_getqos(wh)[0]; 714f3f08e16SAndriy Voskoboinyk else 715b032f27cSSam Leffler qos = 0; 716b032f27cSSam Leffler 717b032f27cSSam Leffler /* 718b032f27cSSam Leffler * Next up, any fragmentation. 719b032f27cSSam Leffler */ 720b032f27cSSam Leffler if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 72111572d7dSMathy Vanhoef m = ieee80211_defrag(ni, m, hdrspace, has_decrypted); 722b032f27cSSam Leffler if (m == NULL) { 723b032f27cSSam Leffler /* Fragment dropped or frame not complete yet */ 724b032f27cSSam Leffler goto out; 725b032f27cSSam Leffler } 726b032f27cSSam Leffler } 727b032f27cSSam Leffler wh = NULL; /* no longer valid, catch any uses */ 728b032f27cSSam Leffler 729b032f27cSSam Leffler /* 730b032f27cSSam Leffler * Next strip any MSDU crypto bits. 731b032f27cSSam Leffler */ 732b032f27cSSam Leffler if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) { 733b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 734b032f27cSSam Leffler ni->ni_macaddr, "data", "%s", "demic error"); 735b032f27cSSam Leffler vap->iv_stats.is_rx_demicfail++; 736b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_demicfail); 737b032f27cSSam Leffler goto out; 738b032f27cSSam Leffler } 739b032f27cSSam Leffler /* copy to listener after decrypt */ 7405463c4a4SSam Leffler if (ieee80211_radiotap_active_vap(vap)) 7415463c4a4SSam Leffler ieee80211_radiotap_rx(vap, m); 742b032f27cSSam Leffler need_tap = 0; 743b032f27cSSam Leffler /* 744b032f27cSSam Leffler * Finally, strip the 802.11 header. 745b032f27cSSam Leffler */ 746f024bdf1SMathy Vanhoef m = ieee80211_decap(vap, m, hdrspace, qos); 747b032f27cSSam Leffler if (m == NULL) { 748b032f27cSSam Leffler /* XXX mask bit to check for both */ 749b032f27cSSam Leffler /* don't count Null data frames as errors */ 750b032f27cSSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || 751b032f27cSSam Leffler subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) 752b032f27cSSam Leffler goto out; 753b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 754b032f27cSSam Leffler ni->ni_macaddr, "data", "%s", "decap error"); 755b032f27cSSam Leffler vap->iv_stats.is_rx_decap++; 756b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_decap); 757b032f27cSSam Leffler goto err; 758b032f27cSSam Leffler } 759ffc19cf5SMathy Vanhoef if (!(qos & IEEE80211_QOS_AMSDU)) 760b032f27cSSam Leffler eh = mtod(m, struct ether_header *); 761ffc19cf5SMathy Vanhoef else 762ffc19cf5SMathy Vanhoef eh = NULL; 763b032f27cSSam Leffler if (!ieee80211_node_is_authorized(ni)) { 764b032f27cSSam Leffler /* 765b032f27cSSam Leffler * Deny any non-PAE frames received prior to 766b032f27cSSam Leffler * authorization. For open/shared-key 767b032f27cSSam Leffler * authentication the port is mark authorized 768b032f27cSSam Leffler * after authentication completes. For 802.1x 769b032f27cSSam Leffler * the port is not marked authorized by the 770b032f27cSSam Leffler * authenticator until the handshake has completed. 771b032f27cSSam Leffler */ 772ffc19cf5SMathy Vanhoef if (eh == NULL || 773ffc19cf5SMathy Vanhoef eh->ether_type != htons(ETHERTYPE_PAE)) { 774b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 775ffc19cf5SMathy Vanhoef ni->ni_macaddr, "data", "unauthorized or " 776ffc19cf5SMathy Vanhoef "unknown port: ether type 0x%x len %u", 777ffc19cf5SMathy Vanhoef eh == NULL ? -1 : eh->ether_type, 778ffc19cf5SMathy Vanhoef m->m_pkthdr.len); 779b032f27cSSam Leffler vap->iv_stats.is_rx_unauth++; 780b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_unauth); 781b032f27cSSam Leffler goto err; 782b032f27cSSam Leffler } 783b032f27cSSam Leffler } else { 784b032f27cSSam Leffler /* 785b032f27cSSam Leffler * When denying unencrypted frames, discard 786b032f27cSSam Leffler * any non-PAE frames received without encryption. 787b032f27cSSam Leffler */ 788b032f27cSSam Leffler if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && 789fe75b452SAdrian Chadd ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) && 790fe75b452SAdrian Chadd (is_hw_decrypted == 0) && 791ffc19cf5SMathy Vanhoef (eh == NULL || 792ffc19cf5SMathy Vanhoef eh->ether_type != htons(ETHERTYPE_PAE))) { 793b032f27cSSam Leffler /* 794b032f27cSSam Leffler * Drop unencrypted frames. 795b032f27cSSam Leffler */ 796b032f27cSSam Leffler vap->iv_stats.is_rx_unencrypted++; 797b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_unencrypted); 798b032f27cSSam Leffler goto out; 799b032f27cSSam Leffler } 800b032f27cSSam Leffler } 801b032f27cSSam Leffler /* XXX require HT? */ 802b032f27cSSam Leffler if (qos & IEEE80211_QOS_AMSDU) { 803b032f27cSSam Leffler m = ieee80211_decap_amsdu(ni, m); 804b032f27cSSam Leffler if (m == NULL) 805b032f27cSSam Leffler return IEEE80211_FC0_TYPE_DATA; 806616190d0SSam Leffler } else { 807616190d0SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 808616190d0SSam Leffler m = ieee80211_decap_fastframe(vap, ni, m); 809b032f27cSSam Leffler if (m == NULL) 810b032f27cSSam Leffler return IEEE80211_FC0_TYPE_DATA; 811616190d0SSam Leffler #endif 812b032f27cSSam Leffler } 813b032f27cSSam Leffler if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL) 814b032f27cSSam Leffler ieee80211_deliver_data(ni->ni_wdsvap, ni, m); 815b032f27cSSam Leffler else 816b032f27cSSam Leffler hostap_deliver_data(vap, ni, m); 817b032f27cSSam Leffler return IEEE80211_FC0_TYPE_DATA; 818b032f27cSSam Leffler 819b032f27cSSam Leffler case IEEE80211_FC0_TYPE_MGT: 820b032f27cSSam Leffler vap->iv_stats.is_rx_mgmt++; 821b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_mgmt); 822b032f27cSSam Leffler if (dir != IEEE80211_FC1_DIR_NODS) { 823b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 824b032f27cSSam Leffler wh, "mgt", "incorrect dir 0x%x", dir); 825b032f27cSSam Leffler vap->iv_stats.is_rx_wrongdir++; 826b032f27cSSam Leffler goto err; 827b032f27cSSam Leffler } 828b032f27cSSam Leffler if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 829b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 830b032f27cSSam Leffler ni->ni_macaddr, "mgt", "too short: len %u", 831b032f27cSSam Leffler m->m_pkthdr.len); 832b032f27cSSam Leffler vap->iv_stats.is_rx_tooshort++; 833b032f27cSSam Leffler goto out; 834b032f27cSSam Leffler } 835b032f27cSSam Leffler if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { 836b032f27cSSam Leffler /* ensure return frames are unicast */ 837b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 838b032f27cSSam Leffler wh, NULL, "source is multicast: %s", 839b032f27cSSam Leffler ether_sprintf(wh->i_addr2)); 840b032f27cSSam Leffler vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ 841b032f27cSSam Leffler goto out; 842b032f27cSSam Leffler } 843b032f27cSSam Leffler #ifdef IEEE80211_DEBUG 844b032f27cSSam Leffler if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || 845b032f27cSSam Leffler ieee80211_msg_dumppkts(vap)) { 846b032f27cSSam Leffler if_printf(ifp, "received %s from %s rssi %d\n", 8474357a5d1SAndriy Voskoboinyk ieee80211_mgt_subtype_name(subtype), 848b032f27cSSam Leffler ether_sprintf(wh->i_addr2), rssi); 849b032f27cSSam Leffler } 850b032f27cSSam Leffler #endif 8512889cbe2SAdrian Chadd if (IEEE80211_IS_PROTECTED(wh)) { 852b032f27cSSam Leffler if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { 853b032f27cSSam Leffler /* 854b032f27cSSam Leffler * Only shared key auth frames with a challenge 855b032f27cSSam Leffler * should be encrypted, discard all others. 856b032f27cSSam Leffler */ 857b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 858b032f27cSSam Leffler wh, NULL, 859b032f27cSSam Leffler "%s", "WEP set but not permitted"); 860b032f27cSSam Leffler vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ 861b032f27cSSam Leffler goto out; 862b032f27cSSam Leffler } 863b032f27cSSam Leffler if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 864b032f27cSSam Leffler /* 865b032f27cSSam Leffler * Discard encrypted frames when privacy is off. 866b032f27cSSam Leffler */ 867b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 868b032f27cSSam Leffler wh, NULL, "%s", "WEP set but PRIVACY off"); 869b032f27cSSam Leffler vap->iv_stats.is_rx_noprivacy++; 870b032f27cSSam Leffler goto out; 871b032f27cSSam Leffler } 872b032f27cSSam Leffler hdrspace = ieee80211_hdrspace(ic, wh); 873fe75b452SAdrian Chadd if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) { 874b032f27cSSam Leffler /* NB: stats+msgs handled in crypto_decap */ 875b032f27cSSam Leffler goto out; 876b032f27cSSam Leffler } 877b032f27cSSam Leffler wh = mtod(m, struct ieee80211_frame *); 8785945b5f5SKevin Lo wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; 879fe75b452SAdrian Chadd has_decrypted = 1; 880b032f27cSSam Leffler } 8814e87d54aSRui Paulo /* 8824e87d54aSRui Paulo * Pass the packet to radiotap before calling iv_recv_mgmt(). 8834e87d54aSRui Paulo * Otherwise iv_recv_mgmt() might pass another packet to 8844e87d54aSRui Paulo * radiotap, resulting in out of order packet captures. 8854e87d54aSRui Paulo */ 886323f12abSRui Paulo if (ieee80211_radiotap_active_vap(vap)) 887323f12abSRui Paulo ieee80211_radiotap_rx(vap, m); 888323f12abSRui Paulo need_tap = 0; 889c79f192cSAdrian Chadd vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); 8908bbd3e41SSam Leffler goto out; 891b032f27cSSam Leffler 892b032f27cSSam Leffler case IEEE80211_FC0_TYPE_CTL: 893b032f27cSSam Leffler vap->iv_stats.is_rx_ctl++; 894b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_ctrl); 89549eae5f7SSam Leffler vap->iv_recv_ctl(ni, m, subtype); 896b032f27cSSam Leffler goto out; 897b032f27cSSam Leffler default: 898b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 899b032f27cSSam Leffler wh, "bad", "frame type 0x%x", type); 900b032f27cSSam Leffler /* should not come here */ 901b032f27cSSam Leffler break; 902b032f27cSSam Leffler } 903b032f27cSSam Leffler err: 904dea45121SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 905b032f27cSSam Leffler out: 906b032f27cSSam Leffler if (m != NULL) { 907a6c3cf3eSSam Leffler if (need_tap && ieee80211_radiotap_active_vap(vap)) 9085463c4a4SSam Leffler ieee80211_radiotap_rx(vap, m); 909b032f27cSSam Leffler m_freem(m); 910b032f27cSSam Leffler } 911b032f27cSSam Leffler return type; 912b032f27cSSam Leffler } 913b032f27cSSam Leffler 914b032f27cSSam Leffler static void 915b032f27cSSam Leffler hostap_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh, 9165463c4a4SSam Leffler int rssi, int nf, uint16_t seq, uint16_t status) 917b032f27cSSam Leffler { 918b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 919b032f27cSSam Leffler 920b032f27cSSam Leffler KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state)); 921b032f27cSSam Leffler 922b032f27cSSam Leffler if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { 923b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 924b032f27cSSam Leffler ni->ni_macaddr, "open auth", 925b032f27cSSam Leffler "bad sta auth mode %u", ni->ni_authmode); 926b032f27cSSam Leffler vap->iv_stats.is_rx_bad_auth++; /* XXX */ 927b032f27cSSam Leffler /* 928b032f27cSSam Leffler * Clear any challenge text that may be there if 929b032f27cSSam Leffler * a previous shared key auth failed and then an 930b032f27cSSam Leffler * open auth is attempted. 931b032f27cSSam Leffler */ 932b032f27cSSam Leffler if (ni->ni_challenge != NULL) { 933b9b53389SAdrian Chadd IEEE80211_FREE(ni->ni_challenge, M_80211_NODE); 934b032f27cSSam Leffler ni->ni_challenge = NULL; 935b032f27cSSam Leffler } 936b032f27cSSam Leffler /* XXX hack to workaround calling convention */ 937b032f27cSSam Leffler ieee80211_send_error(ni, wh->i_addr2, 938b032f27cSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 939b032f27cSSam Leffler (seq + 1) | (IEEE80211_STATUS_ALG<<16)); 940b032f27cSSam Leffler return; 941b032f27cSSam Leffler } 942b032f27cSSam Leffler if (seq != IEEE80211_AUTH_OPEN_REQUEST) { 943b032f27cSSam Leffler vap->iv_stats.is_rx_bad_auth++; 944b032f27cSSam Leffler return; 945b032f27cSSam Leffler } 946b032f27cSSam Leffler /* always accept open authentication requests */ 947b032f27cSSam Leffler if (ni == vap->iv_bss) { 948b032f27cSSam Leffler ni = ieee80211_dup_bss(vap, wh->i_addr2); 949b032f27cSSam Leffler if (ni == NULL) 950b032f27cSSam Leffler return; 951b032f27cSSam Leffler } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) 952b032f27cSSam Leffler (void) ieee80211_ref_node(ni); 953b032f27cSSam Leffler /* 954b032f27cSSam Leffler * Mark the node as referenced to reflect that it's 955b032f27cSSam Leffler * reference count has been bumped to insure it remains 956b032f27cSSam Leffler * after the transaction completes. 957b032f27cSSam Leffler */ 958b032f27cSSam Leffler ni->ni_flags |= IEEE80211_NODE_AREF; 9591b999d64SSam Leffler /* 96012c290feSSam Leffler * Mark the node as requiring a valid association id 9611b999d64SSam Leffler * before outbound traffic is permitted. 9621b999d64SSam Leffler */ 9631b999d64SSam Leffler ni->ni_flags |= IEEE80211_NODE_ASSOCID; 964b032f27cSSam Leffler 965b032f27cSSam Leffler if (vap->iv_acl != NULL && 966b032f27cSSam Leffler vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) { 967b032f27cSSam Leffler /* 968b032f27cSSam Leffler * When the ACL policy is set to RADIUS we defer the 969b032f27cSSam Leffler * authorization to a user agent. Dispatch an event, 970b032f27cSSam Leffler * a subsequent MLME call will decide the fate of the 971b032f27cSSam Leffler * station. If the user agent is not present then the 972b032f27cSSam Leffler * node will be reclaimed due to inactivity. 973b032f27cSSam Leffler */ 974b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, 975b032f27cSSam Leffler IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, ni->ni_macaddr, 97694fc800fSGordon Bergling "%s", "station authentication deferred (radius acl)"); 977b032f27cSSam Leffler ieee80211_notify_node_auth(ni); 978b032f27cSSam Leffler } else { 979b032f27cSSam Leffler IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 980b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, 981b032f27cSSam Leffler IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni->ni_macaddr, 982b032f27cSSam Leffler "%s", "station authenticated (open)"); 983b032f27cSSam Leffler /* 984b032f27cSSam Leffler * When 802.1x is not in use mark the port 985b032f27cSSam Leffler * authorized at this point so traffic can flow. 986b032f27cSSam Leffler */ 987b032f27cSSam Leffler if (ni->ni_authmode != IEEE80211_AUTH_8021X) 988b032f27cSSam Leffler ieee80211_node_authorize(ni); 989b032f27cSSam Leffler } 990b032f27cSSam Leffler } 991b032f27cSSam Leffler 992b032f27cSSam Leffler static void 993b032f27cSSam Leffler hostap_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh, 9945463c4a4SSam Leffler uint8_t *frm, uint8_t *efrm, int rssi, int nf, 995b032f27cSSam Leffler uint16_t seq, uint16_t status) 996b032f27cSSam Leffler { 997b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 998b032f27cSSam Leffler uint8_t *challenge; 99905ea7a3eSBjoern A. Zeeb int estatus; 1000b032f27cSSam Leffler 1001b032f27cSSam Leffler KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state)); 1002b032f27cSSam Leffler 1003b032f27cSSam Leffler /* 1004b032f27cSSam Leffler * NB: this can happen as we allow pre-shared key 1005b032f27cSSam Leffler * authentication to be enabled w/o wep being turned 1006b032f27cSSam Leffler * on so that configuration of these can be done 1007b032f27cSSam Leffler * in any order. It may be better to enforce the 1008b032f27cSSam Leffler * ordering in which case this check would just be 1009b032f27cSSam Leffler * for sanity/consistency. 1010b032f27cSSam Leffler */ 1011b032f27cSSam Leffler if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 1012b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1013b032f27cSSam Leffler ni->ni_macaddr, "shared key auth", 1014b032f27cSSam Leffler "%s", " PRIVACY is disabled"); 1015b032f27cSSam Leffler estatus = IEEE80211_STATUS_ALG; 1016b032f27cSSam Leffler goto bad; 1017b032f27cSSam Leffler } 1018b032f27cSSam Leffler /* 1019b032f27cSSam Leffler * Pre-shared key authentication is evil; accept 1020b032f27cSSam Leffler * it only if explicitly configured (it is supported 1021b032f27cSSam Leffler * mainly for compatibility with clients like Mac OS X). 1022b032f27cSSam Leffler */ 1023b032f27cSSam Leffler if (ni->ni_authmode != IEEE80211_AUTH_AUTO && 1024b032f27cSSam Leffler ni->ni_authmode != IEEE80211_AUTH_SHARED) { 1025b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1026b032f27cSSam Leffler ni->ni_macaddr, "shared key auth", 1027b032f27cSSam Leffler "bad sta auth mode %u", ni->ni_authmode); 1028b032f27cSSam Leffler vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ 1029b032f27cSSam Leffler estatus = IEEE80211_STATUS_ALG; 1030b032f27cSSam Leffler goto bad; 1031b032f27cSSam Leffler } 1032b032f27cSSam Leffler 1033b032f27cSSam Leffler challenge = NULL; 1034b032f27cSSam Leffler if (frm + 1 < efrm) { 1035b032f27cSSam Leffler if ((frm[1] + 2) > (efrm - frm)) { 1036b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1037b032f27cSSam Leffler ni->ni_macaddr, "shared key auth", 1038b032f27cSSam Leffler "ie %d/%d too long", 1039b032f27cSSam Leffler frm[0], (frm[1] + 2) - (efrm - frm)); 1040b032f27cSSam Leffler vap->iv_stats.is_rx_bad_auth++; 1041b032f27cSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 1042b032f27cSSam Leffler goto bad; 1043b032f27cSSam Leffler } 1044b032f27cSSam Leffler if (*frm == IEEE80211_ELEMID_CHALLENGE) 1045b032f27cSSam Leffler challenge = frm; 1046b032f27cSSam Leffler frm += frm[1] + 2; 1047b032f27cSSam Leffler } 1048b032f27cSSam Leffler switch (seq) { 1049b032f27cSSam Leffler case IEEE80211_AUTH_SHARED_CHALLENGE: 1050b032f27cSSam Leffler case IEEE80211_AUTH_SHARED_RESPONSE: 1051b032f27cSSam Leffler if (challenge == NULL) { 1052b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1053b032f27cSSam Leffler ni->ni_macaddr, "shared key auth", 1054b032f27cSSam Leffler "%s", "no challenge"); 1055b032f27cSSam Leffler vap->iv_stats.is_rx_bad_auth++; 1056b032f27cSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 1057b032f27cSSam Leffler goto bad; 1058b032f27cSSam Leffler } 1059b032f27cSSam Leffler if (challenge[1] != IEEE80211_CHALLENGE_LEN) { 1060b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1061b032f27cSSam Leffler ni->ni_macaddr, "shared key auth", 1062b032f27cSSam Leffler "bad challenge len %d", challenge[1]); 1063b032f27cSSam Leffler vap->iv_stats.is_rx_bad_auth++; 1064b032f27cSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 1065b032f27cSSam Leffler goto bad; 1066b032f27cSSam Leffler } 1067b032f27cSSam Leffler default: 1068b032f27cSSam Leffler break; 1069b032f27cSSam Leffler } 1070b032f27cSSam Leffler switch (seq) { 1071b032f27cSSam Leffler case IEEE80211_AUTH_SHARED_REQUEST: 107205ea7a3eSBjoern A. Zeeb { 107305ea7a3eSBjoern A. Zeeb #ifdef IEEE80211_DEBUG 107405ea7a3eSBjoern A. Zeeb bool allocbs; 107505ea7a3eSBjoern A. Zeeb #endif 107605ea7a3eSBjoern A. Zeeb 1077b032f27cSSam Leffler if (ni == vap->iv_bss) { 1078b032f27cSSam Leffler ni = ieee80211_dup_bss(vap, wh->i_addr2); 1079b032f27cSSam Leffler if (ni == NULL) { 1080b032f27cSSam Leffler /* NB: no way to return an error */ 1081b032f27cSSam Leffler return; 1082b032f27cSSam Leffler } 108305ea7a3eSBjoern A. Zeeb #ifdef IEEE80211_DEBUG 1084b032f27cSSam Leffler allocbs = 1; 108505ea7a3eSBjoern A. Zeeb #endif 1086b032f27cSSam Leffler } else { 1087b032f27cSSam Leffler if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) 1088b032f27cSSam Leffler (void) ieee80211_ref_node(ni); 108905ea7a3eSBjoern A. Zeeb #ifdef IEEE80211_DEBUG 1090b032f27cSSam Leffler allocbs = 0; 109105ea7a3eSBjoern A. Zeeb #endif 1092b032f27cSSam Leffler } 1093b032f27cSSam Leffler /* 1094b032f27cSSam Leffler * Mark the node as referenced to reflect that it's 1095b032f27cSSam Leffler * reference count has been bumped to insure it remains 1096b032f27cSSam Leffler * after the transaction completes. 1097b032f27cSSam Leffler */ 1098b032f27cSSam Leffler ni->ni_flags |= IEEE80211_NODE_AREF; 10991b999d64SSam Leffler /* 11006dbbec93SAndriy Voskoboinyk * Mark the node as requiring a valid association id 11011b999d64SSam Leffler * before outbound traffic is permitted. 11021b999d64SSam Leffler */ 11031b999d64SSam Leffler ni->ni_flags |= IEEE80211_NODE_ASSOCID; 1104b032f27cSSam Leffler IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 11055463c4a4SSam Leffler ni->ni_noise = nf; 1106b032f27cSSam Leffler if (!ieee80211_alloc_challenge(ni)) { 1107b032f27cSSam Leffler /* NB: don't return error so they rexmit */ 1108b032f27cSSam Leffler return; 1109b032f27cSSam Leffler } 1110af7d9f8eSBjoern A. Zeeb net80211_get_random_bytes(ni->ni_challenge, 1111b032f27cSSam Leffler IEEE80211_CHALLENGE_LEN); 1112b032f27cSSam Leffler IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 1113b032f27cSSam Leffler ni, "shared key %sauth request", allocbs ? "" : "re"); 1114b032f27cSSam Leffler /* 1115b032f27cSSam Leffler * When the ACL policy is set to RADIUS we defer the 1116b032f27cSSam Leffler * authorization to a user agent. Dispatch an event, 1117b032f27cSSam Leffler * a subsequent MLME call will decide the fate of the 1118b032f27cSSam Leffler * station. If the user agent is not present then the 1119b032f27cSSam Leffler * node will be reclaimed due to inactivity. 1120b032f27cSSam Leffler */ 1121b032f27cSSam Leffler if (vap->iv_acl != NULL && 1122b032f27cSSam Leffler vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) { 1123b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, 1124b032f27cSSam Leffler IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, 1125b032f27cSSam Leffler ni->ni_macaddr, 112694fc800fSGordon Bergling "%s", "station authentication deferred (radius acl)"); 1127b032f27cSSam Leffler ieee80211_notify_node_auth(ni); 1128b032f27cSSam Leffler return; 1129b032f27cSSam Leffler } 1130b032f27cSSam Leffler break; 113105ea7a3eSBjoern A. Zeeb } 1132b032f27cSSam Leffler case IEEE80211_AUTH_SHARED_RESPONSE: 1133b032f27cSSam Leffler if (ni == vap->iv_bss) { 1134b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1135b032f27cSSam Leffler ni->ni_macaddr, "shared key response", 1136b032f27cSSam Leffler "%s", "unknown station"); 1137b032f27cSSam Leffler /* NB: don't send a response */ 1138b032f27cSSam Leffler return; 1139b032f27cSSam Leffler } 1140b032f27cSSam Leffler if (ni->ni_challenge == NULL) { 1141b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1142b032f27cSSam Leffler ni->ni_macaddr, "shared key response", 1143b032f27cSSam Leffler "%s", "no challenge recorded"); 1144b032f27cSSam Leffler vap->iv_stats.is_rx_bad_auth++; 1145b032f27cSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 1146b032f27cSSam Leffler goto bad; 1147b032f27cSSam Leffler } 1148b032f27cSSam Leffler if (memcmp(ni->ni_challenge, &challenge[2], 1149b032f27cSSam Leffler challenge[1]) != 0) { 1150b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1151b032f27cSSam Leffler ni->ni_macaddr, "shared key response", 1152b032f27cSSam Leffler "%s", "challenge mismatch"); 1153b032f27cSSam Leffler vap->iv_stats.is_rx_auth_fail++; 1154b032f27cSSam Leffler estatus = IEEE80211_STATUS_CHALLENGE; 1155b032f27cSSam Leffler goto bad; 1156b032f27cSSam Leffler } 1157b032f27cSSam Leffler IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 1158b032f27cSSam Leffler ni, "%s", "station authenticated (shared key)"); 1159b032f27cSSam Leffler ieee80211_node_authorize(ni); 1160b032f27cSSam Leffler break; 1161b032f27cSSam Leffler default: 1162b032f27cSSam Leffler IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1163b032f27cSSam Leffler ni->ni_macaddr, "shared key auth", 1164b032f27cSSam Leffler "bad seq %d", seq); 1165b032f27cSSam Leffler vap->iv_stats.is_rx_bad_auth++; 1166b032f27cSSam Leffler estatus = IEEE80211_STATUS_SEQUENCE; 1167b032f27cSSam Leffler goto bad; 1168b032f27cSSam Leffler } 1169b032f27cSSam Leffler IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 1170b032f27cSSam Leffler return; 1171b032f27cSSam Leffler bad: 1172b032f27cSSam Leffler /* 1173b032f27cSSam Leffler * Send an error response; but only when operating as an AP. 1174b032f27cSSam Leffler */ 1175b032f27cSSam Leffler /* XXX hack to workaround calling convention */ 1176b032f27cSSam Leffler ieee80211_send_error(ni, wh->i_addr2, 1177b032f27cSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 1178b032f27cSSam Leffler (seq + 1) | (estatus<<16)); 1179b032f27cSSam Leffler } 1180b032f27cSSam Leffler 1181b032f27cSSam Leffler /* 1182b032f27cSSam Leffler * Convert a WPA cipher selector OUI to an internal 1183b032f27cSSam Leffler * cipher algorithm. Where appropriate we also 1184b032f27cSSam Leffler * record any key length. 1185b032f27cSSam Leffler */ 1186b032f27cSSam Leffler static int 118795d9a127SAndriy Voskoboinyk wpa_cipher(const uint8_t *sel, uint8_t *keylen, uint8_t *cipher) 1188b032f27cSSam Leffler { 1189b032f27cSSam Leffler #define WPA_SEL(x) (((x)<<24)|WPA_OUI) 119031021a2bSAndriy Voskoboinyk uint32_t w = le32dec(sel); 1191b032f27cSSam Leffler 1192b032f27cSSam Leffler switch (w) { 1193b032f27cSSam Leffler case WPA_SEL(WPA_CSE_NULL): 119495d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_NONE; 119595d9a127SAndriy Voskoboinyk break; 1196b032f27cSSam Leffler case WPA_SEL(WPA_CSE_WEP40): 1197b032f27cSSam Leffler if (keylen) 1198b032f27cSSam Leffler *keylen = 40 / NBBY; 119995d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_WEP; 120095d9a127SAndriy Voskoboinyk break; 1201b032f27cSSam Leffler case WPA_SEL(WPA_CSE_WEP104): 1202b032f27cSSam Leffler if (keylen) 1203b032f27cSSam Leffler *keylen = 104 / NBBY; 120495d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_WEP; 120595d9a127SAndriy Voskoboinyk break; 1206b032f27cSSam Leffler case WPA_SEL(WPA_CSE_TKIP): 120795d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_TKIP; 120895d9a127SAndriy Voskoboinyk break; 1209b032f27cSSam Leffler case WPA_SEL(WPA_CSE_CCMP): 121095d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_AES_CCM; 121195d9a127SAndriy Voskoboinyk break; 121295d9a127SAndriy Voskoboinyk default: 121395d9a127SAndriy Voskoboinyk return (EINVAL); 1214b032f27cSSam Leffler } 121595d9a127SAndriy Voskoboinyk 121695d9a127SAndriy Voskoboinyk return (0); 1217b032f27cSSam Leffler #undef WPA_SEL 1218b032f27cSSam Leffler } 1219b032f27cSSam Leffler 1220b032f27cSSam Leffler /* 1221b032f27cSSam Leffler * Convert a WPA key management/authentication algorithm 1222b032f27cSSam Leffler * to an internal code. 1223b032f27cSSam Leffler */ 1224b032f27cSSam Leffler static int 1225b032f27cSSam Leffler wpa_keymgmt(const uint8_t *sel) 1226b032f27cSSam Leffler { 1227b032f27cSSam Leffler #define WPA_SEL(x) (((x)<<24)|WPA_OUI) 122831021a2bSAndriy Voskoboinyk uint32_t w = le32dec(sel); 1229b032f27cSSam Leffler 1230b032f27cSSam Leffler switch (w) { 1231b032f27cSSam Leffler case WPA_SEL(WPA_ASE_8021X_UNSPEC): 1232b032f27cSSam Leffler return WPA_ASE_8021X_UNSPEC; 1233b032f27cSSam Leffler case WPA_SEL(WPA_ASE_8021X_PSK): 1234b032f27cSSam Leffler return WPA_ASE_8021X_PSK; 1235b032f27cSSam Leffler case WPA_SEL(WPA_ASE_NONE): 1236b032f27cSSam Leffler return WPA_ASE_NONE; 1237b032f27cSSam Leffler } 1238b032f27cSSam Leffler return 0; /* NB: so is discarded */ 1239b032f27cSSam Leffler #undef WPA_SEL 1240b032f27cSSam Leffler } 1241b032f27cSSam Leffler 1242b032f27cSSam Leffler /* 1243b032f27cSSam Leffler * Parse a WPA information element to collect parameters. 1244b032f27cSSam Leffler * Note that we do not validate security parameters; that 1245b032f27cSSam Leffler * is handled by the authenticator; the parsing done here 1246b032f27cSSam Leffler * is just for internal use in making operational decisions. 1247b032f27cSSam Leffler */ 1248b032f27cSSam Leffler static int 1249b032f27cSSam Leffler ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm, 1250b032f27cSSam Leffler struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) 1251b032f27cSSam Leffler { 1252b032f27cSSam Leffler uint8_t len = frm[1]; 1253b032f27cSSam Leffler uint32_t w; 125495d9a127SAndriy Voskoboinyk int error, n; 1255b032f27cSSam Leffler 1256b032f27cSSam Leffler /* 1257b032f27cSSam Leffler * Check the length once for fixed parts: OUI, type, 1258b032f27cSSam Leffler * version, mcast cipher, and 2 selector counts. 1259b032f27cSSam Leffler * Other, variable-length data, must be checked separately. 1260b032f27cSSam Leffler */ 1261b032f27cSSam Leffler if ((vap->iv_flags & IEEE80211_F_WPA1) == 0) { 1262b032f27cSSam Leffler IEEE80211_DISCARD_IE(vap, 1263b032f27cSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1264b032f27cSSam Leffler wh, "WPA", "not WPA, flags 0x%x", vap->iv_flags); 1265b032f27cSSam Leffler return IEEE80211_REASON_IE_INVALID; 1266b032f27cSSam Leffler } 1267b032f27cSSam Leffler if (len < 14) { 1268b032f27cSSam Leffler IEEE80211_DISCARD_IE(vap, 1269b032f27cSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1270b032f27cSSam Leffler wh, "WPA", "too short, len %u", len); 1271b032f27cSSam Leffler return IEEE80211_REASON_IE_INVALID; 1272b032f27cSSam Leffler } 1273b032f27cSSam Leffler frm += 6, len -= 4; /* NB: len is payload only */ 1274f59310a1SRui Paulo /* NB: iswpaoui already validated the OUI and type */ 127531021a2bSAndriy Voskoboinyk w = le16dec(frm); 1276b032f27cSSam Leffler if (w != WPA_VERSION) { 1277b032f27cSSam Leffler IEEE80211_DISCARD_IE(vap, 1278b032f27cSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1279b032f27cSSam Leffler wh, "WPA", "bad version %u", w); 1280b032f27cSSam Leffler return IEEE80211_REASON_IE_INVALID; 1281b032f27cSSam Leffler } 1282b032f27cSSam Leffler frm += 2, len -= 2; 1283b032f27cSSam Leffler 1284b032f27cSSam Leffler memset(rsn, 0, sizeof(*rsn)); 1285b032f27cSSam Leffler 1286b032f27cSSam Leffler /* multicast/group cipher */ 128795d9a127SAndriy Voskoboinyk error = wpa_cipher(frm, &rsn->rsn_mcastkeylen, &rsn->rsn_mcastcipher); 128895d9a127SAndriy Voskoboinyk if (error != 0) { 128995d9a127SAndriy Voskoboinyk IEEE80211_DISCARD_IE(vap, 129095d9a127SAndriy Voskoboinyk IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 129195d9a127SAndriy Voskoboinyk wh, "WPA", "unknown mcast cipher suite %08X", 129295d9a127SAndriy Voskoboinyk le32dec(frm)); 129395d9a127SAndriy Voskoboinyk return IEEE80211_REASON_GROUP_CIPHER_INVALID; 129495d9a127SAndriy Voskoboinyk } 1295b032f27cSSam Leffler frm += 4, len -= 4; 1296b032f27cSSam Leffler 1297b032f27cSSam Leffler /* unicast ciphers */ 129831021a2bSAndriy Voskoboinyk n = le16dec(frm); 1299b032f27cSSam Leffler frm += 2, len -= 2; 1300b032f27cSSam Leffler if (len < n*4+2) { 1301b032f27cSSam Leffler IEEE80211_DISCARD_IE(vap, 1302b032f27cSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1303b032f27cSSam Leffler wh, "WPA", "ucast cipher data too short; len %u, n %u", 1304b032f27cSSam Leffler len, n); 1305b032f27cSSam Leffler return IEEE80211_REASON_IE_INVALID; 1306b032f27cSSam Leffler } 1307b032f27cSSam Leffler w = 0; 1308b032f27cSSam Leffler for (; n > 0; n--) { 130995d9a127SAndriy Voskoboinyk uint8_t cipher; 131095d9a127SAndriy Voskoboinyk 131195d9a127SAndriy Voskoboinyk error = wpa_cipher(frm, &rsn->rsn_ucastkeylen, &cipher); 131295d9a127SAndriy Voskoboinyk if (error == 0) 131395d9a127SAndriy Voskoboinyk w |= 1 << cipher; 131495d9a127SAndriy Voskoboinyk 1315b032f27cSSam Leffler frm += 4, len -= 4; 1316b032f27cSSam Leffler } 131795d9a127SAndriy Voskoboinyk if (w == 0) { 131895d9a127SAndriy Voskoboinyk IEEE80211_DISCARD_IE(vap, 131995d9a127SAndriy Voskoboinyk IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 132095d9a127SAndriy Voskoboinyk wh, "WPA", "no usable pairwise cipher suite found (w=%d)", 132195d9a127SAndriy Voskoboinyk w); 132295d9a127SAndriy Voskoboinyk return IEEE80211_REASON_PAIRWISE_CIPHER_INVALID; 132395d9a127SAndriy Voskoboinyk } 132495d9a127SAndriy Voskoboinyk /* XXX other? */ 132595d9a127SAndriy Voskoboinyk if (w & (1 << IEEE80211_CIPHER_AES_CCM)) 1326b032f27cSSam Leffler rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; 132795d9a127SAndriy Voskoboinyk else 132895d9a127SAndriy Voskoboinyk rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; 1329b032f27cSSam Leffler 1330b032f27cSSam Leffler /* key management algorithms */ 133131021a2bSAndriy Voskoboinyk n = le16dec(frm); 1332b032f27cSSam Leffler frm += 2, len -= 2; 1333b032f27cSSam Leffler if (len < n*4) { 1334b032f27cSSam Leffler IEEE80211_DISCARD_IE(vap, 1335b032f27cSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1336b032f27cSSam Leffler wh, "WPA", "key mgmt alg data too short; len %u, n %u", 1337b032f27cSSam Leffler len, n); 1338b032f27cSSam Leffler return IEEE80211_REASON_IE_INVALID; 1339b032f27cSSam Leffler } 1340b032f27cSSam Leffler w = 0; 1341b032f27cSSam Leffler for (; n > 0; n--) { 1342b032f27cSSam Leffler w |= wpa_keymgmt(frm); 1343b032f27cSSam Leffler frm += 4, len -= 4; 1344b032f27cSSam Leffler } 1345b032f27cSSam Leffler if (w & WPA_ASE_8021X_UNSPEC) 1346b032f27cSSam Leffler rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC; 1347b032f27cSSam Leffler else 1348b032f27cSSam Leffler rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; 1349b032f27cSSam Leffler 1350b032f27cSSam Leffler if (len > 2) /* optional capabilities */ 135131021a2bSAndriy Voskoboinyk rsn->rsn_caps = le16dec(frm); 1352b032f27cSSam Leffler 1353b032f27cSSam Leffler return 0; 1354b032f27cSSam Leffler } 1355b032f27cSSam Leffler 1356b032f27cSSam Leffler /* 1357b032f27cSSam Leffler * Convert an RSN cipher selector OUI to an internal 1358b032f27cSSam Leffler * cipher algorithm. Where appropriate we also 1359b032f27cSSam Leffler * record any key length. 1360b032f27cSSam Leffler */ 1361b032f27cSSam Leffler static int 136295d9a127SAndriy Voskoboinyk rsn_cipher(const uint8_t *sel, uint8_t *keylen, uint8_t *cipher) 1363b032f27cSSam Leffler { 1364b032f27cSSam Leffler #define RSN_SEL(x) (((x)<<24)|RSN_OUI) 136531021a2bSAndriy Voskoboinyk uint32_t w = le32dec(sel); 1366b032f27cSSam Leffler 1367b032f27cSSam Leffler switch (w) { 1368b032f27cSSam Leffler case RSN_SEL(RSN_CSE_NULL): 136995d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_NONE; 137095d9a127SAndriy Voskoboinyk break; 1371b032f27cSSam Leffler case RSN_SEL(RSN_CSE_WEP40): 1372b032f27cSSam Leffler if (keylen) 1373b032f27cSSam Leffler *keylen = 40 / NBBY; 137495d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_WEP; 137595d9a127SAndriy Voskoboinyk break; 1376b032f27cSSam Leffler case RSN_SEL(RSN_CSE_WEP104): 1377b032f27cSSam Leffler if (keylen) 1378b032f27cSSam Leffler *keylen = 104 / NBBY; 137995d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_WEP; 138095d9a127SAndriy Voskoboinyk break; 1381b032f27cSSam Leffler case RSN_SEL(RSN_CSE_TKIP): 138295d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_TKIP; 138395d9a127SAndriy Voskoboinyk break; 1384b032f27cSSam Leffler case RSN_SEL(RSN_CSE_CCMP): 138595d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_AES_CCM; 138695d9a127SAndriy Voskoboinyk break; 1387b032f27cSSam Leffler case RSN_SEL(RSN_CSE_WRAP): 138895d9a127SAndriy Voskoboinyk *cipher = IEEE80211_CIPHER_AES_OCB; 138995d9a127SAndriy Voskoboinyk break; 139095d9a127SAndriy Voskoboinyk default: 139195d9a127SAndriy Voskoboinyk return (EINVAL); 1392b032f27cSSam Leffler } 139395d9a127SAndriy Voskoboinyk 139495d9a127SAndriy Voskoboinyk return (0); 1395b032f27cSSam Leffler #undef WPA_SEL 1396b032f27cSSam Leffler } 1397b032f27cSSam Leffler 1398b032f27cSSam Leffler /* 1399b032f27cSSam Leffler * Convert an RSN key management/authentication algorithm 1400b032f27cSSam Leffler * to an internal code. 1401b032f27cSSam Leffler */ 1402b032f27cSSam Leffler static int 1403b032f27cSSam Leffler rsn_keymgmt(const uint8_t *sel) 1404b032f27cSSam Leffler { 1405b032f27cSSam Leffler #define RSN_SEL(x) (((x)<<24)|RSN_OUI) 140631021a2bSAndriy Voskoboinyk uint32_t w = le32dec(sel); 1407b032f27cSSam Leffler 1408b032f27cSSam Leffler switch (w) { 1409b032f27cSSam Leffler case RSN_SEL(RSN_ASE_8021X_UNSPEC): 1410b032f27cSSam Leffler return RSN_ASE_8021X_UNSPEC; 1411b032f27cSSam Leffler case RSN_SEL(RSN_ASE_8021X_PSK): 1412b032f27cSSam Leffler return RSN_ASE_8021X_PSK; 1413b032f27cSSam Leffler case RSN_SEL(RSN_ASE_NONE): 1414b032f27cSSam Leffler return RSN_ASE_NONE; 1415b032f27cSSam Leffler } 1416b032f27cSSam Leffler return 0; /* NB: so is discarded */ 1417b032f27cSSam Leffler #undef RSN_SEL 1418b032f27cSSam Leffler } 1419b032f27cSSam Leffler 1420b032f27cSSam Leffler /* 1421b032f27cSSam Leffler * Parse a WPA/RSN information element to collect parameters 1422b032f27cSSam Leffler * and validate the parameters against what has been 1423b032f27cSSam Leffler * configured for the system. 1424b032f27cSSam Leffler */ 1425b032f27cSSam Leffler static int 1426b032f27cSSam Leffler ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm, 1427b032f27cSSam Leffler struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) 1428b032f27cSSam Leffler { 1429b032f27cSSam Leffler uint8_t len = frm[1]; 1430b032f27cSSam Leffler uint32_t w; 143195d9a127SAndriy Voskoboinyk int error, n; 1432b032f27cSSam Leffler 1433b032f27cSSam Leffler /* 1434b032f27cSSam Leffler * Check the length once for fixed parts: 1435b032f27cSSam Leffler * version, mcast cipher, and 2 selector counts. 1436b032f27cSSam Leffler * Other, variable-length data, must be checked separately. 1437b032f27cSSam Leffler */ 1438b032f27cSSam Leffler if ((vap->iv_flags & IEEE80211_F_WPA2) == 0) { 1439b032f27cSSam Leffler IEEE80211_DISCARD_IE(vap, 1440b032f27cSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1441b032f27cSSam Leffler wh, "WPA", "not RSN, flags 0x%x", vap->iv_flags); 1442b032f27cSSam Leffler return IEEE80211_REASON_IE_INVALID; 1443b032f27cSSam Leffler } 144495d9a127SAndriy Voskoboinyk /* XXX may be shorter */ 1445b032f27cSSam Leffler if (len < 10) { 1446b032f27cSSam Leffler IEEE80211_DISCARD_IE(vap, 1447b032f27cSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1448b032f27cSSam Leffler wh, "RSN", "too short, len %u", len); 1449b032f27cSSam Leffler return IEEE80211_REASON_IE_INVALID; 1450b032f27cSSam Leffler } 1451b032f27cSSam Leffler frm += 2; 145231021a2bSAndriy Voskoboinyk w = le16dec(frm); 1453b032f27cSSam Leffler if (w != RSN_VERSION) { 1454b032f27cSSam Leffler IEEE80211_DISCARD_IE(vap, 1455b032f27cSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1456b032f27cSSam Leffler wh, "RSN", "bad version %u", w); 145795d9a127SAndriy Voskoboinyk return IEEE80211_REASON_UNSUPP_RSN_IE_VERSION; 1458b032f27cSSam Leffler } 1459b032f27cSSam Leffler frm += 2, len -= 2; 1460b032f27cSSam Leffler 1461b032f27cSSam Leffler memset(rsn, 0, sizeof(*rsn)); 1462b032f27cSSam Leffler 1463b032f27cSSam Leffler /* multicast/group cipher */ 146495d9a127SAndriy Voskoboinyk error = rsn_cipher(frm, &rsn->rsn_mcastkeylen, &rsn->rsn_mcastcipher); 146595d9a127SAndriy Voskoboinyk if (error != 0) { 146695d9a127SAndriy Voskoboinyk IEEE80211_DISCARD_IE(vap, 146795d9a127SAndriy Voskoboinyk IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 146895d9a127SAndriy Voskoboinyk wh, "RSN", "unknown mcast cipher suite %08X", 146995d9a127SAndriy Voskoboinyk le32dec(frm)); 147095d9a127SAndriy Voskoboinyk return IEEE80211_REASON_GROUP_CIPHER_INVALID; 147195d9a127SAndriy Voskoboinyk } 147295d9a127SAndriy Voskoboinyk if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_NONE) { 147395d9a127SAndriy Voskoboinyk IEEE80211_DISCARD_IE(vap, 147495d9a127SAndriy Voskoboinyk IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 147595d9a127SAndriy Voskoboinyk wh, "RSN", "invalid mcast cipher suite %d", 147695d9a127SAndriy Voskoboinyk rsn->rsn_mcastcipher); 147795d9a127SAndriy Voskoboinyk return IEEE80211_REASON_GROUP_CIPHER_INVALID; 147895d9a127SAndriy Voskoboinyk } 1479b032f27cSSam Leffler frm += 4, len -= 4; 1480b032f27cSSam Leffler 1481b032f27cSSam Leffler /* unicast ciphers */ 148231021a2bSAndriy Voskoboinyk n = le16dec(frm); 1483b032f27cSSam Leffler frm += 2, len -= 2; 1484b032f27cSSam Leffler if (len < n*4+2) { 1485b032f27cSSam Leffler IEEE80211_DISCARD_IE(vap, 1486b032f27cSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1487b032f27cSSam Leffler wh, "RSN", "ucast cipher data too short; len %u, n %u", 1488b032f27cSSam Leffler len, n); 1489b032f27cSSam Leffler return IEEE80211_REASON_IE_INVALID; 1490b032f27cSSam Leffler } 1491b032f27cSSam Leffler w = 0; 149295d9a127SAndriy Voskoboinyk 1493b032f27cSSam Leffler for (; n > 0; n--) { 149495d9a127SAndriy Voskoboinyk uint8_t cipher; 149595d9a127SAndriy Voskoboinyk 149695d9a127SAndriy Voskoboinyk error = rsn_cipher(frm, &rsn->rsn_ucastkeylen, &cipher); 149795d9a127SAndriy Voskoboinyk if (error == 0) 149895d9a127SAndriy Voskoboinyk w |= 1 << cipher; 149995d9a127SAndriy Voskoboinyk 1500b032f27cSSam Leffler frm += 4, len -= 4; 1501b032f27cSSam Leffler } 150295d9a127SAndriy Voskoboinyk if (w & (1 << IEEE80211_CIPHER_AES_CCM)) 1503b032f27cSSam Leffler rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; 150495d9a127SAndriy Voskoboinyk else if (w & (1 << IEEE80211_CIPHER_AES_OCB)) 150595d9a127SAndriy Voskoboinyk rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_OCB; 150695d9a127SAndriy Voskoboinyk else if (w & (1 << IEEE80211_CIPHER_TKIP)) 150795d9a127SAndriy Voskoboinyk rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; 150895d9a127SAndriy Voskoboinyk else if ((w & (1 << IEEE80211_CIPHER_NONE)) && 150995d9a127SAndriy Voskoboinyk (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP || 151095d9a127SAndriy Voskoboinyk rsn->rsn_mcastcipher == IEEE80211_CIPHER_TKIP)) 151195d9a127SAndriy Voskoboinyk rsn->rsn_ucastcipher = IEEE80211_CIPHER_NONE; 151295d9a127SAndriy Voskoboinyk else { 151395d9a127SAndriy Voskoboinyk IEEE80211_DISCARD_IE(vap, 151495d9a127SAndriy Voskoboinyk IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 151595d9a127SAndriy Voskoboinyk wh, "RSN", "no usable pairwise cipher suite found (w=%d)", 151695d9a127SAndriy Voskoboinyk w); 151795d9a127SAndriy Voskoboinyk return IEEE80211_REASON_PAIRWISE_CIPHER_INVALID; 151895d9a127SAndriy Voskoboinyk } 1519b032f27cSSam Leffler 1520b032f27cSSam Leffler /* key management algorithms */ 152131021a2bSAndriy Voskoboinyk n = le16dec(frm); 1522b032f27cSSam Leffler frm += 2, len -= 2; 1523b032f27cSSam Leffler if (len < n*4) { 1524b032f27cSSam Leffler IEEE80211_DISCARD_IE(vap, 1525b032f27cSSam Leffler IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1526b032f27cSSam Leffler wh, "RSN", "key mgmt alg data too short; len %u, n %u", 1527b032f27cSSam Leffler len, n); 1528b032f27cSSam Leffler return IEEE80211_REASON_IE_INVALID; 1529b032f27cSSam Leffler } 1530b032f27cSSam Leffler w = 0; 1531b032f27cSSam Leffler for (; n > 0; n--) { 1532b032f27cSSam Leffler w |= rsn_keymgmt(frm); 1533b032f27cSSam Leffler frm += 4, len -= 4; 1534b032f27cSSam Leffler } 1535b032f27cSSam Leffler if (w & RSN_ASE_8021X_UNSPEC) 1536b032f27cSSam Leffler rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC; 1537b032f27cSSam Leffler else 1538b032f27cSSam Leffler rsn->rsn_keymgmt = RSN_ASE_8021X_PSK; 1539b032f27cSSam Leffler 1540b032f27cSSam Leffler /* optional RSN capabilities */ 15413ab5e297SAdrian Chadd if (len >= 2) { 154231021a2bSAndriy Voskoboinyk rsn->rsn_caps = le16dec(frm); 15433ab5e297SAdrian Chadd frm += 2, len -= 2; 15443ab5e297SAdrian Chadd } 15453ab5e297SAdrian Chadd 15463ab5e297SAdrian Chadd /* XXX PMK Count / PMKID */ 15473ab5e297SAdrian Chadd 15483ab5e297SAdrian Chadd /* XXX Group Cipher Management Suite */ 1549b032f27cSSam Leffler 1550b032f27cSSam Leffler return 0; 1551b032f27cSSam Leffler } 1552b032f27cSSam Leffler 1553b032f27cSSam Leffler /* 1554a4641f4eSPedro F. Giffuni * WPA/802.11i association request processing. 1555b032f27cSSam Leffler */ 1556b032f27cSSam Leffler static int 1557b032f27cSSam Leffler wpa_assocreq(struct ieee80211_node *ni, struct ieee80211_rsnparms *rsnparms, 1558b032f27cSSam Leffler const struct ieee80211_frame *wh, const uint8_t *wpa, 1559b032f27cSSam Leffler const uint8_t *rsn, uint16_t capinfo) 1560b032f27cSSam Leffler { 1561b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 1562b032f27cSSam Leffler uint8_t reason; 1563b032f27cSSam Leffler int badwparsn; 1564b032f27cSSam Leffler 1565b032f27cSSam Leffler ni->ni_flags &= ~(IEEE80211_NODE_WPS|IEEE80211_NODE_TSN); 1566b032f27cSSam Leffler if (wpa == NULL && rsn == NULL) { 1567b032f27cSSam Leffler if (vap->iv_flags_ext & IEEE80211_FEXT_WPS) { 1568b032f27cSSam Leffler /* 1569b032f27cSSam Leffler * W-Fi Protected Setup (WPS) permits 1570b032f27cSSam Leffler * clients to associate and pass EAPOL frames 1571b032f27cSSam Leffler * to establish initial credentials. 1572b032f27cSSam Leffler */ 1573b032f27cSSam Leffler ni->ni_flags |= IEEE80211_NODE_WPS; 1574b032f27cSSam Leffler return 1; 1575b032f27cSSam Leffler } 1576b032f27cSSam Leffler if ((vap->iv_flags_ext & IEEE80211_FEXT_TSN) && 1577b032f27cSSam Leffler (capinfo & IEEE80211_CAPINFO_PRIVACY)) { 1578b032f27cSSam Leffler /* 1579b032f27cSSam Leffler * Transitional Security Network. Permits clients 1580b032f27cSSam Leffler * to associate and use WEP while WPA is configured. 1581b032f27cSSam Leffler */ 1582b032f27cSSam Leffler ni->ni_flags |= IEEE80211_NODE_TSN; 1583b032f27cSSam Leffler return 1; 1584b032f27cSSam Leffler } 1585b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, 1586b032f27cSSam Leffler wh, NULL, "%s", "no WPA/RSN IE in association request"); 1587b032f27cSSam Leffler vap->iv_stats.is_rx_assoc_badwpaie++; 1588b032f27cSSam Leffler reason = IEEE80211_REASON_IE_INVALID; 1589b032f27cSSam Leffler goto bad; 1590b032f27cSSam Leffler } 1591b032f27cSSam Leffler /* assert right association security credentials */ 1592b032f27cSSam Leffler badwparsn = 0; /* NB: to silence compiler */ 1593b032f27cSSam Leffler switch (vap->iv_flags & IEEE80211_F_WPA) { 1594b032f27cSSam Leffler case IEEE80211_F_WPA1: 1595b032f27cSSam Leffler badwparsn = (wpa == NULL); 1596b032f27cSSam Leffler break; 1597b032f27cSSam Leffler case IEEE80211_F_WPA2: 1598b032f27cSSam Leffler badwparsn = (rsn == NULL); 1599b032f27cSSam Leffler break; 1600b032f27cSSam Leffler case IEEE80211_F_WPA1|IEEE80211_F_WPA2: 1601b032f27cSSam Leffler badwparsn = (wpa == NULL && rsn == NULL); 1602b032f27cSSam Leffler break; 1603b032f27cSSam Leffler } 1604b032f27cSSam Leffler if (badwparsn) { 1605b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, 1606b032f27cSSam Leffler wh, NULL, 1607b032f27cSSam Leffler "%s", "missing WPA/RSN IE in association request"); 1608b032f27cSSam Leffler vap->iv_stats.is_rx_assoc_badwpaie++; 1609b032f27cSSam Leffler reason = IEEE80211_REASON_IE_INVALID; 1610b032f27cSSam Leffler goto bad; 1611b032f27cSSam Leffler } 1612b032f27cSSam Leffler /* 1613b032f27cSSam Leffler * Parse WPA/RSN information element. 1614b032f27cSSam Leffler */ 1615b032f27cSSam Leffler if (wpa != NULL) 1616b032f27cSSam Leffler reason = ieee80211_parse_wpa(vap, wpa, rsnparms, wh); 1617b032f27cSSam Leffler else 1618b032f27cSSam Leffler reason = ieee80211_parse_rsn(vap, rsn, rsnparms, wh); 1619b032f27cSSam Leffler if (reason != 0) { 162095d9a127SAndriy Voskoboinyk /* XXX wpa->rsn fallback? */ 1621b032f27cSSam Leffler /* XXX distinguish WPA/RSN? */ 1622b032f27cSSam Leffler vap->iv_stats.is_rx_assoc_badwpaie++; 1623b032f27cSSam Leffler goto bad; 1624b032f27cSSam Leffler } 1625b032f27cSSam Leffler IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, ni, 1626b032f27cSSam Leffler "%s ie: mc %u/%u uc %u/%u key %u caps 0x%x", 1627b032f27cSSam Leffler wpa != NULL ? "WPA" : "RSN", 1628b032f27cSSam Leffler rsnparms->rsn_mcastcipher, rsnparms->rsn_mcastkeylen, 1629b032f27cSSam Leffler rsnparms->rsn_ucastcipher, rsnparms->rsn_ucastkeylen, 1630b032f27cSSam Leffler rsnparms->rsn_keymgmt, rsnparms->rsn_caps); 1631b032f27cSSam Leffler 1632b032f27cSSam Leffler return 1; 1633b032f27cSSam Leffler bad: 1634b032f27cSSam Leffler ieee80211_node_deauth(ni, reason); 1635b032f27cSSam Leffler return 0; 1636b032f27cSSam Leffler } 1637b032f27cSSam Leffler 1638b032f27cSSam Leffler /* XXX find a better place for definition */ 1639b032f27cSSam Leffler struct l2_update_frame { 1640b032f27cSSam Leffler struct ether_header eh; 1641b032f27cSSam Leffler uint8_t dsap; 1642b032f27cSSam Leffler uint8_t ssap; 1643b032f27cSSam Leffler uint8_t control; 1644b032f27cSSam Leffler uint8_t xid[3]; 1645b032f27cSSam Leffler } __packed; 1646b032f27cSSam Leffler 1647b032f27cSSam Leffler /* 1648b032f27cSSam Leffler * Deliver a TGf L2UF frame on behalf of a station. 1649b032f27cSSam Leffler * This primes any bridge when the station is roaming 1650b032f27cSSam Leffler * between ap's on the same wired network. 1651b032f27cSSam Leffler */ 1652b032f27cSSam Leffler static void 1653b032f27cSSam Leffler ieee80211_deliver_l2uf(struct ieee80211_node *ni) 1654b032f27cSSam Leffler { 1655b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 1656b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ifp; 1657b032f27cSSam Leffler struct mbuf *m; 1658b032f27cSSam Leffler struct l2_update_frame *l2uf; 1659b032f27cSSam Leffler struct ether_header *eh; 1660b032f27cSSam Leffler 1661bd29f817SBjoern A. Zeeb m = m_gethdr(IEEE80211_M_NOWAIT, MT_DATA); 1662b032f27cSSam Leffler if (m == NULL) { 1663b032f27cSSam Leffler IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, 1664b032f27cSSam Leffler "%s", "no mbuf for l2uf frame"); 1665b032f27cSSam Leffler vap->iv_stats.is_rx_nobuf++; /* XXX not right */ 1666b032f27cSSam Leffler return; 1667b032f27cSSam Leffler } 1668b032f27cSSam Leffler l2uf = mtod(m, struct l2_update_frame *); 1669b032f27cSSam Leffler eh = &l2uf->eh; 1670b032f27cSSam Leffler /* dst: Broadcast address */ 1671b032f27cSSam Leffler IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr); 1672b032f27cSSam Leffler /* src: associated STA */ 1673b032f27cSSam Leffler IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr); 1674b032f27cSSam Leffler eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh)); 1675b032f27cSSam Leffler 1676b032f27cSSam Leffler l2uf->dsap = 0; 1677b032f27cSSam Leffler l2uf->ssap = 0; 1678b032f27cSSam Leffler l2uf->control = 0xf5; 1679b032f27cSSam Leffler l2uf->xid[0] = 0x81; 1680b032f27cSSam Leffler l2uf->xid[1] = 0x80; 1681b032f27cSSam Leffler l2uf->xid[2] = 0x00; 1682b032f27cSSam Leffler 1683b032f27cSSam Leffler m->m_pkthdr.len = m->m_len = sizeof(*l2uf); 1684b032f27cSSam Leffler hostap_deliver_data(vap, ni, m); 1685b032f27cSSam Leffler } 1686b032f27cSSam Leffler 1687b032f27cSSam Leffler static void 1688b032f27cSSam Leffler ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, 1689b032f27cSSam Leffler int reassoc, int resp, const char *tag, int rate) 1690b032f27cSSam Leffler { 1691b032f27cSSam Leffler IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2, 1692b032f27cSSam Leffler "deny %s request, %s rate set mismatch, rate/MCS %d", 1693b032f27cSSam Leffler reassoc ? "reassoc" : "assoc", tag, rate & IEEE80211_RATE_VAL); 1694b032f27cSSam Leffler IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_BASIC_RATE); 1695b032f27cSSam Leffler ieee80211_node_leave(ni); 1696b032f27cSSam Leffler } 1697b032f27cSSam Leffler 1698b032f27cSSam Leffler static void 1699b032f27cSSam Leffler capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, 1700b032f27cSSam Leffler int reassoc, int resp, const char *tag, int capinfo) 1701b032f27cSSam Leffler { 1702b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 1703b032f27cSSam Leffler 1704b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2, 1705b032f27cSSam Leffler "deny %s request, %s mismatch 0x%x", 1706b032f27cSSam Leffler reassoc ? "reassoc" : "assoc", tag, capinfo); 1707b032f27cSSam Leffler IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_CAPINFO); 1708b032f27cSSam Leffler ieee80211_node_leave(ni); 1709b032f27cSSam Leffler vap->iv_stats.is_rx_assoc_capmismatch++; 1710b032f27cSSam Leffler } 1711b032f27cSSam Leffler 1712b032f27cSSam Leffler static void 1713b032f27cSSam Leffler htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, 1714b032f27cSSam Leffler int reassoc, int resp) 1715b032f27cSSam Leffler { 1716b032f27cSSam Leffler IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2, 1717b032f27cSSam Leffler "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc"); 1718b032f27cSSam Leffler /* XXX no better code */ 1719b8ee2a22SSam Leffler IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_MISSING_HT_CAPS); 1720b032f27cSSam Leffler ieee80211_node_leave(ni); 1721b032f27cSSam Leffler } 1722b032f27cSSam Leffler 1723b032f27cSSam Leffler static void 1724b032f27cSSam Leffler authalgreject(struct ieee80211_node *ni, const struct ieee80211_frame *wh, 1725b032f27cSSam Leffler int algo, int seq, int status) 1726b032f27cSSam Leffler { 1727b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 1728b032f27cSSam Leffler 1729b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 1730b032f27cSSam Leffler wh, NULL, "unsupported alg %d", algo); 1731b032f27cSSam Leffler vap->iv_stats.is_rx_auth_unsupported++; 1732b032f27cSSam Leffler ieee80211_send_error(ni, wh->i_addr2, IEEE80211_FC0_SUBTYPE_AUTH, 1733b032f27cSSam Leffler seq | (status << 16)); 1734b032f27cSSam Leffler } 1735b032f27cSSam Leffler 1736b032f27cSSam Leffler static __inline int 1737b032f27cSSam Leffler ishtmixed(const uint8_t *ie) 1738b032f27cSSam Leffler { 1739b032f27cSSam Leffler const struct ieee80211_ie_htinfo *ht = 1740b032f27cSSam Leffler (const struct ieee80211_ie_htinfo *) ie; 1741b032f27cSSam Leffler return (ht->hi_byte2 & IEEE80211_HTINFO_OPMODE) == 1742b032f27cSSam Leffler IEEE80211_HTINFO_OPMODE_MIXED; 1743b032f27cSSam Leffler } 1744b032f27cSSam Leffler 1745b032f27cSSam Leffler static int 1746b032f27cSSam Leffler is11bclient(const uint8_t *rates, const uint8_t *xrates) 1747b032f27cSSam Leffler { 1748b032f27cSSam Leffler static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11); 1749b032f27cSSam Leffler int i; 1750b032f27cSSam Leffler 1751b032f27cSSam Leffler /* NB: the 11b clients we care about will not have xrates */ 1752b032f27cSSam Leffler if (xrates != NULL || rates == NULL) 1753b032f27cSSam Leffler return 0; 1754b032f27cSSam Leffler for (i = 0; i < rates[1]; i++) { 1755b032f27cSSam Leffler int r = rates[2+i] & IEEE80211_RATE_VAL; 1756b032f27cSSam Leffler if (r > 2*11 || ((1<<r) & brates) == 0) 1757b032f27cSSam Leffler return 0; 1758b032f27cSSam Leffler } 1759b032f27cSSam Leffler return 1; 1760b032f27cSSam Leffler } 1761b032f27cSSam Leffler 1762b032f27cSSam Leffler static void 1763b032f27cSSam Leffler hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 1764c79f192cSAdrian Chadd int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) 1765b032f27cSSam Leffler { 1766b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 1767b032f27cSSam Leffler struct ieee80211com *ic = ni->ni_ic; 1768b032f27cSSam Leffler struct ieee80211_frame *wh; 1769b032f27cSSam Leffler uint8_t *frm, *efrm, *sfrm; 1770b032f27cSSam Leffler uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap; 177151172f62SAdrian Chadd uint8_t *vhtcap, *vhtinfo; 1772b032f27cSSam Leffler int reassoc, resp; 1773b032f27cSSam Leffler uint8_t rate; 1774b032f27cSSam Leffler 1775b032f27cSSam Leffler wh = mtod(m0, struct ieee80211_frame *); 1776b032f27cSSam Leffler frm = (uint8_t *)&wh[1]; 1777b032f27cSSam Leffler efrm = mtod(m0, uint8_t *) + m0->m_len; 1778b032f27cSSam Leffler switch (subtype) { 1779b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 1780b032f27cSSam Leffler /* 1781b032f27cSSam Leffler * We process beacon/probe response frames when scanning; 1782b032f27cSSam Leffler * otherwise we check beacon frames for overlapping non-ERP 1783b032f27cSSam Leffler * BSS in 11g and/or overlapping legacy BSS when in HT. 1784b032f27cSSam Leffler */ 17854ba33fd1SAndriy Voskoboinyk if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 1786b032f27cSSam Leffler vap->iv_stats.is_rx_mgtdiscard++; 1787b032f27cSSam Leffler return; 1788b032f27cSSam Leffler } 17894ba33fd1SAndriy Voskoboinyk /* FALLTHROUGH */ 17904ba33fd1SAndriy Voskoboinyk case IEEE80211_FC0_SUBTYPE_BEACON: { 17914ba33fd1SAndriy Voskoboinyk struct ieee80211_scanparams scan; 17924ba33fd1SAndriy Voskoboinyk 1793b032f27cSSam Leffler /* NB: accept off-channel frames */ 1794c79f192cSAdrian Chadd /* XXX TODO: use rxstatus to determine off-channel details */ 1795c79f192cSAdrian Chadd if (ieee80211_parse_beacon(ni, m0, ic->ic_curchan, &scan) &~ IEEE80211_BPARSE_OFFCHAN) 1796b032f27cSSam Leffler return; 1797b032f27cSSam Leffler /* 1798b032f27cSSam Leffler * Count frame now that we know it's to be processed. 1799b032f27cSSam Leffler */ 1800b032f27cSSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { 1801b032f27cSSam Leffler vap->iv_stats.is_rx_beacon++; /* XXX remove */ 1802b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_beacons); 1803b032f27cSSam Leffler } else 1804b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_proberesp); 1805b032f27cSSam Leffler /* 1806b032f27cSSam Leffler * If scanning, just pass information to the scan module. 1807b032f27cSSam Leffler */ 1808b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) { 1809b032f27cSSam Leffler if (scan.status == 0 && /* NB: on channel */ 1810b032f27cSSam Leffler (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN)) { 1811b032f27cSSam Leffler /* 1812b032f27cSSam Leffler * Actively scanning a channel marked passive; 1813b032f27cSSam Leffler * send a probe request now that we know there 1814b032f27cSSam Leffler * is 802.11 traffic present. 1815b032f27cSSam Leffler * 1816b032f27cSSam Leffler * XXX check if the beacon we recv'd gives 1817b032f27cSSam Leffler * us what we need and suppress the probe req 1818b032f27cSSam Leffler */ 18199776aba3SBjoern A. Zeeb ieee80211_probe_curchan(vap, true); 1820b032f27cSSam Leffler ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; 1821b032f27cSSam Leffler } 18222808a02bSAdrian Chadd ieee80211_add_scan(vap, ic->ic_curchan, &scan, wh, 18232808a02bSAdrian Chadd subtype, rssi, nf); 1824b032f27cSSam Leffler return; 1825b032f27cSSam Leffler } 1826b032f27cSSam Leffler /* 1827b032f27cSSam Leffler * Check beacon for overlapping bss w/ non ERP stations. 1828b032f27cSSam Leffler * If we detect one and protection is configured but not 1829b032f27cSSam Leffler * enabled, enable it and start a timer that'll bring us 1830b032f27cSSam Leffler * out if we stop seeing the bss. 1831b032f27cSSam Leffler */ 1832b032f27cSSam Leffler if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 1833b032f27cSSam Leffler scan.status == 0 && /* NB: on-channel */ 1834b032f27cSSam Leffler ((scan.erp & 0x100) == 0 || /* NB: no ERP, 11b sta*/ 1835b032f27cSSam Leffler (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) { 1836f1481c8dSAdrian Chadd vap->iv_lastnonerp = ticks; 1837f1481c8dSAdrian Chadd vap->iv_flags_ext |= IEEE80211_FEXT_NONERP_PR; 1838f1481c8dSAdrian Chadd /* 1839f1481c8dSAdrian Chadd * XXX TODO: this may need to check all VAPs? 1840f1481c8dSAdrian Chadd */ 1841f1481c8dSAdrian Chadd if (vap->iv_protmode != IEEE80211_PROT_NONE && 1842f1481c8dSAdrian Chadd (vap->iv_flags & IEEE80211_F_USEPROT) == 0) { 1843b032f27cSSam Leffler IEEE80211_NOTE_FRAME(vap, 1844b032f27cSSam Leffler IEEE80211_MSG_ASSOC, wh, 1845b032f27cSSam Leffler "non-ERP present on channel %d " 1846b032f27cSSam Leffler "(saw erp 0x%x from channel %d), " 1847b032f27cSSam Leffler "enable use of protection", 1848b032f27cSSam Leffler ic->ic_curchan->ic_ieee, 1849b032f27cSSam Leffler scan.erp, scan.chan); 1850f1481c8dSAdrian Chadd vap->iv_flags |= IEEE80211_F_USEPROT; 1851f1481c8dSAdrian Chadd ieee80211_vap_update_erp_protmode(vap); 1852b032f27cSSam Leffler } 1853b032f27cSSam Leffler } 1854b032f27cSSam Leffler /* 1855b032f27cSSam Leffler * Check beacon for non-HT station on HT channel 1856b032f27cSSam Leffler * and update HT BSS occupancy as appropriate. 1857b032f27cSSam Leffler */ 1858b032f27cSSam Leffler if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) { 1859b032f27cSSam Leffler if (scan.status & IEEE80211_BPARSE_OFFCHAN) { 1860b032f27cSSam Leffler /* 1861b032f27cSSam Leffler * Off control channel; only check frames 1862b032f27cSSam Leffler * that come in the extension channel when 1863b032f27cSSam Leffler * operating w/ HT40. 1864b032f27cSSam Leffler */ 1865b032f27cSSam Leffler if (!IEEE80211_IS_CHAN_HT40(ic->ic_curchan)) 1866b032f27cSSam Leffler break; 1867b032f27cSSam Leffler if (scan.chan != ic->ic_curchan->ic_extieee) 1868b032f27cSSam Leffler break; 1869b032f27cSSam Leffler } 1870b032f27cSSam Leffler if (scan.htinfo == NULL) { 1871f1481c8dSAdrian Chadd ieee80211_htprot_update(vap, 1872b032f27cSSam Leffler IEEE80211_HTINFO_OPMODE_PROTOPT | 1873b032f27cSSam Leffler IEEE80211_HTINFO_NONHT_PRESENT); 1874b032f27cSSam Leffler } else if (ishtmixed(scan.htinfo)) { 1875b032f27cSSam Leffler /* XXX? take NONHT_PRESENT from beacon? */ 1876f1481c8dSAdrian Chadd ieee80211_htprot_update(vap, 1877b032f27cSSam Leffler IEEE80211_HTINFO_OPMODE_MIXED | 1878b032f27cSSam Leffler IEEE80211_HTINFO_NONHT_PRESENT); 1879b032f27cSSam Leffler } 1880b032f27cSSam Leffler } 1881b032f27cSSam Leffler break; 1882b032f27cSSam Leffler } 1883b032f27cSSam Leffler 1884b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 1885b032f27cSSam Leffler if (vap->iv_state != IEEE80211_S_RUN) { 1886b032f27cSSam Leffler vap->iv_stats.is_rx_mgtdiscard++; 1887b032f27cSSam Leffler return; 1888b032f27cSSam Leffler } 1889b032f27cSSam Leffler /* 1890957458a8SAdrian Chadd * Consult the ACL policy module if setup. 1891957458a8SAdrian Chadd */ 18925a8801b0SBernhard Schmidt if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) { 1893957458a8SAdrian Chadd IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL, 1894957458a8SAdrian Chadd wh, NULL, "%s", "disallowed by ACL"); 1895957458a8SAdrian Chadd vap->iv_stats.is_rx_acl++; 1896957458a8SAdrian Chadd return; 1897957458a8SAdrian Chadd } 1898957458a8SAdrian Chadd /* 1899b032f27cSSam Leffler * prreq frame format 1900b032f27cSSam Leffler * [tlv] ssid 1901b032f27cSSam Leffler * [tlv] supported rates 1902b032f27cSSam Leffler * [tlv] extended supported rates 1903b032f27cSSam Leffler */ 1904b032f27cSSam Leffler ssid = rates = xrates = NULL; 1905b032f27cSSam Leffler while (efrm - frm > 1) { 1906b032f27cSSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 1907b032f27cSSam Leffler switch (*frm) { 1908b032f27cSSam Leffler case IEEE80211_ELEMID_SSID: 1909b032f27cSSam Leffler ssid = frm; 1910b032f27cSSam Leffler break; 1911b032f27cSSam Leffler case IEEE80211_ELEMID_RATES: 1912b032f27cSSam Leffler rates = frm; 1913b032f27cSSam Leffler break; 1914b032f27cSSam Leffler case IEEE80211_ELEMID_XRATES: 1915b032f27cSSam Leffler xrates = frm; 1916b032f27cSSam Leffler break; 1917b032f27cSSam Leffler } 1918b032f27cSSam Leffler frm += frm[1] + 2; 1919b032f27cSSam Leffler } 1920b032f27cSSam Leffler IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); 1921b032f27cSSam Leffler if (xrates != NULL) 1922b032f27cSSam Leffler IEEE80211_VERIFY_ELEMENT(xrates, 1923b032f27cSSam Leffler IEEE80211_RATE_MAXSIZE - rates[1], return); 1924b032f27cSSam Leffler IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); 1925b032f27cSSam Leffler IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); 1926b032f27cSSam Leffler if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { 1927b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1928b032f27cSSam Leffler wh, NULL, 1929b032f27cSSam Leffler "%s", "no ssid with ssid suppression enabled"); 1930b032f27cSSam Leffler vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/ 1931b032f27cSSam Leffler return; 1932b032f27cSSam Leffler } 1933b032f27cSSam Leffler 1934b032f27cSSam Leffler /* XXX find a better class or define it's own */ 1935b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, 1936b032f27cSSam Leffler "%s", "recv probe req"); 1937b032f27cSSam Leffler /* 1938b032f27cSSam Leffler * Some legacy 11b clients cannot hack a complete 1939b032f27cSSam Leffler * probe response frame. When the request includes 1940b032f27cSSam Leffler * only a bare-bones rate set, communicate this to 1941b032f27cSSam Leffler * the transmit side. 1942b032f27cSSam Leffler */ 1943b032f27cSSam Leffler ieee80211_send_proberesp(vap, wh->i_addr2, 1944b032f27cSSam Leffler is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0); 1945b032f27cSSam Leffler break; 1946b032f27cSSam Leffler 1947b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: { 1948b032f27cSSam Leffler uint16_t algo, seq, status; 1949b032f27cSSam Leffler 1950b032f27cSSam Leffler if (vap->iv_state != IEEE80211_S_RUN) { 1951b032f27cSSam Leffler vap->iv_stats.is_rx_mgtdiscard++; 1952b032f27cSSam Leffler return; 1953b032f27cSSam Leffler } 1954b032f27cSSam Leffler if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) { 1955b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 1956b032f27cSSam Leffler wh, NULL, "%s", "wrong bssid"); 1957b032f27cSSam Leffler vap->iv_stats.is_rx_wrongbss++; /*XXX unique stat?*/ 1958b032f27cSSam Leffler return; 1959b032f27cSSam Leffler } 1960b032f27cSSam Leffler /* 1961b032f27cSSam Leffler * auth frame format 1962b032f27cSSam Leffler * [2] algorithm 1963b032f27cSSam Leffler * [2] sequence 1964b032f27cSSam Leffler * [2] status 1965b032f27cSSam Leffler * [tlv*] challenge 1966b032f27cSSam Leffler */ 1967b032f27cSSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); 1968b032f27cSSam Leffler algo = le16toh(*(uint16_t *)frm); 1969b032f27cSSam Leffler seq = le16toh(*(uint16_t *)(frm + 2)); 1970b032f27cSSam Leffler status = le16toh(*(uint16_t *)(frm + 4)); 1971b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2, 1972b032f27cSSam Leffler "recv auth frame with algorithm %d seq %d", algo, seq); 1973b032f27cSSam Leffler /* 1974b032f27cSSam Leffler * Consult the ACL policy module if setup. 1975b032f27cSSam Leffler */ 19765a8801b0SBernhard Schmidt if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) { 1977b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL, 1978b032f27cSSam Leffler wh, NULL, "%s", "disallowed by ACL"); 1979b032f27cSSam Leffler vap->iv_stats.is_rx_acl++; 1980b032f27cSSam Leffler ieee80211_send_error(ni, wh->i_addr2, 1981b032f27cSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 1982b032f27cSSam Leffler (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16)); 1983b032f27cSSam Leffler return; 1984b032f27cSSam Leffler } 1985b032f27cSSam Leffler if (vap->iv_flags & IEEE80211_F_COUNTERM) { 1986b032f27cSSam Leffler IEEE80211_DISCARD(vap, 1987b032f27cSSam Leffler IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, 1988b032f27cSSam Leffler wh, NULL, "%s", "TKIP countermeasures enabled"); 1989b032f27cSSam Leffler vap->iv_stats.is_rx_auth_countermeasures++; 1990b032f27cSSam Leffler ieee80211_send_error(ni, wh->i_addr2, 1991b032f27cSSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 1992b032f27cSSam Leffler IEEE80211_REASON_MIC_FAILURE); 1993b032f27cSSam Leffler return; 1994b032f27cSSam Leffler } 1995b032f27cSSam Leffler if (algo == IEEE80211_AUTH_ALG_SHARED) 19965463c4a4SSam Leffler hostap_auth_shared(ni, wh, frm + 6, efrm, rssi, nf, 1997b032f27cSSam Leffler seq, status); 19985463c4a4SSam Leffler else if (algo == IEEE80211_AUTH_ALG_OPEN) 19995463c4a4SSam Leffler hostap_auth_open(ni, wh, rssi, nf, seq, status); 2000b032f27cSSam Leffler else if (algo == IEEE80211_AUTH_ALG_LEAP) { 2001b032f27cSSam Leffler authalgreject(ni, wh, algo, 2002b032f27cSSam Leffler seq+1, IEEE80211_STATUS_ALG); 2003b032f27cSSam Leffler return; 2004b032f27cSSam Leffler } else { 2005b032f27cSSam Leffler /* 2006b032f27cSSam Leffler * We assume that an unknown algorithm is the result 2007b032f27cSSam Leffler * of a decryption failure on a shared key auth frame; 2008b032f27cSSam Leffler * return a status code appropriate for that instead 2009b032f27cSSam Leffler * of IEEE80211_STATUS_ALG. 2010b032f27cSSam Leffler * 2011b032f27cSSam Leffler * NB: a seq# of 4 is intentional; the decrypted 2012b032f27cSSam Leffler * frame likely has a bogus seq value. 2013b032f27cSSam Leffler */ 2014b032f27cSSam Leffler authalgreject(ni, wh, algo, 2015b032f27cSSam Leffler 4, IEEE80211_STATUS_CHALLENGE); 2016b032f27cSSam Leffler return; 2017b032f27cSSam Leffler } 2018b032f27cSSam Leffler break; 2019b032f27cSSam Leffler } 2020b032f27cSSam Leffler 2021b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 2022b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { 2023b032f27cSSam Leffler uint16_t capinfo, lintval; 2024b032f27cSSam Leffler struct ieee80211_rsnparms rsnparms; 2025b032f27cSSam Leffler 2026b032f27cSSam Leffler if (vap->iv_state != IEEE80211_S_RUN) { 2027b032f27cSSam Leffler vap->iv_stats.is_rx_mgtdiscard++; 2028b032f27cSSam Leffler return; 2029b032f27cSSam Leffler } 2030b032f27cSSam Leffler if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) { 2031b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 2032b032f27cSSam Leffler wh, NULL, "%s", "wrong bssid"); 2033b032f27cSSam Leffler vap->iv_stats.is_rx_assoc_bss++; 2034b032f27cSSam Leffler return; 2035b032f27cSSam Leffler } 2036b032f27cSSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 2037b032f27cSSam Leffler reassoc = 1; 2038b032f27cSSam Leffler resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; 2039b032f27cSSam Leffler } else { 2040b032f27cSSam Leffler reassoc = 0; 2041b032f27cSSam Leffler resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; 2042b032f27cSSam Leffler } 2043b032f27cSSam Leffler if (ni == vap->iv_bss) { 2044b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2, 2045b032f27cSSam Leffler "deny %s request, sta not authenticated", 2046b032f27cSSam Leffler reassoc ? "reassoc" : "assoc"); 2047b032f27cSSam Leffler ieee80211_send_error(ni, wh->i_addr2, 2048b032f27cSSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 2049b032f27cSSam Leffler IEEE80211_REASON_ASSOC_NOT_AUTHED); 2050b032f27cSSam Leffler vap->iv_stats.is_rx_assoc_notauth++; 2051b032f27cSSam Leffler return; 2052b032f27cSSam Leffler } 2053b032f27cSSam Leffler 2054b032f27cSSam Leffler /* 2055b032f27cSSam Leffler * asreq frame format 2056b032f27cSSam Leffler * [2] capability information 2057b032f27cSSam Leffler * [2] listen interval 2058b032f27cSSam Leffler * [6*] current AP address (reassoc only) 2059b032f27cSSam Leffler * [tlv] ssid 2060b032f27cSSam Leffler * [tlv] supported rates 2061b032f27cSSam Leffler * [tlv] extended supported rates 2062b032f27cSSam Leffler * [tlv] WPA or RSN 2063b032f27cSSam Leffler * [tlv] HT capabilities 2064b032f27cSSam Leffler * [tlv] Atheros capabilities 2065b032f27cSSam Leffler */ 2066b032f27cSSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return); 2067b032f27cSSam Leffler capinfo = le16toh(*(uint16_t *)frm); frm += 2; 2068b032f27cSSam Leffler lintval = le16toh(*(uint16_t *)frm); frm += 2; 2069b032f27cSSam Leffler if (reassoc) 2070b032f27cSSam Leffler frm += 6; /* ignore current AP info */ 2071b032f27cSSam Leffler ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL; 207251172f62SAdrian Chadd vhtcap = vhtinfo = NULL; 2073b032f27cSSam Leffler sfrm = frm; 2074b032f27cSSam Leffler while (efrm - frm > 1) { 2075b032f27cSSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 2076b032f27cSSam Leffler switch (*frm) { 2077b032f27cSSam Leffler case IEEE80211_ELEMID_SSID: 2078b032f27cSSam Leffler ssid = frm; 2079b032f27cSSam Leffler break; 2080b032f27cSSam Leffler case IEEE80211_ELEMID_RATES: 2081b032f27cSSam Leffler rates = frm; 2082b032f27cSSam Leffler break; 2083b032f27cSSam Leffler case IEEE80211_ELEMID_XRATES: 2084b032f27cSSam Leffler xrates = frm; 2085b032f27cSSam Leffler break; 2086b032f27cSSam Leffler case IEEE80211_ELEMID_RSN: 2087b032f27cSSam Leffler rsn = frm; 2088b032f27cSSam Leffler break; 2089b032f27cSSam Leffler case IEEE80211_ELEMID_HTCAP: 2090b032f27cSSam Leffler htcap = frm; 2091b032f27cSSam Leffler break; 209251172f62SAdrian Chadd case IEEE80211_ELEMID_VHT_CAP: 209351172f62SAdrian Chadd vhtcap = frm; 209451172f62SAdrian Chadd break; 209551172f62SAdrian Chadd case IEEE80211_ELEMID_VHT_OPMODE: 209651172f62SAdrian Chadd vhtinfo = frm; 209751172f62SAdrian Chadd break; 2098b032f27cSSam Leffler case IEEE80211_ELEMID_VENDOR: 2099b032f27cSSam Leffler if (iswpaoui(frm)) 2100b032f27cSSam Leffler wpa = frm; 2101b032f27cSSam Leffler else if (iswmeinfo(frm)) 2102b032f27cSSam Leffler wme = frm; 2103616190d0SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 2104b032f27cSSam Leffler else if (isatherosoui(frm)) 2105b032f27cSSam Leffler ath = frm; 2106616190d0SSam Leffler #endif 21072bfc8a91SSam Leffler else if (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) { 2108b032f27cSSam Leffler if (ishtcapoui(frm) && htcap == NULL) 2109b032f27cSSam Leffler htcap = frm; 2110b032f27cSSam Leffler } 2111b032f27cSSam Leffler break; 2112b032f27cSSam Leffler } 2113b032f27cSSam Leffler frm += frm[1] + 2; 2114b032f27cSSam Leffler } 2115b032f27cSSam Leffler IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); 2116b032f27cSSam Leffler if (xrates != NULL) 2117b032f27cSSam Leffler IEEE80211_VERIFY_ELEMENT(xrates, 2118b032f27cSSam Leffler IEEE80211_RATE_MAXSIZE - rates[1], return); 2119b032f27cSSam Leffler IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); 2120b032f27cSSam Leffler IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); 2121b032f27cSSam Leffler if (htcap != NULL) { 2122b032f27cSSam Leffler IEEE80211_VERIFY_LENGTH(htcap[1], 2123b032f27cSSam Leffler htcap[0] == IEEE80211_ELEMID_VENDOR ? 2124b032f27cSSam Leffler 4 + sizeof(struct ieee80211_ie_htcap)-2 : 2125b032f27cSSam Leffler sizeof(struct ieee80211_ie_htcap)-2, 2126b032f27cSSam Leffler return); /* XXX just NULL out? */ 2127b032f27cSSam Leffler } 2128b032f27cSSam Leffler 212946788bb1SAdrian Chadd /* Validate VHT IEs */ 213046788bb1SAdrian Chadd if (vhtcap != NULL) { 213146788bb1SAdrian Chadd IEEE80211_VERIFY_LENGTH(vhtcap[1], 2132e85eb4c8SBjoern A. Zeeb sizeof(struct ieee80211_vht_cap), 213346788bb1SAdrian Chadd return); 213446788bb1SAdrian Chadd } 213546788bb1SAdrian Chadd if (vhtinfo != NULL) { 213646788bb1SAdrian Chadd IEEE80211_VERIFY_LENGTH(vhtinfo[1], 2137e85eb4c8SBjoern A. Zeeb sizeof(struct ieee80211_vht_operation), 213846788bb1SAdrian Chadd return); 213946788bb1SAdrian Chadd } 21407f19273cSAdrian Chadd 2141b032f27cSSam Leffler if ((vap->iv_flags & IEEE80211_F_WPA) && 2142b032f27cSSam Leffler !wpa_assocreq(ni, &rsnparms, wh, wpa, rsn, capinfo)) 2143b032f27cSSam Leffler return; 2144b032f27cSSam Leffler /* discard challenge after association */ 2145b032f27cSSam Leffler if (ni->ni_challenge != NULL) { 2146b9b53389SAdrian Chadd IEEE80211_FREE(ni->ni_challenge, M_80211_NODE); 2147b032f27cSSam Leffler ni->ni_challenge = NULL; 2148b032f27cSSam Leffler } 2149b032f27cSSam Leffler /* NB: 802.11 spec says to ignore station's privacy bit */ 2150b032f27cSSam Leffler if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) { 2151b032f27cSSam Leffler capinfomismatch(ni, wh, reassoc, resp, 2152b032f27cSSam Leffler "capability", capinfo); 2153b032f27cSSam Leffler return; 2154b032f27cSSam Leffler } 2155b032f27cSSam Leffler /* 2156b032f27cSSam Leffler * Disallow re-associate w/ invalid slot time setting. 2157b032f27cSSam Leffler */ 2158b032f27cSSam Leffler if (ni->ni_associd != 0 && 2159b032f27cSSam Leffler IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && 2160b032f27cSSam Leffler ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) { 2161b032f27cSSam Leffler capinfomismatch(ni, wh, reassoc, resp, 2162b032f27cSSam Leffler "slot time", capinfo); 2163b032f27cSSam Leffler return; 2164b032f27cSSam Leffler } 2165b032f27cSSam Leffler rate = ieee80211_setup_rates(ni, rates, xrates, 2166b032f27cSSam Leffler IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | 2167b032f27cSSam Leffler IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 2168b032f27cSSam Leffler if (rate & IEEE80211_RATE_BASIC) { 2169b032f27cSSam Leffler ratesetmismatch(ni, wh, reassoc, resp, "legacy", rate); 2170b032f27cSSam Leffler vap->iv_stats.is_rx_assoc_norate++; 2171b032f27cSSam Leffler return; 2172b032f27cSSam Leffler } 2173b032f27cSSam Leffler /* 2174b032f27cSSam Leffler * If constrained to 11g-only stations reject an 2175b032f27cSSam Leffler * 11b-only station. We cheat a bit here by looking 2176b032f27cSSam Leffler * at the max negotiated xmit rate and assuming anyone 2177b032f27cSSam Leffler * with a best rate <24Mb/s is an 11b station. 2178b032f27cSSam Leffler */ 2179b032f27cSSam Leffler if ((vap->iv_flags & IEEE80211_F_PUREG) && rate < 48) { 2180b032f27cSSam Leffler ratesetmismatch(ni, wh, reassoc, resp, "11g", rate); 2181b032f27cSSam Leffler vap->iv_stats.is_rx_assoc_norate++; 2182b032f27cSSam Leffler return; 2183b032f27cSSam Leffler } 218451172f62SAdrian Chadd 2185b032f27cSSam Leffler /* 2186b032f27cSSam Leffler * Do HT rate set handling and setup HT node state. 2187b032f27cSSam Leffler */ 2188b032f27cSSam Leffler ni->ni_chan = vap->iv_bss->ni_chan; 218951172f62SAdrian Chadd 219051172f62SAdrian Chadd /* VHT */ 219120235662SAdrian Chadd if (IEEE80211_IS_CHAN_VHT(ni->ni_chan) && 21927f19273cSAdrian Chadd vhtcap != NULL && 219320235662SAdrian Chadd vhtinfo != NULL) { 219451172f62SAdrian Chadd /* XXX TODO; see below */ 219551172f62SAdrian Chadd printf("%s: VHT TODO!\n", __func__); 219651172f62SAdrian Chadd ieee80211_vht_node_init(ni); 219751172f62SAdrian Chadd ieee80211_vht_update_cap(ni, vhtcap, vhtinfo); 219851172f62SAdrian Chadd } else if (ni->ni_flags & IEEE80211_NODE_VHT) 219951172f62SAdrian Chadd ieee80211_vht_node_cleanup(ni); 220051172f62SAdrian Chadd 220151172f62SAdrian Chadd /* HT */ 2202b032f27cSSam Leffler if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) { 2203b032f27cSSam Leffler rate = ieee80211_setup_htrates(ni, htcap, 2204b032f27cSSam Leffler IEEE80211_F_DOFMCS | IEEE80211_F_DONEGO | 2205b032f27cSSam Leffler IEEE80211_F_DOBRS); 2206b032f27cSSam Leffler if (rate & IEEE80211_RATE_BASIC) { 2207b032f27cSSam Leffler ratesetmismatch(ni, wh, reassoc, resp, 2208b032f27cSSam Leffler "HT", rate); 2209b032f27cSSam Leffler vap->iv_stats.is_ht_assoc_norate++; 2210b032f27cSSam Leffler return; 2211b032f27cSSam Leffler } 2212fdabd982SSam Leffler ieee80211_ht_node_init(ni); 2213fdabd982SSam Leffler ieee80211_ht_updatehtcap(ni, htcap); 2214b032f27cSSam Leffler } else if (ni->ni_flags & IEEE80211_NODE_HT) 2215b032f27cSSam Leffler ieee80211_ht_node_cleanup(ni); 221651172f62SAdrian Chadd 221751172f62SAdrian Chadd /* Finally - this will use HT/VHT info to change node channel */ 221851172f62SAdrian Chadd if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) { 221951172f62SAdrian Chadd ieee80211_ht_updatehtcap_final(ni); 222051172f62SAdrian Chadd } 222151172f62SAdrian Chadd 2222339ccfb3SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 22230e6cbef2SAdrian Chadd /* Always do ff node cleanup; for A-MSDU */ 2224339ccfb3SSam Leffler ieee80211_ff_node_cleanup(ni); 2225339ccfb3SSam Leffler #endif 2226b032f27cSSam Leffler /* 2227b032f27cSSam Leffler * Allow AMPDU operation only with unencrypted traffic 2228b032f27cSSam Leffler * or AES-CCM; the 11n spec only specifies these ciphers 2229b032f27cSSam Leffler * so permitting any others is undefined and can lead 2230b032f27cSSam Leffler * to interoperability problems. 2231b032f27cSSam Leffler */ 2232b032f27cSSam Leffler if ((ni->ni_flags & IEEE80211_NODE_HT) && 2233b032f27cSSam Leffler (((vap->iv_flags & IEEE80211_F_WPA) && 2234b032f27cSSam Leffler rsnparms.rsn_ucastcipher != IEEE80211_CIPHER_AES_CCM) || 2235b032f27cSSam Leffler (vap->iv_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) { 2236b032f27cSSam Leffler IEEE80211_NOTE(vap, 2237b032f27cSSam Leffler IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, 2238b032f27cSSam Leffler "disallow HT use because WEP or TKIP requested, " 2239b032f27cSSam Leffler "capinfo 0x%x ucastcipher %d", capinfo, 2240b032f27cSSam Leffler rsnparms.rsn_ucastcipher); 2241b032f27cSSam Leffler ieee80211_ht_node_cleanup(ni); 22420e6cbef2SAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG 22430e6cbef2SAdrian Chadd /* Always do ff node cleanup; for A-MSDU */ 22440e6cbef2SAdrian Chadd ieee80211_ff_node_cleanup(ni); 22450e6cbef2SAdrian Chadd #endif 2246b032f27cSSam Leffler vap->iv_stats.is_ht_assoc_downgrade++; 2247b032f27cSSam Leffler } 2248b032f27cSSam Leffler /* 2249b032f27cSSam Leffler * If constrained to 11n-only stations reject legacy stations. 2250b032f27cSSam Leffler */ 22512bfc8a91SSam Leffler if ((vap->iv_flags_ht & IEEE80211_FHT_PUREN) && 2252b032f27cSSam Leffler (ni->ni_flags & IEEE80211_NODE_HT) == 0) { 2253b032f27cSSam Leffler htcapmismatch(ni, wh, reassoc, resp); 2254b032f27cSSam Leffler vap->iv_stats.is_ht_assoc_nohtcap++; 2255b032f27cSSam Leffler return; 2256b032f27cSSam Leffler } 2257b032f27cSSam Leffler IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 22585463c4a4SSam Leffler ni->ni_noise = nf; 2259b032f27cSSam Leffler ni->ni_intval = lintval; 2260b032f27cSSam Leffler ni->ni_capinfo = capinfo; 2261b032f27cSSam Leffler ni->ni_fhdwell = vap->iv_bss->ni_fhdwell; 2262b032f27cSSam Leffler ni->ni_fhindex = vap->iv_bss->ni_fhindex; 2263b032f27cSSam Leffler /* 2264b032f27cSSam Leffler * Store the IEs. 2265b032f27cSSam Leffler * XXX maybe better to just expand 2266b032f27cSSam Leffler */ 2267b032f27cSSam Leffler if (ieee80211_ies_init(&ni->ni_ies, sfrm, efrm - sfrm)) { 2268b032f27cSSam Leffler #define setie(_ie, _off) ieee80211_ies_setie(ni->ni_ies, _ie, _off) 2269b032f27cSSam Leffler if (wpa != NULL) 2270b032f27cSSam Leffler setie(wpa_ie, wpa - sfrm); 2271b032f27cSSam Leffler if (rsn != NULL) 2272b032f27cSSam Leffler setie(rsn_ie, rsn - sfrm); 2273b032f27cSSam Leffler if (htcap != NULL) 2274b032f27cSSam Leffler setie(htcap_ie, htcap - sfrm); 2275b032f27cSSam Leffler if (wme != NULL) { 2276b032f27cSSam Leffler setie(wme_ie, wme - sfrm); 2277b032f27cSSam Leffler /* 2278b032f27cSSam Leffler * Mark node as capable of QoS. 2279b032f27cSSam Leffler */ 2280b032f27cSSam Leffler ni->ni_flags |= IEEE80211_NODE_QOS; 22818379e8dbSAdrian Chadd if (ieee80211_parse_wmeie(wme, wh, ni) > 0) { 22828379e8dbSAdrian Chadd if (ni->ni_uapsd != 0) 22838379e8dbSAdrian Chadd ni->ni_flags |= 22848379e8dbSAdrian Chadd IEEE80211_NODE_UAPSD; 22858379e8dbSAdrian Chadd else 22868379e8dbSAdrian Chadd ni->ni_flags &= 22878379e8dbSAdrian Chadd ~IEEE80211_NODE_UAPSD; 22888379e8dbSAdrian Chadd } 2289b032f27cSSam Leffler } else 22908379e8dbSAdrian Chadd ni->ni_flags &= 22918379e8dbSAdrian Chadd ~(IEEE80211_NODE_QOS | 22928379e8dbSAdrian Chadd IEEE80211_NODE_UAPSD); 2293616190d0SSam Leffler #ifdef IEEE80211_SUPPORT_SUPERG 2294b032f27cSSam Leffler if (ath != NULL) { 2295b032f27cSSam Leffler setie(ath_ie, ath - sfrm); 2296b032f27cSSam Leffler /* 2297b032f27cSSam Leffler * Parse ATH station parameters. 2298b032f27cSSam Leffler */ 2299b032f27cSSam Leffler ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); 2300b032f27cSSam Leffler } else 2301616190d0SSam Leffler #endif 2302b032f27cSSam Leffler ni->ni_ath_flags = 0; 2303b032f27cSSam Leffler #undef setie 2304b032f27cSSam Leffler } else { 2305b032f27cSSam Leffler ni->ni_flags &= ~IEEE80211_NODE_QOS; 23068379e8dbSAdrian Chadd ni->ni_flags &= ~IEEE80211_NODE_UAPSD; 2307b032f27cSSam Leffler ni->ni_ath_flags = 0; 2308b032f27cSSam Leffler } 2309b032f27cSSam Leffler ieee80211_node_join(ni, resp); 2310b032f27cSSam Leffler ieee80211_deliver_l2uf(ni); 2311b032f27cSSam Leffler break; 2312b032f27cSSam Leffler } 2313b032f27cSSam Leffler 2314b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: 2315b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_DISASSOC: { 231605ea7a3eSBjoern A. Zeeb #ifdef IEEE80211_DEBUG 2317b032f27cSSam Leffler uint16_t reason; 231805ea7a3eSBjoern A. Zeeb #endif 2319b032f27cSSam Leffler 2320b032f27cSSam Leffler if (vap->iv_state != IEEE80211_S_RUN || 2321b032f27cSSam Leffler /* NB: can happen when in promiscuous mode */ 2322b032f27cSSam Leffler !IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { 2323b032f27cSSam Leffler vap->iv_stats.is_rx_mgtdiscard++; 2324b032f27cSSam Leffler break; 2325b032f27cSSam Leffler } 2326b032f27cSSam Leffler /* 2327b032f27cSSam Leffler * deauth/disassoc frame format 2328b032f27cSSam Leffler * [2] reason 2329b032f27cSSam Leffler */ 2330b032f27cSSam Leffler IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); 233105ea7a3eSBjoern A. Zeeb #ifdef IEEE80211_DEBUG 2332b032f27cSSam Leffler reason = le16toh(*(uint16_t *)frm); 233305ea7a3eSBjoern A. Zeeb #endif 2334b032f27cSSam Leffler if (subtype == IEEE80211_FC0_SUBTYPE_DEAUTH) { 2335b032f27cSSam Leffler vap->iv_stats.is_rx_deauth++; 2336b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_deauth); 2337b032f27cSSam Leffler } else { 2338b032f27cSSam Leffler vap->iv_stats.is_rx_disassoc++; 2339b032f27cSSam Leffler IEEE80211_NODE_STAT(ni, rx_disassoc); 2340b032f27cSSam Leffler } 2341b032f27cSSam Leffler IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, 2342d72d72d3SAndriy Voskoboinyk "recv %s (reason: %d (%s))", 23434357a5d1SAndriy Voskoboinyk ieee80211_mgt_subtype_name(subtype), 2344d72d72d3SAndriy Voskoboinyk reason, ieee80211_reason_to_string(reason)); 2345b032f27cSSam Leffler if (ni != vap->iv_bss) 2346b032f27cSSam Leffler ieee80211_node_leave(ni); 2347b032f27cSSam Leffler break; 2348b032f27cSSam Leffler } 2349b032f27cSSam Leffler 2350b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_ACTION: 235196283082SBernhard Schmidt case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: 2352893c4d6eSBernhard Schmidt if (ni == vap->iv_bss) { 2353893c4d6eSBernhard Schmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 2354893c4d6eSBernhard Schmidt wh, NULL, "%s", "unknown node"); 2355893c4d6eSBernhard Schmidt vap->iv_stats.is_rx_mgtdiscard++; 2356893c4d6eSBernhard Schmidt } else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && 2357893c4d6eSBernhard Schmidt !IEEE80211_IS_MULTICAST(wh->i_addr1)) { 2358893c4d6eSBernhard Schmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 2359893c4d6eSBernhard Schmidt wh, NULL, "%s", "not for us"); 2360893c4d6eSBernhard Schmidt vap->iv_stats.is_rx_mgtdiscard++; 2361893c4d6eSBernhard Schmidt } else if (vap->iv_state != IEEE80211_S_RUN) { 236296283082SBernhard Schmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 236396283082SBernhard Schmidt wh, NULL, "wrong state %s", 236496283082SBernhard Schmidt ieee80211_state_name[vap->iv_state]); 2365b032f27cSSam Leffler vap->iv_stats.is_rx_mgtdiscard++; 2366893c4d6eSBernhard Schmidt } else { 2367893c4d6eSBernhard Schmidt if (ieee80211_parse_action(ni, m0) == 0) 2368893c4d6eSBernhard Schmidt (void)ic->ic_recv_action(ni, wh, frm, efrm); 236996283082SBernhard Schmidt } 2370b032f27cSSam Leffler break; 2371b032f27cSSam Leffler 2372b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 2373b032f27cSSam Leffler case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 2374665d5ae9SAndriy Voskoboinyk case IEEE80211_FC0_SUBTYPE_TIMING_ADV: 237596283082SBernhard Schmidt case IEEE80211_FC0_SUBTYPE_ATIM: 237696283082SBernhard Schmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 237796283082SBernhard Schmidt wh, NULL, "%s", "not handled"); 237896283082SBernhard Schmidt vap->iv_stats.is_rx_mgtdiscard++; 237996283082SBernhard Schmidt break; 238096283082SBernhard Schmidt 2381b032f27cSSam Leffler default: 2382b032f27cSSam Leffler IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 2383b032f27cSSam Leffler wh, "mgt", "subtype 0x%x not handled", subtype); 2384b032f27cSSam Leffler vap->iv_stats.is_rx_badsubtype++; 2385b032f27cSSam Leffler break; 2386b032f27cSSam Leffler } 2387b032f27cSSam Leffler } 2388b032f27cSSam Leffler 238949eae5f7SSam Leffler static void 239049eae5f7SSam Leffler hostap_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) 239149eae5f7SSam Leffler { 239249eae5f7SSam Leffler switch (subtype) { 239349eae5f7SSam Leffler case IEEE80211_FC0_SUBTYPE_PS_POLL: 2394e7f0d7cfSAdrian Chadd ni->ni_vap->iv_recv_pspoll(ni, m); 239549eae5f7SSam Leffler break; 239649eae5f7SSam Leffler case IEEE80211_FC0_SUBTYPE_BAR: 239749eae5f7SSam Leffler ieee80211_recv_bar(ni, m); 239849eae5f7SSam Leffler break; 239949eae5f7SSam Leffler } 240049eae5f7SSam Leffler } 240149eae5f7SSam Leffler 2402b032f27cSSam Leffler /* 2403b032f27cSSam Leffler * Process a received ps-poll frame. 2404b032f27cSSam Leffler */ 2405e7f0d7cfSAdrian Chadd void 2406e7f0d7cfSAdrian Chadd ieee80211_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0) 2407b032f27cSSam Leffler { 2408b032f27cSSam Leffler struct ieee80211vap *vap = ni->ni_vap; 24095cda6006SAdrian Chadd struct ieee80211com *ic = vap->iv_ic; 2410b032f27cSSam Leffler struct ieee80211_frame_min *wh; 2411b032f27cSSam Leffler struct mbuf *m; 2412b032f27cSSam Leffler uint16_t aid; 2413b032f27cSSam Leffler int qlen; 2414b032f27cSSam Leffler 2415b032f27cSSam Leffler wh = mtod(m0, struct ieee80211_frame_min *); 2416b032f27cSSam Leffler if (ni->ni_associd == 0) { 2417b032f27cSSam Leffler IEEE80211_DISCARD(vap, 2418b032f27cSSam Leffler IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, 2419b032f27cSSam Leffler (struct ieee80211_frame *) wh, NULL, 2420b032f27cSSam Leffler "%s", "unassociated station"); 2421b032f27cSSam Leffler vap->iv_stats.is_ps_unassoc++; 2422b032f27cSSam Leffler IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 2423b032f27cSSam Leffler IEEE80211_REASON_NOT_ASSOCED); 2424b032f27cSSam Leffler return; 2425b032f27cSSam Leffler } 2426b032f27cSSam Leffler 2427b032f27cSSam Leffler aid = le16toh(*(uint16_t *)wh->i_dur); 2428b032f27cSSam Leffler if (aid != ni->ni_associd) { 2429b032f27cSSam Leffler IEEE80211_DISCARD(vap, 2430b032f27cSSam Leffler IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, 2431b032f27cSSam Leffler (struct ieee80211_frame *) wh, NULL, 2432b032f27cSSam Leffler "aid mismatch: sta aid 0x%x poll aid 0x%x", 2433b032f27cSSam Leffler ni->ni_associd, aid); 2434b032f27cSSam Leffler vap->iv_stats.is_ps_badaid++; 2435693e3122SSam Leffler /* 2436693e3122SSam Leffler * NB: We used to deauth the station but it turns out 2437693e3122SSam Leffler * the Blackberry Curve 8230 (and perhaps other devices) 2438693e3122SSam Leffler * sometimes send the wrong AID when WME is negotiated. 2439693e3122SSam Leffler * Being more lenient here seems ok as we already check 2440693e3122SSam Leffler * the station is associated and we only return frames 2441693e3122SSam Leffler * queued for the station (i.e. we don't use the AID). 2442693e3122SSam Leffler */ 2443b032f27cSSam Leffler return; 2444b032f27cSSam Leffler } 2445b032f27cSSam Leffler 2446b032f27cSSam Leffler /* Okay, take the first queued packet and put it out... */ 244763092fceSSam Leffler m = ieee80211_node_psq_dequeue(ni, &qlen); 2448b032f27cSSam Leffler if (m == NULL) { 2449b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_POWER, wh->i_addr2, 2450b032f27cSSam Leffler "%s", "recv ps-poll, but queue empty"); 2451b032f27cSSam Leffler ieee80211_send_nulldata(ieee80211_ref_node(ni)); 2452b032f27cSSam Leffler vap->iv_stats.is_ps_qempty++; /* XXX node stat */ 2453b032f27cSSam Leffler if (vap->iv_set_tim != NULL) 2454b032f27cSSam Leffler vap->iv_set_tim(ni, 0); /* just in case */ 2455b032f27cSSam Leffler return; 2456b032f27cSSam Leffler } 2457b032f27cSSam Leffler /* 2458b032f27cSSam Leffler * If there are more packets, set the more packets bit 2459b032f27cSSam Leffler * in the packet dispatched to the station; otherwise 2460b032f27cSSam Leffler * turn off the TIM bit. 2461b032f27cSSam Leffler */ 2462b032f27cSSam Leffler if (qlen != 0) { 2463b032f27cSSam Leffler IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 2464b032f27cSSam Leffler "recv ps-poll, send packet, %u still queued", qlen); 2465b032f27cSSam Leffler m->m_flags |= M_MORE_DATA; 2466b032f27cSSam Leffler } else { 2467b032f27cSSam Leffler IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 2468b032f27cSSam Leffler "%s", "recv ps-poll, send packet, queue empty"); 2469b032f27cSSam Leffler if (vap->iv_set_tim != NULL) 2470b032f27cSSam Leffler vap->iv_set_tim(ni, 0); 2471b032f27cSSam Leffler } 2472b032f27cSSam Leffler m->m_flags |= M_PWR_SAV; /* bypass PS handling */ 247363092fceSSam Leffler 24747ea3aadaSAdrian Chadd /* 24755cda6006SAdrian Chadd * Do the right thing; if it's an encap'ed frame then 2476d3a4ade3SAdrian Chadd * call ieee80211_parent_xmitpkt() else 2477e7495198SAdrian Chadd * call ieee80211_vap_xmitpkt(). 24787ea3aadaSAdrian Chadd */ 24795cda6006SAdrian Chadd if (m->m_flags & M_ENCAP) { 2480d3a4ade3SAdrian Chadd (void) ieee80211_parent_xmitpkt(ic, m); 24815cda6006SAdrian Chadd } else { 2482e7495198SAdrian Chadd (void) ieee80211_vap_xmitpkt(vap, m); 24837ea3aadaSAdrian Chadd } 2484b032f27cSSam Leffler } 2485