ieee80211_hostap.c revision 283535
1178354Ssam/*- 2178354Ssam * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting 3178354Ssam * All rights reserved. 4178354Ssam * 5178354Ssam * Redistribution and use in source and binary forms, with or without 6178354Ssam * modification, are permitted provided that the following conditions 7178354Ssam * are met: 8178354Ssam * 1. Redistributions of source code must retain the above copyright 9178354Ssam * notice, this list of conditions and the following disclaimer. 10178354Ssam * 2. Redistributions in binary form must reproduce the above copyright 11178354Ssam * notice, this list of conditions and the following disclaimer in the 12178354Ssam * documentation and/or other materials provided with the distribution. 13178354Ssam * 14178354Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15178354Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16178354Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17178354Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18178354Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19178354Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20178354Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21178354Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22178354Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23178354Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24178354Ssam */ 25178354Ssam 26178354Ssam#include <sys/cdefs.h> 27178354Ssam#ifdef __FreeBSD__ 28178354Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_hostap.c 283535 2015-05-25 16:37:41Z adrian $"); 29178354Ssam#endif 30178354Ssam 31178354Ssam/* 32178354Ssam * IEEE 802.11 HOSTAP mode support. 33178354Ssam */ 34178354Ssam#include "opt_inet.h" 35178354Ssam#include "opt_wlan.h" 36178354Ssam 37178354Ssam#include <sys/param.h> 38178354Ssam#include <sys/systm.h> 39178354Ssam#include <sys/mbuf.h> 40178354Ssam#include <sys/malloc.h> 41178354Ssam#include <sys/kernel.h> 42178354Ssam 43178354Ssam#include <sys/socket.h> 44178354Ssam#include <sys/sockio.h> 45178354Ssam#include <sys/endian.h> 46178354Ssam#include <sys/errno.h> 47178354Ssam#include <sys/proc.h> 48178354Ssam#include <sys/sysctl.h> 49178354Ssam 50178354Ssam#include <net/if.h> 51257176Sglebius#include <net/if_var.h> 52178354Ssam#include <net/if_media.h> 53178354Ssam#include <net/if_llc.h> 54178354Ssam#include <net/ethernet.h> 55178354Ssam 56178354Ssam#include <net/bpf.h> 57178354Ssam 58178354Ssam#include <net80211/ieee80211_var.h> 59178354Ssam#include <net80211/ieee80211_hostap.h> 60178354Ssam#include <net80211/ieee80211_input.h> 61190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 62190391Ssam#include <net80211/ieee80211_superg.h> 63190391Ssam#endif 64178354Ssam#include <net80211/ieee80211_wds.h> 65178354Ssam 66178354Ssam#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) 67178354Ssam 68178354Ssamstatic void hostap_vattach(struct ieee80211vap *); 69178354Ssamstatic int hostap_newstate(struct ieee80211vap *, enum ieee80211_state, int); 70178354Ssamstatic int hostap_input(struct ieee80211_node *ni, struct mbuf *m, 71283535Sadrian const struct ieee80211_rx_stats *, 72192468Ssam int rssi, int nf); 73178354Ssamstatic void hostap_deliver_data(struct ieee80211vap *, 74178354Ssam struct ieee80211_node *, struct mbuf *); 75178354Ssamstatic void hostap_recv_mgmt(struct ieee80211_node *, struct mbuf *, 76283535Sadrian int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf); 77191546Ssamstatic void hostap_recv_ctl(struct ieee80211_node *, struct mbuf *, int); 78178354Ssam 79178354Ssamvoid 80178354Ssamieee80211_hostap_attach(struct ieee80211com *ic) 81178354Ssam{ 82178354Ssam ic->ic_vattach[IEEE80211_M_HOSTAP] = hostap_vattach; 83178354Ssam} 84178354Ssam 85178354Ssamvoid 86178354Ssamieee80211_hostap_detach(struct ieee80211com *ic) 87178354Ssam{ 88178354Ssam} 89178354Ssam 90178354Ssamstatic void 91178354Ssamhostap_vdetach(struct ieee80211vap *vap) 92178354Ssam{ 93178354Ssam} 94178354Ssam 95178354Ssamstatic void 96178354Ssamhostap_vattach(struct ieee80211vap *vap) 97178354Ssam{ 98178354Ssam vap->iv_newstate = hostap_newstate; 99178354Ssam vap->iv_input = hostap_input; 100178354Ssam vap->iv_recv_mgmt = hostap_recv_mgmt; 101191546Ssam vap->iv_recv_ctl = hostap_recv_ctl; 102178354Ssam vap->iv_opdetach = hostap_vdetach; 103178354Ssam vap->iv_deliver_data = hostap_deliver_data; 104241138Sadrian vap->iv_recv_pspoll = ieee80211_recv_pspoll; 105178354Ssam} 106178354Ssam 107178354Ssamstatic void 108178354Ssamsta_disassoc(void *arg, struct ieee80211_node *ni) 109178354Ssam{ 110178354Ssam struct ieee80211vap *vap = arg; 111178354Ssam 112178354Ssam if (ni->ni_vap == vap && ni->ni_associd != 0) { 113178354Ssam IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC, 114178354Ssam IEEE80211_REASON_ASSOC_LEAVE); 115178354Ssam ieee80211_node_leave(ni); 116178354Ssam } 117178354Ssam} 118178354Ssam 119193413Ssamstatic void 120193413Ssamsta_csa(void *arg, struct ieee80211_node *ni) 121193413Ssam{ 122193413Ssam struct ieee80211vap *vap = arg; 123193413Ssam 124193413Ssam if (ni->ni_vap == vap && ni->ni_associd != 0) 125193413Ssam if (ni->ni_inact > vap->iv_inact_init) { 126193413Ssam ni->ni_inact = vap->iv_inact_init; 127193413Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, 128193413Ssam "%s: inact %u", __func__, ni->ni_inact); 129193413Ssam } 130193413Ssam} 131193413Ssam 132193414Ssamstatic void 133193414Ssamsta_drop(void *arg, struct ieee80211_node *ni) 134193414Ssam{ 135193414Ssam struct ieee80211vap *vap = arg; 136193414Ssam 137193414Ssam if (ni->ni_vap == vap && ni->ni_associd != 0) 138193414Ssam ieee80211_node_leave(ni); 139193414Ssam} 140193414Ssam 141178354Ssam/* 142193414Ssam * Does a channel change require associated stations to re-associate 143193414Ssam * so protocol state is correct. This is used when doing CSA across 144193414Ssam * bands or similar (e.g. HT -> legacy). 145193414Ssam */ 146193414Ssamstatic int 147193414Ssamisbandchange(struct ieee80211com *ic) 148193414Ssam{ 149193414Ssam return ((ic->ic_bsschan->ic_flags ^ ic->ic_csa_newchan->ic_flags) & 150193414Ssam (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_HALF | 151193414Ssam IEEE80211_CHAN_QUARTER | IEEE80211_CHAN_HT)) != 0; 152193414Ssam} 153193414Ssam 154193414Ssam/* 155178354Ssam * IEEE80211_M_HOSTAP vap state machine handler. 156178354Ssam */ 157178354Ssamstatic int 158178354Ssamhostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 159178354Ssam{ 160178354Ssam struct ieee80211com *ic = vap->iv_ic; 161178354Ssam enum ieee80211_state ostate; 162178354Ssam 163178354Ssam IEEE80211_LOCK_ASSERT(ic); 164178354Ssam 165178354Ssam ostate = vap->iv_state; 166178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 167178354Ssam __func__, ieee80211_state_name[ostate], 168178354Ssam ieee80211_state_name[nstate], arg); 169178354Ssam vap->iv_state = nstate; /* state transition */ 170178354Ssam if (ostate != IEEE80211_S_SCAN) 171178354Ssam ieee80211_cancel_scan(vap); /* background scan */ 172178354Ssam switch (nstate) { 173178354Ssam case IEEE80211_S_INIT: 174178354Ssam switch (ostate) { 175178354Ssam case IEEE80211_S_SCAN: 176178354Ssam ieee80211_cancel_scan(vap); 177178354Ssam break; 178178354Ssam case IEEE80211_S_CAC: 179178354Ssam ieee80211_dfs_cac_stop(vap); 180178354Ssam break; 181178354Ssam case IEEE80211_S_RUN: 182178354Ssam ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap); 183178354Ssam break; 184178354Ssam default: 185178354Ssam break; 186178354Ssam } 187178354Ssam if (ostate != IEEE80211_S_INIT) { 188178354Ssam /* NB: optimize INIT -> INIT case */ 189178354Ssam ieee80211_reset_bss(vap); 190178354Ssam } 191178354Ssam if (vap->iv_auth->ia_detach != NULL) 192178354Ssam vap->iv_auth->ia_detach(vap); 193178354Ssam break; 194178354Ssam case IEEE80211_S_SCAN: 195178354Ssam switch (ostate) { 196178354Ssam case IEEE80211_S_CSA: 197178354Ssam case IEEE80211_S_RUN: 198178354Ssam ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap); 199178354Ssam /* 200178354Ssam * Clear overlapping BSS state; the beacon frame 201178354Ssam * will be reconstructed on transition to the RUN 202178354Ssam * state and the timeout routines check if the flag 203178354Ssam * is set before doing anything so this is sufficient. 204178354Ssam */ 205178354Ssam ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR; 206193655Ssam ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR; 207178354Ssam /* fall thru... */ 208178354Ssam case IEEE80211_S_CAC: 209178354Ssam /* 210178354Ssam * NB: We may get here because of a manual channel 211178354Ssam * change in which case we need to stop CAC 212178354Ssam * XXX no need to stop if ostate RUN but it's ok 213178354Ssam */ 214178354Ssam ieee80211_dfs_cac_stop(vap); 215178354Ssam /* fall thru... */ 216178354Ssam case IEEE80211_S_INIT: 217178354Ssam if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && 218178354Ssam !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { 219178354Ssam /* 220178354Ssam * Already have a channel; bypass the 221178354Ssam * scan and startup immediately. 222178354Ssam * ieee80211_create_ibss will call back to 223178354Ssam * move us to RUN state. 224178354Ssam */ 225178354Ssam ieee80211_create_ibss(vap, vap->iv_des_chan); 226178354Ssam break; 227178354Ssam } 228178354Ssam /* 229178354Ssam * Initiate a scan. We can come here as a result 230178354Ssam * of an IEEE80211_IOC_SCAN_REQ too in which case 231178354Ssam * the vap will be marked with IEEE80211_FEXT_SCANREQ 232178354Ssam * and the scan request parameters will be present 233178354Ssam * in iv_scanreq. Otherwise we do the default. 234178354Ssam */ 235178354Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 236178354Ssam ieee80211_check_scan(vap, 237178354Ssam vap->iv_scanreq_flags, 238178354Ssam vap->iv_scanreq_duration, 239178354Ssam vap->iv_scanreq_mindwell, 240178354Ssam vap->iv_scanreq_maxdwell, 241178354Ssam vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 242178354Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 243178354Ssam } else 244178354Ssam ieee80211_check_scan_current(vap); 245178354Ssam break; 246178354Ssam case IEEE80211_S_SCAN: 247178354Ssam /* 248178354Ssam * A state change requires a reset; scan. 249178354Ssam */ 250178354Ssam ieee80211_check_scan_current(vap); 251178354Ssam break; 252178354Ssam default: 253178354Ssam break; 254178354Ssam } 255178354Ssam break; 256178354Ssam case IEEE80211_S_CAC: 257178354Ssam /* 258178354Ssam * Start CAC on a DFS channel. We come here when starting 259178354Ssam * a bss on a DFS channel (see ieee80211_create_ibss). 260178354Ssam */ 261178354Ssam ieee80211_dfs_cac_start(vap); 262178354Ssam break; 263178354Ssam case IEEE80211_S_RUN: 264178354Ssam if (vap->iv_flags & IEEE80211_F_WPA) { 265178354Ssam /* XXX validate prerequisites */ 266178354Ssam } 267178354Ssam switch (ostate) { 268178354Ssam case IEEE80211_S_INIT: 269178354Ssam /* 270178354Ssam * Already have a channel; bypass the 271178354Ssam * scan and startup immediately. 272178354Ssam * Note that ieee80211_create_ibss will call 273178354Ssam * back to do a RUN->RUN state change. 274178354Ssam */ 275178354Ssam ieee80211_create_ibss(vap, 276178354Ssam ieee80211_ht_adjust_channel(ic, 277193655Ssam ic->ic_curchan, vap->iv_flags_ht)); 278178354Ssam /* NB: iv_bss is changed on return */ 279178354Ssam break; 280178354Ssam case IEEE80211_S_CAC: 281178354Ssam /* 282178354Ssam * NB: This is the normal state change when CAC 283178354Ssam * expires and no radar was detected; no need to 284178354Ssam * clear the CAC timer as it's already expired. 285178354Ssam */ 286178354Ssam /* fall thru... */ 287178354Ssam case IEEE80211_S_CSA: 288178354Ssam /* 289193413Ssam * Shorten inactivity timer of associated stations 290193413Ssam * to weed out sta's that don't follow a CSA. 291193413Ssam */ 292193413Ssam ieee80211_iterate_nodes(&ic->ic_sta, sta_csa, vap); 293193413Ssam /* 294178354Ssam * Update bss node channel to reflect where 295178354Ssam * we landed after CSA. 296178354Ssam */ 297178354Ssam ieee80211_node_set_chan(vap->iv_bss, 298178354Ssam ieee80211_ht_adjust_channel(ic, ic->ic_curchan, 299178354Ssam ieee80211_htchanflags(vap->iv_bss->ni_chan))); 300178354Ssam /* XXX bypass debug msgs */ 301178354Ssam break; 302178354Ssam case IEEE80211_S_SCAN: 303178354Ssam case IEEE80211_S_RUN: 304178354Ssam#ifdef IEEE80211_DEBUG 305178354Ssam if (ieee80211_msg_debug(vap)) { 306178354Ssam struct ieee80211_node *ni = vap->iv_bss; 307178354Ssam ieee80211_note(vap, 308178354Ssam "synchronized with %s ssid ", 309178354Ssam ether_sprintf(ni->ni_bssid)); 310178354Ssam ieee80211_print_essid(ni->ni_essid, 311178354Ssam ni->ni_esslen); 312178354Ssam /* XXX MCS/HT */ 313178354Ssam printf(" channel %d start %uMb\n", 314178354Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), 315178354Ssam IEEE80211_RATE2MBS(ni->ni_txrate)); 316178354Ssam } 317178354Ssam#endif 318178354Ssam break; 319178354Ssam default: 320178354Ssam break; 321178354Ssam } 322178354Ssam /* 323178354Ssam * Start/stop the authenticator. We delay until here 324178354Ssam * to allow configuration to happen out of order. 325178354Ssam */ 326178354Ssam if (vap->iv_auth->ia_attach != NULL) { 327178354Ssam /* XXX check failure */ 328178354Ssam vap->iv_auth->ia_attach(vap); 329178354Ssam } else if (vap->iv_auth->ia_detach != NULL) { 330178354Ssam vap->iv_auth->ia_detach(vap); 331178354Ssam } 332178354Ssam ieee80211_node_authorize(vap->iv_bss); 333178354Ssam break; 334193414Ssam case IEEE80211_S_CSA: 335193414Ssam if (ostate == IEEE80211_S_RUN && isbandchange(ic)) { 336193414Ssam /* 337193414Ssam * On a ``band change'' silently drop associated 338193414Ssam * stations as they must re-associate before they 339193414Ssam * can pass traffic (as otherwise protocol state 340193414Ssam * such as capabilities and the negotiated rate 341193414Ssam * set may/will be wrong). 342193414Ssam */ 343193414Ssam ieee80211_iterate_nodes(&ic->ic_sta, sta_drop, vap); 344193414Ssam } 345193414Ssam break; 346178354Ssam default: 347178354Ssam break; 348178354Ssam } 349178354Ssam return 0; 350178354Ssam} 351178354Ssam 352178354Ssamstatic void 353178354Ssamhostap_deliver_data(struct ieee80211vap *vap, 354178354Ssam struct ieee80211_node *ni, struct mbuf *m) 355178354Ssam{ 356178354Ssam struct ether_header *eh = mtod(m, struct ether_header *); 357178354Ssam struct ifnet *ifp = vap->iv_ifp; 358178354Ssam 359193292Ssam /* clear driver/net80211 flags before passing up */ 360254640Sadrian#if __FreeBSD_version >= 1000046 361254523Sandre m->m_flags &= ~(M_MCAST | M_BCAST); 362254523Sandre m_clrprotoflags(m); 363254640Sadrian#else 364254640Sadrian m->m_flags &= ~(M_80211_RX | M_MCAST | M_BCAST); 365254640Sadrian#endif 366193292Ssam 367178354Ssam KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP, 368178354Ssam ("gack, opmode %d", vap->iv_opmode)); 369178354Ssam /* 370178354Ssam * Do accounting. 371178354Ssam */ 372271861Sglebius if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 373178354Ssam IEEE80211_NODE_STAT(ni, rx_data); 374178354Ssam IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len); 375178354Ssam if (ETHER_IS_MULTICAST(eh->ether_dhost)) { 376178354Ssam m->m_flags |= M_MCAST; /* XXX M_BCAST? */ 377178354Ssam IEEE80211_NODE_STAT(ni, rx_mcast); 378178354Ssam } else 379178354Ssam IEEE80211_NODE_STAT(ni, rx_ucast); 380178354Ssam 381178354Ssam /* perform as a bridge within the AP */ 382178354Ssam if ((vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0) { 383178354Ssam struct mbuf *mcopy = NULL; 384178354Ssam 385178354Ssam if (m->m_flags & M_MCAST) { 386243882Sglebius mcopy = m_dup(m, M_NOWAIT); 387178354Ssam if (mcopy == NULL) 388271861Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 389178354Ssam else 390178354Ssam mcopy->m_flags |= M_MCAST; 391178354Ssam } else { 392178354Ssam /* 393178354Ssam * Check if the destination is associated with the 394178354Ssam * same vap and authorized to receive traffic. 395178354Ssam * Beware of traffic destined for the vap itself; 396178354Ssam * sending it will not work; just let it be delivered 397178354Ssam * normally. 398178354Ssam */ 399178354Ssam struct ieee80211_node *sta = ieee80211_find_vap_node( 400178354Ssam &vap->iv_ic->ic_sta, vap, eh->ether_dhost); 401178354Ssam if (sta != NULL) { 402178354Ssam if (ieee80211_node_is_authorized(sta)) { 403178354Ssam /* 404178354Ssam * Beware of sending to ourself; this 405178354Ssam * needs to happen via the normal 406178354Ssam * input path. 407178354Ssam */ 408178354Ssam if (sta != vap->iv_bss) { 409178354Ssam mcopy = m; 410178354Ssam m = NULL; 411178354Ssam } 412178354Ssam } else { 413178354Ssam vap->iv_stats.is_rx_unauth++; 414178354Ssam IEEE80211_NODE_STAT(sta, rx_unauth); 415178354Ssam } 416178354Ssam ieee80211_free_node(sta); 417178354Ssam } 418178354Ssam } 419178354Ssam if (mcopy != NULL) { 420178354Ssam int len, err; 421178354Ssam len = mcopy->m_pkthdr.len; 422254082Sadrian err = ieee80211_vap_xmitpkt(vap, mcopy); 423178354Ssam if (err) { 424178354Ssam /* NB: IFQ_HANDOFF reclaims mcopy */ 425178354Ssam } else { 426271861Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 427178354Ssam } 428178354Ssam } 429178354Ssam } 430178354Ssam if (m != NULL) { 431178354Ssam /* 432178354Ssam * Mark frame as coming from vap's interface. 433178354Ssam */ 434178354Ssam m->m_pkthdr.rcvif = ifp; 435178354Ssam if (m->m_flags & M_MCAST) { 436178354Ssam /* 437178354Ssam * Spam DWDS vap's w/ multicast traffic. 438178354Ssam */ 439178354Ssam /* XXX only if dwds in use? */ 440178354Ssam ieee80211_dwds_mcast(vap, m); 441178354Ssam } 442178354Ssam if (ni->ni_vlan != 0) { 443178354Ssam /* attach vlan tag */ 444178354Ssam m->m_pkthdr.ether_vtag = ni->ni_vlan; 445178354Ssam m->m_flags |= M_VLANTAG; 446178354Ssam } 447178354Ssam ifp->if_input(ifp, m); 448178354Ssam } 449178354Ssam} 450178354Ssam 451178354Ssam/* 452178354Ssam * Decide if a received management frame should be 453178354Ssam * printed when debugging is enabled. This filters some 454178354Ssam * of the less interesting frames that come frequently 455178354Ssam * (e.g. beacons). 456178354Ssam */ 457178354Ssamstatic __inline int 458178354Ssamdoprint(struct ieee80211vap *vap, int subtype) 459178354Ssam{ 460178354Ssam switch (subtype) { 461178354Ssam case IEEE80211_FC0_SUBTYPE_BEACON: 462178354Ssam return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); 463178354Ssam case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 464178354Ssam return 0; 465178354Ssam } 466178354Ssam return 1; 467178354Ssam} 468178354Ssam 469178354Ssam/* 470178354Ssam * Process a received frame. The node associated with the sender 471178354Ssam * should be supplied. If nothing was found in the node table then 472178354Ssam * the caller is assumed to supply a reference to iv_bss instead. 473178354Ssam * The RSSI and a timestamp are also supplied. The RSSI data is used 474178354Ssam * during AP scanning to select a AP to associate with; it can have 475178354Ssam * any units so long as values have consistent units and higher values 476178354Ssam * mean ``better signal''. The receive timestamp is currently not used 477178354Ssam * by the 802.11 layer. 478178354Ssam */ 479178354Ssamstatic int 480283535Sadrianhostap_input(struct ieee80211_node *ni, struct mbuf *m, 481283535Sadrian const struct ieee80211_rx_stats *rxs, int rssi, int nf) 482178354Ssam{ 483178354Ssam struct ieee80211vap *vap = ni->ni_vap; 484178354Ssam struct ieee80211com *ic = ni->ni_ic; 485178354Ssam struct ifnet *ifp = vap->iv_ifp; 486178354Ssam struct ieee80211_frame *wh; 487178354Ssam struct ieee80211_key *key; 488178354Ssam struct ether_header *eh; 489203422Srpaulo int hdrspace, need_tap = 1; /* mbuf need to be tapped. */ 490178354Ssam uint8_t dir, type, subtype, qos; 491178354Ssam uint8_t *bssid; 492178354Ssam uint16_t rxseq; 493178354Ssam 494183247Ssam if (m->m_flags & M_AMPDU_MPDU) { 495178354Ssam /* 496178354Ssam * Fastpath for A-MPDU reorder q resubmission. Frames 497183247Ssam * w/ M_AMPDU_MPDU marked have already passed through 498183247Ssam * here but were received out of order and been held on 499183247Ssam * the reorder queue. When resubmitted they are marked 500183247Ssam * with the M_AMPDU_MPDU flag and we can bypass most of 501183247Ssam * the normal processing. 502178354Ssam */ 503178354Ssam wh = mtod(m, struct ieee80211_frame *); 504178354Ssam type = IEEE80211_FC0_TYPE_DATA; 505178354Ssam dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 506178354Ssam subtype = IEEE80211_FC0_SUBTYPE_QOS; 507178354Ssam hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ 508178354Ssam goto resubmit_ampdu; 509178354Ssam } 510178354Ssam 511178354Ssam KASSERT(ni != NULL, ("null node")); 512178354Ssam ni->ni_inact = ni->ni_inact_reload; 513178354Ssam 514178354Ssam type = -1; /* undefined */ 515178354Ssam 516178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { 517178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 518178354Ssam ni->ni_macaddr, NULL, 519178354Ssam "too short (1): len %u", m->m_pkthdr.len); 520178354Ssam vap->iv_stats.is_rx_tooshort++; 521178354Ssam goto out; 522178354Ssam } 523178354Ssam /* 524178354Ssam * Bit of a cheat here, we use a pointer for a 3-address 525178354Ssam * frame format but don't reference fields past outside 526178354Ssam * ieee80211_frame_min w/o first validating the data is 527178354Ssam * present. 528178354Ssam */ 529178354Ssam wh = mtod(m, struct ieee80211_frame *); 530178354Ssam 531178354Ssam if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 532178354Ssam IEEE80211_FC0_VERSION_0) { 533178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 534191547Ssam ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x", 535191547Ssam wh->i_fc[0], wh->i_fc[1]); 536178354Ssam vap->iv_stats.is_rx_badversion++; 537178354Ssam goto err; 538178354Ssam } 539178354Ssam 540178354Ssam dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 541178354Ssam type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 542178354Ssam subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 543178354Ssam if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 544178354Ssam if (dir != IEEE80211_FC1_DIR_NODS) 545178354Ssam bssid = wh->i_addr1; 546178354Ssam else if (type == IEEE80211_FC0_TYPE_CTL) 547178354Ssam bssid = wh->i_addr1; 548178354Ssam else { 549178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 550178354Ssam IEEE80211_DISCARD_MAC(vap, 551178354Ssam IEEE80211_MSG_ANY, ni->ni_macaddr, 552178354Ssam NULL, "too short (2): len %u", 553178354Ssam m->m_pkthdr.len); 554178354Ssam vap->iv_stats.is_rx_tooshort++; 555178354Ssam goto out; 556178354Ssam } 557178354Ssam bssid = wh->i_addr3; 558178354Ssam } 559178354Ssam /* 560178354Ssam * Validate the bssid. 561178354Ssam */ 562178354Ssam if (!(type == IEEE80211_FC0_TYPE_MGT && 563178354Ssam subtype == IEEE80211_FC0_SUBTYPE_BEACON) && 564178354Ssam !IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && 565178354Ssam !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { 566178354Ssam /* not interested in */ 567178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 568178354Ssam bssid, NULL, "%s", "not to bss"); 569178354Ssam vap->iv_stats.is_rx_wrongbss++; 570178354Ssam goto out; 571178354Ssam } 572178354Ssam 573178354Ssam IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 574192468Ssam ni->ni_noise = nf; 575282820Sadrian if (IEEE80211_HAS_SEQ(type, subtype)) { 576178354Ssam uint8_t tid = ieee80211_gettid(wh); 577178354Ssam if (IEEE80211_QOS_HAS_SEQ(wh) && 578178354Ssam TID_TO_WME_AC(tid) >= WME_AC_VI) 579178354Ssam ic->ic_wme.wme_hipri_traffic++; 580178354Ssam rxseq = le16toh(*(uint16_t *)wh->i_seq); 581221418Sadrian if (! ieee80211_check_rxseq(ni, wh)) { 582178354Ssam /* duplicate, discard */ 583178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 584178354Ssam bssid, "duplicate", 585178354Ssam "seqno <%u,%u> fragno <%u,%u> tid %u", 586178354Ssam rxseq >> IEEE80211_SEQ_SEQ_SHIFT, 587178354Ssam ni->ni_rxseqs[tid] >> 588178354Ssam IEEE80211_SEQ_SEQ_SHIFT, 589178354Ssam rxseq & IEEE80211_SEQ_FRAG_MASK, 590178354Ssam ni->ni_rxseqs[tid] & 591178354Ssam IEEE80211_SEQ_FRAG_MASK, 592178354Ssam tid); 593178354Ssam vap->iv_stats.is_rx_dup++; 594178354Ssam IEEE80211_NODE_STAT(ni, rx_dup); 595178354Ssam goto out; 596178354Ssam } 597178354Ssam ni->ni_rxseqs[tid] = rxseq; 598178354Ssam } 599178354Ssam } 600178354Ssam 601178354Ssam switch (type) { 602178354Ssam case IEEE80211_FC0_TYPE_DATA: 603178354Ssam hdrspace = ieee80211_hdrspace(ic, wh); 604178354Ssam if (m->m_len < hdrspace && 605178354Ssam (m = m_pullup(m, hdrspace)) == NULL) { 606178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 607178354Ssam ni->ni_macaddr, NULL, 608178354Ssam "data too short: expecting %u", hdrspace); 609178354Ssam vap->iv_stats.is_rx_tooshort++; 610178354Ssam goto out; /* XXX */ 611178354Ssam } 612178354Ssam if (!(dir == IEEE80211_FC1_DIR_TODS || 613178354Ssam (dir == IEEE80211_FC1_DIR_DSTODS && 614178354Ssam (vap->iv_flags & IEEE80211_F_DWDS)))) { 615178354Ssam if (dir != IEEE80211_FC1_DIR_DSTODS) { 616178354Ssam IEEE80211_DISCARD(vap, 617178354Ssam IEEE80211_MSG_INPUT, wh, "data", 618178354Ssam "incorrect dir 0x%x", dir); 619178354Ssam } else { 620178354Ssam IEEE80211_DISCARD(vap, 621178354Ssam IEEE80211_MSG_INPUT | 622178354Ssam IEEE80211_MSG_WDS, wh, 623178354Ssam "4-address data", 624178354Ssam "%s", "DWDS not enabled"); 625178354Ssam } 626178354Ssam vap->iv_stats.is_rx_wrongdir++; 627178354Ssam goto out; 628178354Ssam } 629178354Ssam /* check if source STA is associated */ 630178354Ssam if (ni == vap->iv_bss) { 631178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 632178354Ssam wh, "data", "%s", "unknown src"); 633178354Ssam ieee80211_send_error(ni, wh->i_addr2, 634178354Ssam IEEE80211_FC0_SUBTYPE_DEAUTH, 635178354Ssam IEEE80211_REASON_NOT_AUTHED); 636178354Ssam vap->iv_stats.is_rx_notassoc++; 637178354Ssam goto err; 638178354Ssam } 639178354Ssam if (ni->ni_associd == 0) { 640178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 641178354Ssam wh, "data", "%s", "unassoc src"); 642178354Ssam IEEE80211_SEND_MGMT(ni, 643178354Ssam IEEE80211_FC0_SUBTYPE_DISASSOC, 644178354Ssam IEEE80211_REASON_NOT_ASSOCED); 645178354Ssam vap->iv_stats.is_rx_notassoc++; 646178354Ssam goto err; 647178354Ssam } 648178354Ssam 649178354Ssam /* 650178354Ssam * Check for power save state change. 651178354Ssam * XXX out-of-order A-MPDU frames? 652178354Ssam */ 653178354Ssam if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ 654178354Ssam (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) 655241138Sadrian vap->iv_node_ps(ni, 656178354Ssam wh->i_fc[1] & IEEE80211_FC1_PWR_MGT); 657178354Ssam /* 658178354Ssam * For 4-address packets handle WDS discovery 659178354Ssam * notifications. Once a WDS link is setup frames 660178354Ssam * are just delivered to the WDS vap (see below). 661178354Ssam */ 662178354Ssam if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap == NULL) { 663178354Ssam if (!ieee80211_node_is_authorized(ni)) { 664178354Ssam IEEE80211_DISCARD(vap, 665178354Ssam IEEE80211_MSG_INPUT | 666178354Ssam IEEE80211_MSG_WDS, wh, 667178354Ssam "4-address data", 668178354Ssam "%s", "unauthorized port"); 669178354Ssam vap->iv_stats.is_rx_unauth++; 670178354Ssam IEEE80211_NODE_STAT(ni, rx_unauth); 671178354Ssam goto err; 672178354Ssam } 673178354Ssam ieee80211_dwds_discover(ni, m); 674178354Ssam return type; 675178354Ssam } 676178354Ssam 677178354Ssam /* 678183247Ssam * Handle A-MPDU re-ordering. If the frame is to be 679183247Ssam * processed directly then ieee80211_ampdu_reorder 680178354Ssam * will return 0; otherwise it has consumed the mbuf 681178354Ssam * and we should do nothing more with it. 682178354Ssam */ 683183247Ssam if ((m->m_flags & M_AMPDU) && 684178354Ssam ieee80211_ampdu_reorder(ni, m) != 0) { 685178354Ssam m = NULL; 686178354Ssam goto out; 687178354Ssam } 688178354Ssam resubmit_ampdu: 689178354Ssam 690178354Ssam /* 691178354Ssam * Handle privacy requirements. Note that we 692178354Ssam * must not be preempted from here until after 693178354Ssam * we (potentially) call ieee80211_crypto_demic; 694178354Ssam * otherwise we may violate assumptions in the 695178354Ssam * crypto cipher modules used to do delayed update 696178354Ssam * of replay sequence numbers. 697178354Ssam */ 698260444Skevlo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 699178354Ssam if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 700178354Ssam /* 701178354Ssam * Discard encrypted frames when privacy is off. 702178354Ssam */ 703178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 704178354Ssam wh, "WEP", "%s", "PRIVACY off"); 705178354Ssam vap->iv_stats.is_rx_noprivacy++; 706178354Ssam IEEE80211_NODE_STAT(ni, rx_noprivacy); 707178354Ssam goto out; 708178354Ssam } 709178354Ssam key = ieee80211_crypto_decap(ni, m, hdrspace); 710178354Ssam if (key == NULL) { 711178354Ssam /* NB: stats+msgs handled in crypto_decap */ 712178354Ssam IEEE80211_NODE_STAT(ni, rx_wepfail); 713178354Ssam goto out; 714178354Ssam } 715178354Ssam wh = mtod(m, struct ieee80211_frame *); 716260444Skevlo wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; 717178354Ssam } else { 718178354Ssam /* XXX M_WEP and IEEE80211_F_PRIVACY */ 719178354Ssam key = NULL; 720178354Ssam } 721178354Ssam 722178354Ssam /* 723178354Ssam * Save QoS bits for use below--before we strip the header. 724178354Ssam */ 725178354Ssam if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { 726178354Ssam qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? 727178354Ssam ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : 728178354Ssam ((struct ieee80211_qosframe *)wh)->i_qos[0]; 729178354Ssam } else 730178354Ssam qos = 0; 731178354Ssam 732178354Ssam /* 733178354Ssam * Next up, any fragmentation. 734178354Ssam */ 735178354Ssam if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 736178354Ssam m = ieee80211_defrag(ni, m, hdrspace); 737178354Ssam if (m == NULL) { 738178354Ssam /* Fragment dropped or frame not complete yet */ 739178354Ssam goto out; 740178354Ssam } 741178354Ssam } 742178354Ssam wh = NULL; /* no longer valid, catch any uses */ 743178354Ssam 744178354Ssam /* 745178354Ssam * Next strip any MSDU crypto bits. 746178354Ssam */ 747178354Ssam if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) { 748178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 749178354Ssam ni->ni_macaddr, "data", "%s", "demic error"); 750178354Ssam vap->iv_stats.is_rx_demicfail++; 751178354Ssam IEEE80211_NODE_STAT(ni, rx_demicfail); 752178354Ssam goto out; 753178354Ssam } 754178354Ssam /* copy to listener after decrypt */ 755192468Ssam if (ieee80211_radiotap_active_vap(vap)) 756192468Ssam ieee80211_radiotap_rx(vap, m); 757178354Ssam need_tap = 0; 758178354Ssam /* 759178354Ssam * Finally, strip the 802.11 header. 760178354Ssam */ 761178354Ssam m = ieee80211_decap(vap, m, hdrspace); 762178354Ssam if (m == NULL) { 763178354Ssam /* XXX mask bit to check for both */ 764178354Ssam /* don't count Null data frames as errors */ 765178354Ssam if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || 766178354Ssam subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) 767178354Ssam goto out; 768178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 769178354Ssam ni->ni_macaddr, "data", "%s", "decap error"); 770178354Ssam vap->iv_stats.is_rx_decap++; 771178354Ssam IEEE80211_NODE_STAT(ni, rx_decap); 772178354Ssam goto err; 773178354Ssam } 774178354Ssam eh = mtod(m, struct ether_header *); 775178354Ssam if (!ieee80211_node_is_authorized(ni)) { 776178354Ssam /* 777178354Ssam * Deny any non-PAE frames received prior to 778178354Ssam * authorization. For open/shared-key 779178354Ssam * authentication the port is mark authorized 780178354Ssam * after authentication completes. For 802.1x 781178354Ssam * the port is not marked authorized by the 782178354Ssam * authenticator until the handshake has completed. 783178354Ssam */ 784178354Ssam if (eh->ether_type != htons(ETHERTYPE_PAE)) { 785178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 786178354Ssam eh->ether_shost, "data", 787178354Ssam "unauthorized port: ether type 0x%x len %u", 788178354Ssam eh->ether_type, m->m_pkthdr.len); 789178354Ssam vap->iv_stats.is_rx_unauth++; 790178354Ssam IEEE80211_NODE_STAT(ni, rx_unauth); 791178354Ssam goto err; 792178354Ssam } 793178354Ssam } else { 794178354Ssam /* 795178354Ssam * When denying unencrypted frames, discard 796178354Ssam * any non-PAE frames received without encryption. 797178354Ssam */ 798178354Ssam if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && 799178354Ssam (key == NULL && (m->m_flags & M_WEP) == 0) && 800178354Ssam eh->ether_type != htons(ETHERTYPE_PAE)) { 801178354Ssam /* 802178354Ssam * Drop unencrypted frames. 803178354Ssam */ 804178354Ssam vap->iv_stats.is_rx_unencrypted++; 805178354Ssam IEEE80211_NODE_STAT(ni, rx_unencrypted); 806178354Ssam goto out; 807178354Ssam } 808178354Ssam } 809178354Ssam /* XXX require HT? */ 810178354Ssam if (qos & IEEE80211_QOS_AMSDU) { 811178354Ssam m = ieee80211_decap_amsdu(ni, m); 812178354Ssam if (m == NULL) 813178354Ssam return IEEE80211_FC0_TYPE_DATA; 814190391Ssam } else { 815190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 816190391Ssam m = ieee80211_decap_fastframe(vap, ni, m); 817190391Ssam if (m == NULL) 818178354Ssam return IEEE80211_FC0_TYPE_DATA; 819190391Ssam#endif 820178354Ssam } 821178354Ssam if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL) 822178354Ssam ieee80211_deliver_data(ni->ni_wdsvap, ni, m); 823178354Ssam else 824178354Ssam hostap_deliver_data(vap, ni, m); 825178354Ssam return IEEE80211_FC0_TYPE_DATA; 826178354Ssam 827178354Ssam case IEEE80211_FC0_TYPE_MGT: 828178354Ssam vap->iv_stats.is_rx_mgmt++; 829178354Ssam IEEE80211_NODE_STAT(ni, rx_mgmt); 830178354Ssam if (dir != IEEE80211_FC1_DIR_NODS) { 831178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 832178354Ssam wh, "mgt", "incorrect dir 0x%x", dir); 833178354Ssam vap->iv_stats.is_rx_wrongdir++; 834178354Ssam goto err; 835178354Ssam } 836178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 837178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 838178354Ssam ni->ni_macaddr, "mgt", "too short: len %u", 839178354Ssam m->m_pkthdr.len); 840178354Ssam vap->iv_stats.is_rx_tooshort++; 841178354Ssam goto out; 842178354Ssam } 843178354Ssam if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { 844178354Ssam /* ensure return frames are unicast */ 845178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 846178354Ssam wh, NULL, "source is multicast: %s", 847178354Ssam ether_sprintf(wh->i_addr2)); 848178354Ssam vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ 849178354Ssam goto out; 850178354Ssam } 851178354Ssam#ifdef IEEE80211_DEBUG 852178354Ssam if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || 853178354Ssam ieee80211_msg_dumppkts(vap)) { 854178354Ssam if_printf(ifp, "received %s from %s rssi %d\n", 855178354Ssam ieee80211_mgt_subtype_name[subtype >> 856178354Ssam IEEE80211_FC0_SUBTYPE_SHIFT], 857178354Ssam ether_sprintf(wh->i_addr2), rssi); 858178354Ssam } 859178354Ssam#endif 860260444Skevlo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 861178354Ssam if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { 862178354Ssam /* 863178354Ssam * Only shared key auth frames with a challenge 864178354Ssam * should be encrypted, discard all others. 865178354Ssam */ 866178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 867178354Ssam wh, NULL, 868178354Ssam "%s", "WEP set but not permitted"); 869178354Ssam vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ 870178354Ssam goto out; 871178354Ssam } 872178354Ssam if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 873178354Ssam /* 874178354Ssam * Discard encrypted frames when privacy is off. 875178354Ssam */ 876178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 877178354Ssam wh, NULL, "%s", "WEP set but PRIVACY off"); 878178354Ssam vap->iv_stats.is_rx_noprivacy++; 879178354Ssam goto out; 880178354Ssam } 881178354Ssam hdrspace = ieee80211_hdrspace(ic, wh); 882178354Ssam key = ieee80211_crypto_decap(ni, m, hdrspace); 883178354Ssam if (key == NULL) { 884178354Ssam /* NB: stats+msgs handled in crypto_decap */ 885178354Ssam goto out; 886178354Ssam } 887178354Ssam wh = mtod(m, struct ieee80211_frame *); 888260444Skevlo wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; 889178354Ssam } 890205791Srpaulo /* 891205791Srpaulo * Pass the packet to radiotap before calling iv_recv_mgmt(). 892205791Srpaulo * Otherwise iv_recv_mgmt() might pass another packet to 893205791Srpaulo * radiotap, resulting in out of order packet captures. 894205791Srpaulo */ 895205516Srpaulo if (ieee80211_radiotap_active_vap(vap)) 896205516Srpaulo ieee80211_radiotap_rx(vap, m); 897205516Srpaulo need_tap = 0; 898283535Sadrian vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); 899191534Ssam goto out; 900178354Ssam 901178354Ssam case IEEE80211_FC0_TYPE_CTL: 902178354Ssam vap->iv_stats.is_rx_ctl++; 903178354Ssam IEEE80211_NODE_STAT(ni, rx_ctrl); 904191546Ssam vap->iv_recv_ctl(ni, m, subtype); 905178354Ssam goto out; 906178354Ssam default: 907178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 908178354Ssam wh, "bad", "frame type 0x%x", type); 909178354Ssam /* should not come here */ 910178354Ssam break; 911178354Ssam } 912178354Ssamerr: 913271861Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 914178354Ssamout: 915178354Ssam if (m != NULL) { 916192765Ssam if (need_tap && ieee80211_radiotap_active_vap(vap)) 917192468Ssam ieee80211_radiotap_rx(vap, m); 918178354Ssam m_freem(m); 919178354Ssam } 920178354Ssam return type; 921178354Ssam} 922178354Ssam 923178354Ssamstatic void 924178354Ssamhostap_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh, 925192468Ssam int rssi, int nf, uint16_t seq, uint16_t status) 926178354Ssam{ 927178354Ssam struct ieee80211vap *vap = ni->ni_vap; 928178354Ssam 929178354Ssam KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state)); 930178354Ssam 931178354Ssam if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { 932178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 933178354Ssam ni->ni_macaddr, "open auth", 934178354Ssam "bad sta auth mode %u", ni->ni_authmode); 935178354Ssam vap->iv_stats.is_rx_bad_auth++; /* XXX */ 936178354Ssam /* 937178354Ssam * Clear any challenge text that may be there if 938178354Ssam * a previous shared key auth failed and then an 939178354Ssam * open auth is attempted. 940178354Ssam */ 941178354Ssam if (ni->ni_challenge != NULL) { 942186302Ssam free(ni->ni_challenge, M_80211_NODE); 943178354Ssam ni->ni_challenge = NULL; 944178354Ssam } 945178354Ssam /* XXX hack to workaround calling convention */ 946178354Ssam ieee80211_send_error(ni, wh->i_addr2, 947178354Ssam IEEE80211_FC0_SUBTYPE_AUTH, 948178354Ssam (seq + 1) | (IEEE80211_STATUS_ALG<<16)); 949178354Ssam return; 950178354Ssam } 951178354Ssam if (seq != IEEE80211_AUTH_OPEN_REQUEST) { 952178354Ssam vap->iv_stats.is_rx_bad_auth++; 953178354Ssam return; 954178354Ssam } 955178354Ssam /* always accept open authentication requests */ 956178354Ssam if (ni == vap->iv_bss) { 957178354Ssam ni = ieee80211_dup_bss(vap, wh->i_addr2); 958178354Ssam if (ni == NULL) 959178354Ssam return; 960178354Ssam } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) 961178354Ssam (void) ieee80211_ref_node(ni); 962178354Ssam /* 963178354Ssam * Mark the node as referenced to reflect that it's 964178354Ssam * reference count has been bumped to insure it remains 965178354Ssam * after the transaction completes. 966178354Ssam */ 967178354Ssam ni->ni_flags |= IEEE80211_NODE_AREF; 968186099Ssam /* 969186151Ssam * Mark the node as requiring a valid association id 970186099Ssam * before outbound traffic is permitted. 971186099Ssam */ 972186099Ssam ni->ni_flags |= IEEE80211_NODE_ASSOCID; 973178354Ssam 974178354Ssam if (vap->iv_acl != NULL && 975178354Ssam vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) { 976178354Ssam /* 977178354Ssam * When the ACL policy is set to RADIUS we defer the 978178354Ssam * authorization to a user agent. Dispatch an event, 979178354Ssam * a subsequent MLME call will decide the fate of the 980178354Ssam * station. If the user agent is not present then the 981178354Ssam * node will be reclaimed due to inactivity. 982178354Ssam */ 983178354Ssam IEEE80211_NOTE_MAC(vap, 984178354Ssam IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, ni->ni_macaddr, 985178354Ssam "%s", "station authentication defered (radius acl)"); 986178354Ssam ieee80211_notify_node_auth(ni); 987178354Ssam } else { 988178354Ssam IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 989178354Ssam IEEE80211_NOTE_MAC(vap, 990178354Ssam IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni->ni_macaddr, 991178354Ssam "%s", "station authenticated (open)"); 992178354Ssam /* 993178354Ssam * When 802.1x is not in use mark the port 994178354Ssam * authorized at this point so traffic can flow. 995178354Ssam */ 996178354Ssam if (ni->ni_authmode != IEEE80211_AUTH_8021X) 997178354Ssam ieee80211_node_authorize(ni); 998178354Ssam } 999178354Ssam} 1000178354Ssam 1001178354Ssamstatic void 1002178354Ssamhostap_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh, 1003192468Ssam uint8_t *frm, uint8_t *efrm, int rssi, int nf, 1004178354Ssam uint16_t seq, uint16_t status) 1005178354Ssam{ 1006178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1007178354Ssam uint8_t *challenge; 1008178354Ssam int allocbs, estatus; 1009178354Ssam 1010178354Ssam KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state)); 1011178354Ssam 1012178354Ssam /* 1013178354Ssam * NB: this can happen as we allow pre-shared key 1014178354Ssam * authentication to be enabled w/o wep being turned 1015178354Ssam * on so that configuration of these can be done 1016178354Ssam * in any order. It may be better to enforce the 1017178354Ssam * ordering in which case this check would just be 1018178354Ssam * for sanity/consistency. 1019178354Ssam */ 1020178354Ssam if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 1021178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1022178354Ssam ni->ni_macaddr, "shared key auth", 1023178354Ssam "%s", " PRIVACY is disabled"); 1024178354Ssam estatus = IEEE80211_STATUS_ALG; 1025178354Ssam goto bad; 1026178354Ssam } 1027178354Ssam /* 1028178354Ssam * Pre-shared key authentication is evil; accept 1029178354Ssam * it only if explicitly configured (it is supported 1030178354Ssam * mainly for compatibility with clients like Mac OS X). 1031178354Ssam */ 1032178354Ssam if (ni->ni_authmode != IEEE80211_AUTH_AUTO && 1033178354Ssam ni->ni_authmode != IEEE80211_AUTH_SHARED) { 1034178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1035178354Ssam ni->ni_macaddr, "shared key auth", 1036178354Ssam "bad sta auth mode %u", ni->ni_authmode); 1037178354Ssam vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ 1038178354Ssam estatus = IEEE80211_STATUS_ALG; 1039178354Ssam goto bad; 1040178354Ssam } 1041178354Ssam 1042178354Ssam challenge = NULL; 1043178354Ssam if (frm + 1 < efrm) { 1044178354Ssam if ((frm[1] + 2) > (efrm - frm)) { 1045178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1046178354Ssam ni->ni_macaddr, "shared key auth", 1047178354Ssam "ie %d/%d too long", 1048178354Ssam frm[0], (frm[1] + 2) - (efrm - frm)); 1049178354Ssam vap->iv_stats.is_rx_bad_auth++; 1050178354Ssam estatus = IEEE80211_STATUS_CHALLENGE; 1051178354Ssam goto bad; 1052178354Ssam } 1053178354Ssam if (*frm == IEEE80211_ELEMID_CHALLENGE) 1054178354Ssam challenge = frm; 1055178354Ssam frm += frm[1] + 2; 1056178354Ssam } 1057178354Ssam switch (seq) { 1058178354Ssam case IEEE80211_AUTH_SHARED_CHALLENGE: 1059178354Ssam case IEEE80211_AUTH_SHARED_RESPONSE: 1060178354Ssam if (challenge == NULL) { 1061178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1062178354Ssam ni->ni_macaddr, "shared key auth", 1063178354Ssam "%s", "no challenge"); 1064178354Ssam vap->iv_stats.is_rx_bad_auth++; 1065178354Ssam estatus = IEEE80211_STATUS_CHALLENGE; 1066178354Ssam goto bad; 1067178354Ssam } 1068178354Ssam if (challenge[1] != IEEE80211_CHALLENGE_LEN) { 1069178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1070178354Ssam ni->ni_macaddr, "shared key auth", 1071178354Ssam "bad challenge len %d", challenge[1]); 1072178354Ssam vap->iv_stats.is_rx_bad_auth++; 1073178354Ssam estatus = IEEE80211_STATUS_CHALLENGE; 1074178354Ssam goto bad; 1075178354Ssam } 1076178354Ssam default: 1077178354Ssam break; 1078178354Ssam } 1079178354Ssam switch (seq) { 1080178354Ssam case IEEE80211_AUTH_SHARED_REQUEST: 1081178354Ssam if (ni == vap->iv_bss) { 1082178354Ssam ni = ieee80211_dup_bss(vap, wh->i_addr2); 1083178354Ssam if (ni == NULL) { 1084178354Ssam /* NB: no way to return an error */ 1085178354Ssam return; 1086178354Ssam } 1087178354Ssam allocbs = 1; 1088178354Ssam } else { 1089178354Ssam if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) 1090178354Ssam (void) ieee80211_ref_node(ni); 1091178354Ssam allocbs = 0; 1092178354Ssam } 1093178354Ssam /* 1094178354Ssam * Mark the node as referenced to reflect that it's 1095178354Ssam * reference count has been bumped to insure it remains 1096178354Ssam * after the transaction completes. 1097178354Ssam */ 1098178354Ssam ni->ni_flags |= IEEE80211_NODE_AREF; 1099186099Ssam /* 1100186099Ssam * Mark the node as requiring a valid associatio id 1101186099Ssam * before outbound traffic is permitted. 1102186099Ssam */ 1103186099Ssam ni->ni_flags |= IEEE80211_NODE_ASSOCID; 1104178354Ssam IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 1105192468Ssam ni->ni_noise = nf; 1106178354Ssam if (!ieee80211_alloc_challenge(ni)) { 1107178354Ssam /* NB: don't return error so they rexmit */ 1108178354Ssam return; 1109178354Ssam } 1110178354Ssam get_random_bytes(ni->ni_challenge, 1111178354Ssam IEEE80211_CHALLENGE_LEN); 1112178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 1113178354Ssam ni, "shared key %sauth request", allocbs ? "" : "re"); 1114178354Ssam /* 1115178354Ssam * When the ACL policy is set to RADIUS we defer the 1116178354Ssam * authorization to a user agent. Dispatch an event, 1117178354Ssam * a subsequent MLME call will decide the fate of the 1118178354Ssam * station. If the user agent is not present then the 1119178354Ssam * node will be reclaimed due to inactivity. 1120178354Ssam */ 1121178354Ssam if (vap->iv_acl != NULL && 1122178354Ssam vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) { 1123178354Ssam IEEE80211_NOTE_MAC(vap, 1124178354Ssam IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, 1125178354Ssam ni->ni_macaddr, 1126178354Ssam "%s", "station authentication defered (radius acl)"); 1127178354Ssam ieee80211_notify_node_auth(ni); 1128178354Ssam return; 1129178354Ssam } 1130178354Ssam break; 1131178354Ssam case IEEE80211_AUTH_SHARED_RESPONSE: 1132178354Ssam if (ni == vap->iv_bss) { 1133178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1134178354Ssam ni->ni_macaddr, "shared key response", 1135178354Ssam "%s", "unknown station"); 1136178354Ssam /* NB: don't send a response */ 1137178354Ssam return; 1138178354Ssam } 1139178354Ssam if (ni->ni_challenge == NULL) { 1140178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1141178354Ssam ni->ni_macaddr, "shared key response", 1142178354Ssam "%s", "no challenge recorded"); 1143178354Ssam vap->iv_stats.is_rx_bad_auth++; 1144178354Ssam estatus = IEEE80211_STATUS_CHALLENGE; 1145178354Ssam goto bad; 1146178354Ssam } 1147178354Ssam if (memcmp(ni->ni_challenge, &challenge[2], 1148178354Ssam challenge[1]) != 0) { 1149178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1150178354Ssam ni->ni_macaddr, "shared key response", 1151178354Ssam "%s", "challenge mismatch"); 1152178354Ssam vap->iv_stats.is_rx_auth_fail++; 1153178354Ssam estatus = IEEE80211_STATUS_CHALLENGE; 1154178354Ssam goto bad; 1155178354Ssam } 1156178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, 1157178354Ssam ni, "%s", "station authenticated (shared key)"); 1158178354Ssam ieee80211_node_authorize(ni); 1159178354Ssam break; 1160178354Ssam default: 1161178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, 1162178354Ssam ni->ni_macaddr, "shared key auth", 1163178354Ssam "bad seq %d", seq); 1164178354Ssam vap->iv_stats.is_rx_bad_auth++; 1165178354Ssam estatus = IEEE80211_STATUS_SEQUENCE; 1166178354Ssam goto bad; 1167178354Ssam } 1168178354Ssam IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); 1169178354Ssam return; 1170178354Ssambad: 1171178354Ssam /* 1172178354Ssam * Send an error response; but only when operating as an AP. 1173178354Ssam */ 1174178354Ssam /* XXX hack to workaround calling convention */ 1175178354Ssam ieee80211_send_error(ni, wh->i_addr2, 1176178354Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1177178354Ssam (seq + 1) | (estatus<<16)); 1178178354Ssam} 1179178354Ssam 1180178354Ssam/* 1181178354Ssam * Convert a WPA cipher selector OUI to an internal 1182178354Ssam * cipher algorithm. Where appropriate we also 1183178354Ssam * record any key length. 1184178354Ssam */ 1185178354Ssamstatic int 1186178354Ssamwpa_cipher(const uint8_t *sel, uint8_t *keylen) 1187178354Ssam{ 1188178354Ssam#define WPA_SEL(x) (((x)<<24)|WPA_OUI) 1189178354Ssam uint32_t w = LE_READ_4(sel); 1190178354Ssam 1191178354Ssam switch (w) { 1192178354Ssam case WPA_SEL(WPA_CSE_NULL): 1193178354Ssam return IEEE80211_CIPHER_NONE; 1194178354Ssam case WPA_SEL(WPA_CSE_WEP40): 1195178354Ssam if (keylen) 1196178354Ssam *keylen = 40 / NBBY; 1197178354Ssam return IEEE80211_CIPHER_WEP; 1198178354Ssam case WPA_SEL(WPA_CSE_WEP104): 1199178354Ssam if (keylen) 1200178354Ssam *keylen = 104 / NBBY; 1201178354Ssam return IEEE80211_CIPHER_WEP; 1202178354Ssam case WPA_SEL(WPA_CSE_TKIP): 1203178354Ssam return IEEE80211_CIPHER_TKIP; 1204178354Ssam case WPA_SEL(WPA_CSE_CCMP): 1205178354Ssam return IEEE80211_CIPHER_AES_CCM; 1206178354Ssam } 1207178354Ssam return 32; /* NB: so 1<< is discarded */ 1208178354Ssam#undef WPA_SEL 1209178354Ssam} 1210178354Ssam 1211178354Ssam/* 1212178354Ssam * Convert a WPA key management/authentication algorithm 1213178354Ssam * to an internal code. 1214178354Ssam */ 1215178354Ssamstatic int 1216178354Ssamwpa_keymgmt(const uint8_t *sel) 1217178354Ssam{ 1218178354Ssam#define WPA_SEL(x) (((x)<<24)|WPA_OUI) 1219178354Ssam uint32_t w = LE_READ_4(sel); 1220178354Ssam 1221178354Ssam switch (w) { 1222178354Ssam case WPA_SEL(WPA_ASE_8021X_UNSPEC): 1223178354Ssam return WPA_ASE_8021X_UNSPEC; 1224178354Ssam case WPA_SEL(WPA_ASE_8021X_PSK): 1225178354Ssam return WPA_ASE_8021X_PSK; 1226178354Ssam case WPA_SEL(WPA_ASE_NONE): 1227178354Ssam return WPA_ASE_NONE; 1228178354Ssam } 1229178354Ssam return 0; /* NB: so is discarded */ 1230178354Ssam#undef WPA_SEL 1231178354Ssam} 1232178354Ssam 1233178354Ssam/* 1234178354Ssam * Parse a WPA information element to collect parameters. 1235178354Ssam * Note that we do not validate security parameters; that 1236178354Ssam * is handled by the authenticator; the parsing done here 1237178354Ssam * is just for internal use in making operational decisions. 1238178354Ssam */ 1239178354Ssamstatic int 1240178354Ssamieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm, 1241178354Ssam struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) 1242178354Ssam{ 1243178354Ssam uint8_t len = frm[1]; 1244178354Ssam uint32_t w; 1245178354Ssam int n; 1246178354Ssam 1247178354Ssam /* 1248178354Ssam * Check the length once for fixed parts: OUI, type, 1249178354Ssam * version, mcast cipher, and 2 selector counts. 1250178354Ssam * Other, variable-length data, must be checked separately. 1251178354Ssam */ 1252178354Ssam if ((vap->iv_flags & IEEE80211_F_WPA1) == 0) { 1253178354Ssam IEEE80211_DISCARD_IE(vap, 1254178354Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1255178354Ssam wh, "WPA", "not WPA, flags 0x%x", vap->iv_flags); 1256178354Ssam return IEEE80211_REASON_IE_INVALID; 1257178354Ssam } 1258178354Ssam if (len < 14) { 1259178354Ssam IEEE80211_DISCARD_IE(vap, 1260178354Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1261178354Ssam wh, "WPA", "too short, len %u", len); 1262178354Ssam return IEEE80211_REASON_IE_INVALID; 1263178354Ssam } 1264178354Ssam frm += 6, len -= 4; /* NB: len is payload only */ 1265200242Srpaulo /* NB: iswpaoui already validated the OUI and type */ 1266178354Ssam w = LE_READ_2(frm); 1267178354Ssam if (w != WPA_VERSION) { 1268178354Ssam IEEE80211_DISCARD_IE(vap, 1269178354Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1270178354Ssam wh, "WPA", "bad version %u", w); 1271178354Ssam return IEEE80211_REASON_IE_INVALID; 1272178354Ssam } 1273178354Ssam frm += 2, len -= 2; 1274178354Ssam 1275178354Ssam memset(rsn, 0, sizeof(*rsn)); 1276178354Ssam 1277178354Ssam /* multicast/group cipher */ 1278178354Ssam rsn->rsn_mcastcipher = wpa_cipher(frm, &rsn->rsn_mcastkeylen); 1279178354Ssam frm += 4, len -= 4; 1280178354Ssam 1281178354Ssam /* unicast ciphers */ 1282178354Ssam n = LE_READ_2(frm); 1283178354Ssam frm += 2, len -= 2; 1284178354Ssam if (len < n*4+2) { 1285178354Ssam IEEE80211_DISCARD_IE(vap, 1286178354Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1287178354Ssam wh, "WPA", "ucast cipher data too short; len %u, n %u", 1288178354Ssam len, n); 1289178354Ssam return IEEE80211_REASON_IE_INVALID; 1290178354Ssam } 1291178354Ssam w = 0; 1292178354Ssam for (; n > 0; n--) { 1293178354Ssam w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen); 1294178354Ssam frm += 4, len -= 4; 1295178354Ssam } 1296178354Ssam if (w & (1<<IEEE80211_CIPHER_TKIP)) 1297178354Ssam rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; 1298178354Ssam else 1299178354Ssam rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; 1300178354Ssam 1301178354Ssam /* key management algorithms */ 1302178354Ssam n = LE_READ_2(frm); 1303178354Ssam frm += 2, len -= 2; 1304178354Ssam if (len < n*4) { 1305178354Ssam IEEE80211_DISCARD_IE(vap, 1306178354Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1307178354Ssam wh, "WPA", "key mgmt alg data too short; len %u, n %u", 1308178354Ssam len, n); 1309178354Ssam return IEEE80211_REASON_IE_INVALID; 1310178354Ssam } 1311178354Ssam w = 0; 1312178354Ssam for (; n > 0; n--) { 1313178354Ssam w |= wpa_keymgmt(frm); 1314178354Ssam frm += 4, len -= 4; 1315178354Ssam } 1316178354Ssam if (w & WPA_ASE_8021X_UNSPEC) 1317178354Ssam rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC; 1318178354Ssam else 1319178354Ssam rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; 1320178354Ssam 1321178354Ssam if (len > 2) /* optional capabilities */ 1322178354Ssam rsn->rsn_caps = LE_READ_2(frm); 1323178354Ssam 1324178354Ssam return 0; 1325178354Ssam} 1326178354Ssam 1327178354Ssam/* 1328178354Ssam * Convert an RSN cipher selector OUI to an internal 1329178354Ssam * cipher algorithm. Where appropriate we also 1330178354Ssam * record any key length. 1331178354Ssam */ 1332178354Ssamstatic int 1333178354Ssamrsn_cipher(const uint8_t *sel, uint8_t *keylen) 1334178354Ssam{ 1335178354Ssam#define RSN_SEL(x) (((x)<<24)|RSN_OUI) 1336178354Ssam uint32_t w = LE_READ_4(sel); 1337178354Ssam 1338178354Ssam switch (w) { 1339178354Ssam case RSN_SEL(RSN_CSE_NULL): 1340178354Ssam return IEEE80211_CIPHER_NONE; 1341178354Ssam case RSN_SEL(RSN_CSE_WEP40): 1342178354Ssam if (keylen) 1343178354Ssam *keylen = 40 / NBBY; 1344178354Ssam return IEEE80211_CIPHER_WEP; 1345178354Ssam case RSN_SEL(RSN_CSE_WEP104): 1346178354Ssam if (keylen) 1347178354Ssam *keylen = 104 / NBBY; 1348178354Ssam return IEEE80211_CIPHER_WEP; 1349178354Ssam case RSN_SEL(RSN_CSE_TKIP): 1350178354Ssam return IEEE80211_CIPHER_TKIP; 1351178354Ssam case RSN_SEL(RSN_CSE_CCMP): 1352178354Ssam return IEEE80211_CIPHER_AES_CCM; 1353178354Ssam case RSN_SEL(RSN_CSE_WRAP): 1354178354Ssam return IEEE80211_CIPHER_AES_OCB; 1355178354Ssam } 1356178354Ssam return 32; /* NB: so 1<< is discarded */ 1357178354Ssam#undef WPA_SEL 1358178354Ssam} 1359178354Ssam 1360178354Ssam/* 1361178354Ssam * Convert an RSN key management/authentication algorithm 1362178354Ssam * to an internal code. 1363178354Ssam */ 1364178354Ssamstatic int 1365178354Ssamrsn_keymgmt(const uint8_t *sel) 1366178354Ssam{ 1367178354Ssam#define RSN_SEL(x) (((x)<<24)|RSN_OUI) 1368178354Ssam uint32_t w = LE_READ_4(sel); 1369178354Ssam 1370178354Ssam switch (w) { 1371178354Ssam case RSN_SEL(RSN_ASE_8021X_UNSPEC): 1372178354Ssam return RSN_ASE_8021X_UNSPEC; 1373178354Ssam case RSN_SEL(RSN_ASE_8021X_PSK): 1374178354Ssam return RSN_ASE_8021X_PSK; 1375178354Ssam case RSN_SEL(RSN_ASE_NONE): 1376178354Ssam return RSN_ASE_NONE; 1377178354Ssam } 1378178354Ssam return 0; /* NB: so is discarded */ 1379178354Ssam#undef RSN_SEL 1380178354Ssam} 1381178354Ssam 1382178354Ssam/* 1383178354Ssam * Parse a WPA/RSN information element to collect parameters 1384178354Ssam * and validate the parameters against what has been 1385178354Ssam * configured for the system. 1386178354Ssam */ 1387178354Ssamstatic int 1388178354Ssamieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm, 1389178354Ssam struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) 1390178354Ssam{ 1391178354Ssam uint8_t len = frm[1]; 1392178354Ssam uint32_t w; 1393178354Ssam int n; 1394178354Ssam 1395178354Ssam /* 1396178354Ssam * Check the length once for fixed parts: 1397178354Ssam * version, mcast cipher, and 2 selector counts. 1398178354Ssam * Other, variable-length data, must be checked separately. 1399178354Ssam */ 1400178354Ssam if ((vap->iv_flags & IEEE80211_F_WPA2) == 0) { 1401178354Ssam IEEE80211_DISCARD_IE(vap, 1402178354Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1403178354Ssam wh, "WPA", "not RSN, flags 0x%x", vap->iv_flags); 1404178354Ssam return IEEE80211_REASON_IE_INVALID; 1405178354Ssam } 1406178354Ssam if (len < 10) { 1407178354Ssam IEEE80211_DISCARD_IE(vap, 1408178354Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1409178354Ssam wh, "RSN", "too short, len %u", len); 1410178354Ssam return IEEE80211_REASON_IE_INVALID; 1411178354Ssam } 1412178354Ssam frm += 2; 1413178354Ssam w = LE_READ_2(frm); 1414178354Ssam if (w != RSN_VERSION) { 1415178354Ssam IEEE80211_DISCARD_IE(vap, 1416178354Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1417178354Ssam wh, "RSN", "bad version %u", w); 1418178354Ssam return IEEE80211_REASON_IE_INVALID; 1419178354Ssam } 1420178354Ssam frm += 2, len -= 2; 1421178354Ssam 1422178354Ssam memset(rsn, 0, sizeof(*rsn)); 1423178354Ssam 1424178354Ssam /* multicast/group cipher */ 1425178354Ssam rsn->rsn_mcastcipher = rsn_cipher(frm, &rsn->rsn_mcastkeylen); 1426178354Ssam frm += 4, len -= 4; 1427178354Ssam 1428178354Ssam /* unicast ciphers */ 1429178354Ssam n = LE_READ_2(frm); 1430178354Ssam frm += 2, len -= 2; 1431178354Ssam if (len < n*4+2) { 1432178354Ssam IEEE80211_DISCARD_IE(vap, 1433178354Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1434178354Ssam wh, "RSN", "ucast cipher data too short; len %u, n %u", 1435178354Ssam len, n); 1436178354Ssam return IEEE80211_REASON_IE_INVALID; 1437178354Ssam } 1438178354Ssam w = 0; 1439178354Ssam for (; n > 0; n--) { 1440178354Ssam w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen); 1441178354Ssam frm += 4, len -= 4; 1442178354Ssam } 1443178354Ssam if (w & (1<<IEEE80211_CIPHER_TKIP)) 1444178354Ssam rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; 1445178354Ssam else 1446178354Ssam rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; 1447178354Ssam 1448178354Ssam /* key management algorithms */ 1449178354Ssam n = LE_READ_2(frm); 1450178354Ssam frm += 2, len -= 2; 1451178354Ssam if (len < n*4) { 1452178354Ssam IEEE80211_DISCARD_IE(vap, 1453178354Ssam IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, 1454178354Ssam wh, "RSN", "key mgmt alg data too short; len %u, n %u", 1455178354Ssam len, n); 1456178354Ssam return IEEE80211_REASON_IE_INVALID; 1457178354Ssam } 1458178354Ssam w = 0; 1459178354Ssam for (; n > 0; n--) { 1460178354Ssam w |= rsn_keymgmt(frm); 1461178354Ssam frm += 4, len -= 4; 1462178354Ssam } 1463178354Ssam if (w & RSN_ASE_8021X_UNSPEC) 1464178354Ssam rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC; 1465178354Ssam else 1466178354Ssam rsn->rsn_keymgmt = RSN_ASE_8021X_PSK; 1467178354Ssam 1468178354Ssam /* optional RSN capabilities */ 1469178354Ssam if (len > 2) 1470178354Ssam rsn->rsn_caps = LE_READ_2(frm); 1471178354Ssam /* XXXPMKID */ 1472178354Ssam 1473178354Ssam return 0; 1474178354Ssam} 1475178354Ssam 1476178354Ssam/* 1477178354Ssam * WPA/802.11i assocation request processing. 1478178354Ssam */ 1479178354Ssamstatic int 1480178354Ssamwpa_assocreq(struct ieee80211_node *ni, struct ieee80211_rsnparms *rsnparms, 1481178354Ssam const struct ieee80211_frame *wh, const uint8_t *wpa, 1482178354Ssam const uint8_t *rsn, uint16_t capinfo) 1483178354Ssam{ 1484178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1485178354Ssam uint8_t reason; 1486178354Ssam int badwparsn; 1487178354Ssam 1488178354Ssam ni->ni_flags &= ~(IEEE80211_NODE_WPS|IEEE80211_NODE_TSN); 1489178354Ssam if (wpa == NULL && rsn == NULL) { 1490178354Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_WPS) { 1491178354Ssam /* 1492178354Ssam * W-Fi Protected Setup (WPS) permits 1493178354Ssam * clients to associate and pass EAPOL frames 1494178354Ssam * to establish initial credentials. 1495178354Ssam */ 1496178354Ssam ni->ni_flags |= IEEE80211_NODE_WPS; 1497178354Ssam return 1; 1498178354Ssam } 1499178354Ssam if ((vap->iv_flags_ext & IEEE80211_FEXT_TSN) && 1500178354Ssam (capinfo & IEEE80211_CAPINFO_PRIVACY)) { 1501178354Ssam /* 1502178354Ssam * Transitional Security Network. Permits clients 1503178354Ssam * to associate and use WEP while WPA is configured. 1504178354Ssam */ 1505178354Ssam ni->ni_flags |= IEEE80211_NODE_TSN; 1506178354Ssam return 1; 1507178354Ssam } 1508178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, 1509178354Ssam wh, NULL, "%s", "no WPA/RSN IE in association request"); 1510178354Ssam vap->iv_stats.is_rx_assoc_badwpaie++; 1511178354Ssam reason = IEEE80211_REASON_IE_INVALID; 1512178354Ssam goto bad; 1513178354Ssam } 1514178354Ssam /* assert right association security credentials */ 1515178354Ssam badwparsn = 0; /* NB: to silence compiler */ 1516178354Ssam switch (vap->iv_flags & IEEE80211_F_WPA) { 1517178354Ssam case IEEE80211_F_WPA1: 1518178354Ssam badwparsn = (wpa == NULL); 1519178354Ssam break; 1520178354Ssam case IEEE80211_F_WPA2: 1521178354Ssam badwparsn = (rsn == NULL); 1522178354Ssam break; 1523178354Ssam case IEEE80211_F_WPA1|IEEE80211_F_WPA2: 1524178354Ssam badwparsn = (wpa == NULL && rsn == NULL); 1525178354Ssam break; 1526178354Ssam } 1527178354Ssam if (badwparsn) { 1528178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, 1529178354Ssam wh, NULL, 1530178354Ssam "%s", "missing WPA/RSN IE in association request"); 1531178354Ssam vap->iv_stats.is_rx_assoc_badwpaie++; 1532178354Ssam reason = IEEE80211_REASON_IE_INVALID; 1533178354Ssam goto bad; 1534178354Ssam } 1535178354Ssam /* 1536178354Ssam * Parse WPA/RSN information element. 1537178354Ssam */ 1538178354Ssam if (wpa != NULL) 1539178354Ssam reason = ieee80211_parse_wpa(vap, wpa, rsnparms, wh); 1540178354Ssam else 1541178354Ssam reason = ieee80211_parse_rsn(vap, rsn, rsnparms, wh); 1542178354Ssam if (reason != 0) { 1543178354Ssam /* XXX distinguish WPA/RSN? */ 1544178354Ssam vap->iv_stats.is_rx_assoc_badwpaie++; 1545178354Ssam goto bad; 1546178354Ssam } 1547178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, ni, 1548178354Ssam "%s ie: mc %u/%u uc %u/%u key %u caps 0x%x", 1549178354Ssam wpa != NULL ? "WPA" : "RSN", 1550178354Ssam rsnparms->rsn_mcastcipher, rsnparms->rsn_mcastkeylen, 1551178354Ssam rsnparms->rsn_ucastcipher, rsnparms->rsn_ucastkeylen, 1552178354Ssam rsnparms->rsn_keymgmt, rsnparms->rsn_caps); 1553178354Ssam 1554178354Ssam return 1; 1555178354Ssambad: 1556178354Ssam ieee80211_node_deauth(ni, reason); 1557178354Ssam return 0; 1558178354Ssam} 1559178354Ssam 1560178354Ssam/* XXX find a better place for definition */ 1561178354Ssamstruct l2_update_frame { 1562178354Ssam struct ether_header eh; 1563178354Ssam uint8_t dsap; 1564178354Ssam uint8_t ssap; 1565178354Ssam uint8_t control; 1566178354Ssam uint8_t xid[3]; 1567178354Ssam} __packed; 1568178354Ssam 1569178354Ssam/* 1570178354Ssam * Deliver a TGf L2UF frame on behalf of a station. 1571178354Ssam * This primes any bridge when the station is roaming 1572178354Ssam * between ap's on the same wired network. 1573178354Ssam */ 1574178354Ssamstatic void 1575178354Ssamieee80211_deliver_l2uf(struct ieee80211_node *ni) 1576178354Ssam{ 1577178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1578178354Ssam struct ifnet *ifp = vap->iv_ifp; 1579178354Ssam struct mbuf *m; 1580178354Ssam struct l2_update_frame *l2uf; 1581178354Ssam struct ether_header *eh; 1582178354Ssam 1583178354Ssam m = m_gethdr(M_NOWAIT, MT_DATA); 1584178354Ssam if (m == NULL) { 1585178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, 1586178354Ssam "%s", "no mbuf for l2uf frame"); 1587178354Ssam vap->iv_stats.is_rx_nobuf++; /* XXX not right */ 1588178354Ssam return; 1589178354Ssam } 1590178354Ssam l2uf = mtod(m, struct l2_update_frame *); 1591178354Ssam eh = &l2uf->eh; 1592178354Ssam /* dst: Broadcast address */ 1593178354Ssam IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr); 1594178354Ssam /* src: associated STA */ 1595178354Ssam IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr); 1596178354Ssam eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh)); 1597178354Ssam 1598178354Ssam l2uf->dsap = 0; 1599178354Ssam l2uf->ssap = 0; 1600178354Ssam l2uf->control = 0xf5; 1601178354Ssam l2uf->xid[0] = 0x81; 1602178354Ssam l2uf->xid[1] = 0x80; 1603178354Ssam l2uf->xid[2] = 0x00; 1604178354Ssam 1605178354Ssam m->m_pkthdr.len = m->m_len = sizeof(*l2uf); 1606178354Ssam hostap_deliver_data(vap, ni, m); 1607178354Ssam} 1608178354Ssam 1609178354Ssamstatic void 1610178354Ssamratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, 1611178354Ssam int reassoc, int resp, const char *tag, int rate) 1612178354Ssam{ 1613178354Ssam IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2, 1614178354Ssam "deny %s request, %s rate set mismatch, rate/MCS %d", 1615178354Ssam reassoc ? "reassoc" : "assoc", tag, rate & IEEE80211_RATE_VAL); 1616178354Ssam IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_BASIC_RATE); 1617178354Ssam ieee80211_node_leave(ni); 1618178354Ssam} 1619178354Ssam 1620178354Ssamstatic void 1621178354Ssamcapinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, 1622178354Ssam int reassoc, int resp, const char *tag, int capinfo) 1623178354Ssam{ 1624178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1625178354Ssam 1626178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2, 1627178354Ssam "deny %s request, %s mismatch 0x%x", 1628178354Ssam reassoc ? "reassoc" : "assoc", tag, capinfo); 1629178354Ssam IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_CAPINFO); 1630178354Ssam ieee80211_node_leave(ni); 1631178354Ssam vap->iv_stats.is_rx_assoc_capmismatch++; 1632178354Ssam} 1633178354Ssam 1634178354Ssamstatic void 1635178354Ssamhtcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, 1636178354Ssam int reassoc, int resp) 1637178354Ssam{ 1638178354Ssam IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2, 1639178354Ssam "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc"); 1640178354Ssam /* XXX no better code */ 1641193543Ssam IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_MISSING_HT_CAPS); 1642178354Ssam ieee80211_node_leave(ni); 1643178354Ssam} 1644178354Ssam 1645178354Ssamstatic void 1646178354Ssamauthalgreject(struct ieee80211_node *ni, const struct ieee80211_frame *wh, 1647178354Ssam int algo, int seq, int status) 1648178354Ssam{ 1649178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1650178354Ssam 1651178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 1652178354Ssam wh, NULL, "unsupported alg %d", algo); 1653178354Ssam vap->iv_stats.is_rx_auth_unsupported++; 1654178354Ssam ieee80211_send_error(ni, wh->i_addr2, IEEE80211_FC0_SUBTYPE_AUTH, 1655178354Ssam seq | (status << 16)); 1656178354Ssam} 1657178354Ssam 1658178354Ssamstatic __inline int 1659178354Ssamishtmixed(const uint8_t *ie) 1660178354Ssam{ 1661178354Ssam const struct ieee80211_ie_htinfo *ht = 1662178354Ssam (const struct ieee80211_ie_htinfo *) ie; 1663178354Ssam return (ht->hi_byte2 & IEEE80211_HTINFO_OPMODE) == 1664178354Ssam IEEE80211_HTINFO_OPMODE_MIXED; 1665178354Ssam} 1666178354Ssam 1667178354Ssamstatic int 1668178354Ssamis11bclient(const uint8_t *rates, const uint8_t *xrates) 1669178354Ssam{ 1670178354Ssam static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11); 1671178354Ssam int i; 1672178354Ssam 1673178354Ssam /* NB: the 11b clients we care about will not have xrates */ 1674178354Ssam if (xrates != NULL || rates == NULL) 1675178354Ssam return 0; 1676178354Ssam for (i = 0; i < rates[1]; i++) { 1677178354Ssam int r = rates[2+i] & IEEE80211_RATE_VAL; 1678178354Ssam if (r > 2*11 || ((1<<r) & brates) == 0) 1679178354Ssam return 0; 1680178354Ssam } 1681178354Ssam return 1; 1682178354Ssam} 1683178354Ssam 1684178354Ssamstatic void 1685178354Ssamhostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 1686283535Sadrian int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) 1687178354Ssam{ 1688178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1689178354Ssam struct ieee80211com *ic = ni->ni_ic; 1690178354Ssam struct ieee80211_frame *wh; 1691178354Ssam uint8_t *frm, *efrm, *sfrm; 1692178354Ssam uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap; 1693178354Ssam int reassoc, resp; 1694178354Ssam uint8_t rate; 1695178354Ssam 1696178354Ssam wh = mtod(m0, struct ieee80211_frame *); 1697178354Ssam frm = (uint8_t *)&wh[1]; 1698178354Ssam efrm = mtod(m0, uint8_t *) + m0->m_len; 1699178354Ssam switch (subtype) { 1700178354Ssam case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 1701178354Ssam case IEEE80211_FC0_SUBTYPE_BEACON: { 1702178354Ssam struct ieee80211_scanparams scan; 1703178354Ssam /* 1704178354Ssam * We process beacon/probe response frames when scanning; 1705178354Ssam * otherwise we check beacon frames for overlapping non-ERP 1706178354Ssam * BSS in 11g and/or overlapping legacy BSS when in HT. 1707178354Ssam */ 1708178354Ssam if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && 1709178354Ssam subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) { 1710178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 1711178354Ssam return; 1712178354Ssam } 1713178354Ssam /* NB: accept off-channel frames */ 1714283535Sadrian /* XXX TODO: use rxstatus to determine off-channel details */ 1715283535Sadrian if (ieee80211_parse_beacon(ni, m0, ic->ic_curchan, &scan) &~ IEEE80211_BPARSE_OFFCHAN) 1716178354Ssam return; 1717178354Ssam /* 1718178354Ssam * Count frame now that we know it's to be processed. 1719178354Ssam */ 1720178354Ssam if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { 1721178354Ssam vap->iv_stats.is_rx_beacon++; /* XXX remove */ 1722178354Ssam IEEE80211_NODE_STAT(ni, rx_beacons); 1723178354Ssam } else 1724178354Ssam IEEE80211_NODE_STAT(ni, rx_proberesp); 1725178354Ssam /* 1726178354Ssam * If scanning, just pass information to the scan module. 1727178354Ssam */ 1728178354Ssam if (ic->ic_flags & IEEE80211_F_SCAN) { 1729178354Ssam if (scan.status == 0 && /* NB: on channel */ 1730178354Ssam (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN)) { 1731178354Ssam /* 1732178354Ssam * Actively scanning a channel marked passive; 1733178354Ssam * send a probe request now that we know there 1734178354Ssam * is 802.11 traffic present. 1735178354Ssam * 1736178354Ssam * XXX check if the beacon we recv'd gives 1737178354Ssam * us what we need and suppress the probe req 1738178354Ssam */ 1739178354Ssam ieee80211_probe_curchan(vap, 1); 1740178354Ssam ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; 1741178354Ssam } 1742282742Sadrian ieee80211_add_scan(vap, ic->ic_curchan, &scan, wh, 1743282742Sadrian subtype, rssi, nf); 1744178354Ssam return; 1745178354Ssam } 1746178354Ssam /* 1747178354Ssam * Check beacon for overlapping bss w/ non ERP stations. 1748178354Ssam * If we detect one and protection is configured but not 1749178354Ssam * enabled, enable it and start a timer that'll bring us 1750178354Ssam * out if we stop seeing the bss. 1751178354Ssam */ 1752178354Ssam if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 1753178354Ssam scan.status == 0 && /* NB: on-channel */ 1754178354Ssam ((scan.erp & 0x100) == 0 || /* NB: no ERP, 11b sta*/ 1755178354Ssam (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) { 1756178354Ssam ic->ic_lastnonerp = ticks; 1757178354Ssam ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR; 1758178354Ssam if (ic->ic_protmode != IEEE80211_PROT_NONE && 1759178354Ssam (ic->ic_flags & IEEE80211_F_USEPROT) == 0) { 1760178354Ssam IEEE80211_NOTE_FRAME(vap, 1761178354Ssam IEEE80211_MSG_ASSOC, wh, 1762178354Ssam "non-ERP present on channel %d " 1763178354Ssam "(saw erp 0x%x from channel %d), " 1764178354Ssam "enable use of protection", 1765178354Ssam ic->ic_curchan->ic_ieee, 1766178354Ssam scan.erp, scan.chan); 1767178354Ssam ic->ic_flags |= IEEE80211_F_USEPROT; 1768178354Ssam ieee80211_notify_erp(ic); 1769178354Ssam } 1770178354Ssam } 1771178354Ssam /* 1772178354Ssam * Check beacon for non-HT station on HT channel 1773178354Ssam * and update HT BSS occupancy as appropriate. 1774178354Ssam */ 1775178354Ssam if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) { 1776178354Ssam if (scan.status & IEEE80211_BPARSE_OFFCHAN) { 1777178354Ssam /* 1778178354Ssam * Off control channel; only check frames 1779178354Ssam * that come in the extension channel when 1780178354Ssam * operating w/ HT40. 1781178354Ssam */ 1782178354Ssam if (!IEEE80211_IS_CHAN_HT40(ic->ic_curchan)) 1783178354Ssam break; 1784178354Ssam if (scan.chan != ic->ic_curchan->ic_extieee) 1785178354Ssam break; 1786178354Ssam } 1787178354Ssam if (scan.htinfo == NULL) { 1788178354Ssam ieee80211_htprot_update(ic, 1789178354Ssam IEEE80211_HTINFO_OPMODE_PROTOPT | 1790178354Ssam IEEE80211_HTINFO_NONHT_PRESENT); 1791178354Ssam } else if (ishtmixed(scan.htinfo)) { 1792178354Ssam /* XXX? take NONHT_PRESENT from beacon? */ 1793178354Ssam ieee80211_htprot_update(ic, 1794178354Ssam IEEE80211_HTINFO_OPMODE_MIXED | 1795178354Ssam IEEE80211_HTINFO_NONHT_PRESENT); 1796178354Ssam } 1797178354Ssam } 1798178354Ssam break; 1799178354Ssam } 1800178354Ssam 1801178354Ssam case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 1802178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 1803178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 1804178354Ssam return; 1805178354Ssam } 1806178354Ssam /* 1807228514Sadrian * Consult the ACL policy module if setup. 1808228514Sadrian */ 1809228622Sbschmidt if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) { 1810228514Sadrian IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL, 1811228514Sadrian wh, NULL, "%s", "disallowed by ACL"); 1812228514Sadrian vap->iv_stats.is_rx_acl++; 1813228514Sadrian return; 1814228514Sadrian } 1815228514Sadrian /* 1816178354Ssam * prreq frame format 1817178354Ssam * [tlv] ssid 1818178354Ssam * [tlv] supported rates 1819178354Ssam * [tlv] extended supported rates 1820178354Ssam */ 1821178354Ssam ssid = rates = xrates = NULL; 1822178354Ssam sfrm = frm; 1823178354Ssam while (efrm - frm > 1) { 1824178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 1825178354Ssam switch (*frm) { 1826178354Ssam case IEEE80211_ELEMID_SSID: 1827178354Ssam ssid = frm; 1828178354Ssam break; 1829178354Ssam case IEEE80211_ELEMID_RATES: 1830178354Ssam rates = frm; 1831178354Ssam break; 1832178354Ssam case IEEE80211_ELEMID_XRATES: 1833178354Ssam xrates = frm; 1834178354Ssam break; 1835178354Ssam } 1836178354Ssam frm += frm[1] + 2; 1837178354Ssam } 1838178354Ssam IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); 1839178354Ssam if (xrates != NULL) 1840178354Ssam IEEE80211_VERIFY_ELEMENT(xrates, 1841178354Ssam IEEE80211_RATE_MAXSIZE - rates[1], return); 1842178354Ssam IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); 1843178354Ssam IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); 1844178354Ssam if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { 1845178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 1846178354Ssam wh, NULL, 1847178354Ssam "%s", "no ssid with ssid suppression enabled"); 1848178354Ssam vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/ 1849178354Ssam return; 1850178354Ssam } 1851178354Ssam 1852178354Ssam /* XXX find a better class or define it's own */ 1853178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, 1854178354Ssam "%s", "recv probe req"); 1855178354Ssam /* 1856178354Ssam * Some legacy 11b clients cannot hack a complete 1857178354Ssam * probe response frame. When the request includes 1858178354Ssam * only a bare-bones rate set, communicate this to 1859178354Ssam * the transmit side. 1860178354Ssam */ 1861178354Ssam ieee80211_send_proberesp(vap, wh->i_addr2, 1862178354Ssam is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0); 1863178354Ssam break; 1864178354Ssam 1865178354Ssam case IEEE80211_FC0_SUBTYPE_AUTH: { 1866178354Ssam uint16_t algo, seq, status; 1867178354Ssam 1868178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 1869178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 1870178354Ssam return; 1871178354Ssam } 1872178354Ssam if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) { 1873178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 1874178354Ssam wh, NULL, "%s", "wrong bssid"); 1875178354Ssam vap->iv_stats.is_rx_wrongbss++; /*XXX unique stat?*/ 1876178354Ssam return; 1877178354Ssam } 1878178354Ssam /* 1879178354Ssam * auth frame format 1880178354Ssam * [2] algorithm 1881178354Ssam * [2] sequence 1882178354Ssam * [2] status 1883178354Ssam * [tlv*] challenge 1884178354Ssam */ 1885178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); 1886178354Ssam algo = le16toh(*(uint16_t *)frm); 1887178354Ssam seq = le16toh(*(uint16_t *)(frm + 2)); 1888178354Ssam status = le16toh(*(uint16_t *)(frm + 4)); 1889178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2, 1890178354Ssam "recv auth frame with algorithm %d seq %d", algo, seq); 1891178354Ssam /* 1892178354Ssam * Consult the ACL policy module if setup. 1893178354Ssam */ 1894228622Sbschmidt if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) { 1895178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL, 1896178354Ssam wh, NULL, "%s", "disallowed by ACL"); 1897178354Ssam vap->iv_stats.is_rx_acl++; 1898178354Ssam ieee80211_send_error(ni, wh->i_addr2, 1899178354Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1900178354Ssam (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16)); 1901178354Ssam return; 1902178354Ssam } 1903178354Ssam if (vap->iv_flags & IEEE80211_F_COUNTERM) { 1904178354Ssam IEEE80211_DISCARD(vap, 1905178354Ssam IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, 1906178354Ssam wh, NULL, "%s", "TKIP countermeasures enabled"); 1907178354Ssam vap->iv_stats.is_rx_auth_countermeasures++; 1908178354Ssam ieee80211_send_error(ni, wh->i_addr2, 1909178354Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1910178354Ssam IEEE80211_REASON_MIC_FAILURE); 1911178354Ssam return; 1912178354Ssam } 1913178354Ssam if (algo == IEEE80211_AUTH_ALG_SHARED) 1914192468Ssam hostap_auth_shared(ni, wh, frm + 6, efrm, rssi, nf, 1915192468Ssam seq, status); 1916178354Ssam else if (algo == IEEE80211_AUTH_ALG_OPEN) 1917192468Ssam hostap_auth_open(ni, wh, rssi, nf, seq, status); 1918178354Ssam else if (algo == IEEE80211_AUTH_ALG_LEAP) { 1919178354Ssam authalgreject(ni, wh, algo, 1920178354Ssam seq+1, IEEE80211_STATUS_ALG); 1921178354Ssam return; 1922178354Ssam } else { 1923178354Ssam /* 1924178354Ssam * We assume that an unknown algorithm is the result 1925178354Ssam * of a decryption failure on a shared key auth frame; 1926178354Ssam * return a status code appropriate for that instead 1927178354Ssam * of IEEE80211_STATUS_ALG. 1928178354Ssam * 1929178354Ssam * NB: a seq# of 4 is intentional; the decrypted 1930178354Ssam * frame likely has a bogus seq value. 1931178354Ssam */ 1932178354Ssam authalgreject(ni, wh, algo, 1933178354Ssam 4, IEEE80211_STATUS_CHALLENGE); 1934178354Ssam return; 1935178354Ssam } 1936178354Ssam break; 1937178354Ssam } 1938178354Ssam 1939178354Ssam case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 1940178354Ssam case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { 1941178354Ssam uint16_t capinfo, lintval; 1942178354Ssam struct ieee80211_rsnparms rsnparms; 1943178354Ssam 1944178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 1945178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 1946178354Ssam return; 1947178354Ssam } 1948178354Ssam if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) { 1949178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 1950178354Ssam wh, NULL, "%s", "wrong bssid"); 1951178354Ssam vap->iv_stats.is_rx_assoc_bss++; 1952178354Ssam return; 1953178354Ssam } 1954178354Ssam if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 1955178354Ssam reassoc = 1; 1956178354Ssam resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; 1957178354Ssam } else { 1958178354Ssam reassoc = 0; 1959178354Ssam resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; 1960178354Ssam } 1961178354Ssam if (ni == vap->iv_bss) { 1962178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2, 1963178354Ssam "deny %s request, sta not authenticated", 1964178354Ssam reassoc ? "reassoc" : "assoc"); 1965178354Ssam ieee80211_send_error(ni, wh->i_addr2, 1966178354Ssam IEEE80211_FC0_SUBTYPE_DEAUTH, 1967178354Ssam IEEE80211_REASON_ASSOC_NOT_AUTHED); 1968178354Ssam vap->iv_stats.is_rx_assoc_notauth++; 1969178354Ssam return; 1970178354Ssam } 1971178354Ssam 1972178354Ssam /* 1973178354Ssam * asreq frame format 1974178354Ssam * [2] capability information 1975178354Ssam * [2] listen interval 1976178354Ssam * [6*] current AP address (reassoc only) 1977178354Ssam * [tlv] ssid 1978178354Ssam * [tlv] supported rates 1979178354Ssam * [tlv] extended supported rates 1980178354Ssam * [tlv] WPA or RSN 1981178354Ssam * [tlv] HT capabilities 1982178354Ssam * [tlv] Atheros capabilities 1983178354Ssam */ 1984178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return); 1985178354Ssam capinfo = le16toh(*(uint16_t *)frm); frm += 2; 1986178354Ssam lintval = le16toh(*(uint16_t *)frm); frm += 2; 1987178354Ssam if (reassoc) 1988178354Ssam frm += 6; /* ignore current AP info */ 1989178354Ssam ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL; 1990178354Ssam sfrm = frm; 1991178354Ssam while (efrm - frm > 1) { 1992178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 1993178354Ssam switch (*frm) { 1994178354Ssam case IEEE80211_ELEMID_SSID: 1995178354Ssam ssid = frm; 1996178354Ssam break; 1997178354Ssam case IEEE80211_ELEMID_RATES: 1998178354Ssam rates = frm; 1999178354Ssam break; 2000178354Ssam case IEEE80211_ELEMID_XRATES: 2001178354Ssam xrates = frm; 2002178354Ssam break; 2003178354Ssam case IEEE80211_ELEMID_RSN: 2004178354Ssam rsn = frm; 2005178354Ssam break; 2006178354Ssam case IEEE80211_ELEMID_HTCAP: 2007178354Ssam htcap = frm; 2008178354Ssam break; 2009178354Ssam case IEEE80211_ELEMID_VENDOR: 2010178354Ssam if (iswpaoui(frm)) 2011178354Ssam wpa = frm; 2012178354Ssam else if (iswmeinfo(frm)) 2013178354Ssam wme = frm; 2014190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2015178354Ssam else if (isatherosoui(frm)) 2016178354Ssam ath = frm; 2017190391Ssam#endif 2018193655Ssam else if (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) { 2019178354Ssam if (ishtcapoui(frm) && htcap == NULL) 2020178354Ssam htcap = frm; 2021178354Ssam } 2022178354Ssam break; 2023178354Ssam } 2024178354Ssam frm += frm[1] + 2; 2025178354Ssam } 2026178354Ssam IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); 2027178354Ssam if (xrates != NULL) 2028178354Ssam IEEE80211_VERIFY_ELEMENT(xrates, 2029178354Ssam IEEE80211_RATE_MAXSIZE - rates[1], return); 2030178354Ssam IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); 2031178354Ssam IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); 2032178354Ssam if (htcap != NULL) { 2033178354Ssam IEEE80211_VERIFY_LENGTH(htcap[1], 2034178354Ssam htcap[0] == IEEE80211_ELEMID_VENDOR ? 2035178354Ssam 4 + sizeof(struct ieee80211_ie_htcap)-2 : 2036178354Ssam sizeof(struct ieee80211_ie_htcap)-2, 2037178354Ssam return); /* XXX just NULL out? */ 2038178354Ssam } 2039178354Ssam 2040178354Ssam if ((vap->iv_flags & IEEE80211_F_WPA) && 2041178354Ssam !wpa_assocreq(ni, &rsnparms, wh, wpa, rsn, capinfo)) 2042178354Ssam return; 2043178354Ssam /* discard challenge after association */ 2044178354Ssam if (ni->ni_challenge != NULL) { 2045186302Ssam free(ni->ni_challenge, M_80211_NODE); 2046178354Ssam ni->ni_challenge = NULL; 2047178354Ssam } 2048178354Ssam /* NB: 802.11 spec says to ignore station's privacy bit */ 2049178354Ssam if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) { 2050178354Ssam capinfomismatch(ni, wh, reassoc, resp, 2051178354Ssam "capability", capinfo); 2052178354Ssam return; 2053178354Ssam } 2054178354Ssam /* 2055178354Ssam * Disallow re-associate w/ invalid slot time setting. 2056178354Ssam */ 2057178354Ssam if (ni->ni_associd != 0 && 2058178354Ssam IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && 2059178354Ssam ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) { 2060178354Ssam capinfomismatch(ni, wh, reassoc, resp, 2061178354Ssam "slot time", capinfo); 2062178354Ssam return; 2063178354Ssam } 2064178354Ssam rate = ieee80211_setup_rates(ni, rates, xrates, 2065178354Ssam IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | 2066178354Ssam IEEE80211_F_DONEGO | IEEE80211_F_DODEL); 2067178354Ssam if (rate & IEEE80211_RATE_BASIC) { 2068178354Ssam ratesetmismatch(ni, wh, reassoc, resp, "legacy", rate); 2069178354Ssam vap->iv_stats.is_rx_assoc_norate++; 2070178354Ssam return; 2071178354Ssam } 2072178354Ssam /* 2073178354Ssam * If constrained to 11g-only stations reject an 2074178354Ssam * 11b-only station. We cheat a bit here by looking 2075178354Ssam * at the max negotiated xmit rate and assuming anyone 2076178354Ssam * with a best rate <24Mb/s is an 11b station. 2077178354Ssam */ 2078178354Ssam if ((vap->iv_flags & IEEE80211_F_PUREG) && rate < 48) { 2079178354Ssam ratesetmismatch(ni, wh, reassoc, resp, "11g", rate); 2080178354Ssam vap->iv_stats.is_rx_assoc_norate++; 2081178354Ssam return; 2082178354Ssam } 2083178354Ssam /* 2084178354Ssam * Do HT rate set handling and setup HT node state. 2085178354Ssam */ 2086178354Ssam ni->ni_chan = vap->iv_bss->ni_chan; 2087178354Ssam if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) { 2088178354Ssam rate = ieee80211_setup_htrates(ni, htcap, 2089178354Ssam IEEE80211_F_DOFMCS | IEEE80211_F_DONEGO | 2090178354Ssam IEEE80211_F_DOBRS); 2091178354Ssam if (rate & IEEE80211_RATE_BASIC) { 2092178354Ssam ratesetmismatch(ni, wh, reassoc, resp, 2093178354Ssam "HT", rate); 2094178354Ssam vap->iv_stats.is_ht_assoc_norate++; 2095178354Ssam return; 2096178354Ssam } 2097183254Ssam ieee80211_ht_node_init(ni); 2098183254Ssam ieee80211_ht_updatehtcap(ni, htcap); 2099178354Ssam } else if (ni->ni_flags & IEEE80211_NODE_HT) 2100178354Ssam ieee80211_ht_node_cleanup(ni); 2101190579Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2102190579Ssam else if (ni->ni_ath_flags & IEEE80211_NODE_ATH) 2103190579Ssam ieee80211_ff_node_cleanup(ni); 2104190579Ssam#endif 2105178354Ssam /* 2106178354Ssam * Allow AMPDU operation only with unencrypted traffic 2107178354Ssam * or AES-CCM; the 11n spec only specifies these ciphers 2108178354Ssam * so permitting any others is undefined and can lead 2109178354Ssam * to interoperability problems. 2110178354Ssam */ 2111178354Ssam if ((ni->ni_flags & IEEE80211_NODE_HT) && 2112178354Ssam (((vap->iv_flags & IEEE80211_F_WPA) && 2113178354Ssam rsnparms.rsn_ucastcipher != IEEE80211_CIPHER_AES_CCM) || 2114178354Ssam (vap->iv_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) { 2115178354Ssam IEEE80211_NOTE(vap, 2116178354Ssam IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, 2117178354Ssam "disallow HT use because WEP or TKIP requested, " 2118178354Ssam "capinfo 0x%x ucastcipher %d", capinfo, 2119178354Ssam rsnparms.rsn_ucastcipher); 2120178354Ssam ieee80211_ht_node_cleanup(ni); 2121178354Ssam vap->iv_stats.is_ht_assoc_downgrade++; 2122178354Ssam } 2123178354Ssam /* 2124178354Ssam * If constrained to 11n-only stations reject legacy stations. 2125178354Ssam */ 2126193655Ssam if ((vap->iv_flags_ht & IEEE80211_FHT_PUREN) && 2127178354Ssam (ni->ni_flags & IEEE80211_NODE_HT) == 0) { 2128178354Ssam htcapmismatch(ni, wh, reassoc, resp); 2129178354Ssam vap->iv_stats.is_ht_assoc_nohtcap++; 2130178354Ssam return; 2131178354Ssam } 2132178354Ssam IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 2133192468Ssam ni->ni_noise = nf; 2134178354Ssam ni->ni_intval = lintval; 2135178354Ssam ni->ni_capinfo = capinfo; 2136178354Ssam ni->ni_fhdwell = vap->iv_bss->ni_fhdwell; 2137178354Ssam ni->ni_fhindex = vap->iv_bss->ni_fhindex; 2138178354Ssam /* 2139178354Ssam * Store the IEs. 2140178354Ssam * XXX maybe better to just expand 2141178354Ssam */ 2142178354Ssam if (ieee80211_ies_init(&ni->ni_ies, sfrm, efrm - sfrm)) { 2143178354Ssam#define setie(_ie, _off) ieee80211_ies_setie(ni->ni_ies, _ie, _off) 2144178354Ssam if (wpa != NULL) 2145178354Ssam setie(wpa_ie, wpa - sfrm); 2146178354Ssam if (rsn != NULL) 2147178354Ssam setie(rsn_ie, rsn - sfrm); 2148178354Ssam if (htcap != NULL) 2149178354Ssam setie(htcap_ie, htcap - sfrm); 2150178354Ssam if (wme != NULL) { 2151178354Ssam setie(wme_ie, wme - sfrm); 2152178354Ssam /* 2153178354Ssam * Mark node as capable of QoS. 2154178354Ssam */ 2155178354Ssam ni->ni_flags |= IEEE80211_NODE_QOS; 2156178354Ssam } else 2157178354Ssam ni->ni_flags &= ~IEEE80211_NODE_QOS; 2158190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 2159178354Ssam if (ath != NULL) { 2160178354Ssam setie(ath_ie, ath - sfrm); 2161178354Ssam /* 2162178354Ssam * Parse ATH station parameters. 2163178354Ssam */ 2164178354Ssam ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); 2165178354Ssam } else 2166190391Ssam#endif 2167178354Ssam ni->ni_ath_flags = 0; 2168178354Ssam#undef setie 2169178354Ssam } else { 2170178354Ssam ni->ni_flags &= ~IEEE80211_NODE_QOS; 2171178354Ssam ni->ni_ath_flags = 0; 2172178354Ssam } 2173178354Ssam ieee80211_node_join(ni, resp); 2174178354Ssam ieee80211_deliver_l2uf(ni); 2175178354Ssam break; 2176178354Ssam } 2177178354Ssam 2178178354Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 2179178354Ssam case IEEE80211_FC0_SUBTYPE_DISASSOC: { 2180178354Ssam uint16_t reason; 2181178354Ssam 2182178354Ssam if (vap->iv_state != IEEE80211_S_RUN || 2183178354Ssam /* NB: can happen when in promiscuous mode */ 2184178354Ssam !IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { 2185178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 2186178354Ssam break; 2187178354Ssam } 2188178354Ssam /* 2189178354Ssam * deauth/disassoc frame format 2190178354Ssam * [2] reason 2191178354Ssam */ 2192178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); 2193178354Ssam reason = le16toh(*(uint16_t *)frm); 2194178354Ssam if (subtype == IEEE80211_FC0_SUBTYPE_DEAUTH) { 2195178354Ssam vap->iv_stats.is_rx_deauth++; 2196178354Ssam IEEE80211_NODE_STAT(ni, rx_deauth); 2197178354Ssam } else { 2198178354Ssam vap->iv_stats.is_rx_disassoc++; 2199178354Ssam IEEE80211_NODE_STAT(ni, rx_disassoc); 2200178354Ssam } 2201178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, 2202178354Ssam "recv %s (reason %d)", ieee80211_mgt_subtype_name[subtype >> 2203178354Ssam IEEE80211_FC0_SUBTYPE_SHIFT], reason); 2204178354Ssam if (ni != vap->iv_bss) 2205178354Ssam ieee80211_node_leave(ni); 2206178354Ssam break; 2207178354Ssam } 2208178354Ssam 2209178354Ssam case IEEE80211_FC0_SUBTYPE_ACTION: 2210218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: 2211218958Sbschmidt if (ni == vap->iv_bss) { 2212218927Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 2213218958Sbschmidt wh, NULL, "%s", "unknown node"); 2214218958Sbschmidt vap->iv_stats.is_rx_mgtdiscard++; 2215218958Sbschmidt } else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && 2216218958Sbschmidt !IEEE80211_IS_MULTICAST(wh->i_addr1)) { 2217218958Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 2218218958Sbschmidt wh, NULL, "%s", "not for us"); 2219218958Sbschmidt vap->iv_stats.is_rx_mgtdiscard++; 2220218958Sbschmidt } else if (vap->iv_state != IEEE80211_S_RUN) { 2221218958Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 2222218927Sbschmidt wh, NULL, "wrong state %s", 2223218927Sbschmidt ieee80211_state_name[vap->iv_state]); 2224178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 2225218958Sbschmidt } else { 2226218958Sbschmidt if (ieee80211_parse_action(ni, m0) == 0) 2227218958Sbschmidt (void)ic->ic_recv_action(ni, wh, frm, efrm); 2228218927Sbschmidt } 2229178354Ssam break; 2230178354Ssam 2231178354Ssam case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 2232178354Ssam case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 2233218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ATIM: 2234218927Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 2235218927Sbschmidt wh, NULL, "%s", "not handled"); 2236218927Sbschmidt vap->iv_stats.is_rx_mgtdiscard++; 2237218927Sbschmidt break; 2238218927Sbschmidt 2239178354Ssam default: 2240178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 2241218927Sbschmidt wh, "mgt", "subtype 0x%x not handled", subtype); 2242178354Ssam vap->iv_stats.is_rx_badsubtype++; 2243178354Ssam break; 2244178354Ssam } 2245178354Ssam} 2246178354Ssam 2247191546Ssamstatic void 2248191546Ssamhostap_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) 2249191546Ssam{ 2250191546Ssam switch (subtype) { 2251191546Ssam case IEEE80211_FC0_SUBTYPE_PS_POLL: 2252241138Sadrian ni->ni_vap->iv_recv_pspoll(ni, m); 2253191546Ssam break; 2254191546Ssam case IEEE80211_FC0_SUBTYPE_BAR: 2255191546Ssam ieee80211_recv_bar(ni, m); 2256191546Ssam break; 2257191546Ssam } 2258191546Ssam} 2259191546Ssam 2260178354Ssam/* 2261178354Ssam * Process a received ps-poll frame. 2262178354Ssam */ 2263241138Sadrianvoid 2264241138Sadrianieee80211_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0) 2265178354Ssam{ 2266178354Ssam struct ieee80211vap *vap = ni->ni_vap; 2267248069Sadrian struct ieee80211com *ic = vap->iv_ic; 2268178354Ssam struct ieee80211_frame_min *wh; 2269178354Ssam struct mbuf *m; 2270178354Ssam uint16_t aid; 2271178354Ssam int qlen; 2272178354Ssam 2273178354Ssam wh = mtod(m0, struct ieee80211_frame_min *); 2274178354Ssam if (ni->ni_associd == 0) { 2275178354Ssam IEEE80211_DISCARD(vap, 2276178354Ssam IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, 2277178354Ssam (struct ieee80211_frame *) wh, NULL, 2278178354Ssam "%s", "unassociated station"); 2279178354Ssam vap->iv_stats.is_ps_unassoc++; 2280178354Ssam IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 2281178354Ssam IEEE80211_REASON_NOT_ASSOCED); 2282178354Ssam return; 2283178354Ssam } 2284178354Ssam 2285178354Ssam aid = le16toh(*(uint16_t *)wh->i_dur); 2286178354Ssam if (aid != ni->ni_associd) { 2287178354Ssam IEEE80211_DISCARD(vap, 2288178354Ssam IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, 2289178354Ssam (struct ieee80211_frame *) wh, NULL, 2290178354Ssam "aid mismatch: sta aid 0x%x poll aid 0x%x", 2291178354Ssam ni->ni_associd, aid); 2292178354Ssam vap->iv_stats.is_ps_badaid++; 2293180837Ssam /* 2294180837Ssam * NB: We used to deauth the station but it turns out 2295180837Ssam * the Blackberry Curve 8230 (and perhaps other devices) 2296180837Ssam * sometimes send the wrong AID when WME is negotiated. 2297180837Ssam * Being more lenient here seems ok as we already check 2298180837Ssam * the station is associated and we only return frames 2299180837Ssam * queued for the station (i.e. we don't use the AID). 2300180837Ssam */ 2301178354Ssam return; 2302178354Ssam } 2303178354Ssam 2304178354Ssam /* Okay, take the first queued packet and put it out... */ 2305184288Ssam m = ieee80211_node_psq_dequeue(ni, &qlen); 2306178354Ssam if (m == NULL) { 2307178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_POWER, wh->i_addr2, 2308178354Ssam "%s", "recv ps-poll, but queue empty"); 2309178354Ssam ieee80211_send_nulldata(ieee80211_ref_node(ni)); 2310178354Ssam vap->iv_stats.is_ps_qempty++; /* XXX node stat */ 2311178354Ssam if (vap->iv_set_tim != NULL) 2312178354Ssam vap->iv_set_tim(ni, 0); /* just in case */ 2313178354Ssam return; 2314178354Ssam } 2315178354Ssam /* 2316178354Ssam * If there are more packets, set the more packets bit 2317178354Ssam * in the packet dispatched to the station; otherwise 2318178354Ssam * turn off the TIM bit. 2319178354Ssam */ 2320178354Ssam if (qlen != 0) { 2321178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 2322178354Ssam "recv ps-poll, send packet, %u still queued", qlen); 2323178354Ssam m->m_flags |= M_MORE_DATA; 2324178354Ssam } else { 2325178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, 2326178354Ssam "%s", "recv ps-poll, send packet, queue empty"); 2327178354Ssam if (vap->iv_set_tim != NULL) 2328178354Ssam vap->iv_set_tim(ni, 0); 2329178354Ssam } 2330178354Ssam m->m_flags |= M_PWR_SAV; /* bypass PS handling */ 2331184288Ssam 2332245098Sadrian /* 2333248069Sadrian * Do the right thing; if it's an encap'ed frame then 2334254082Sadrian * call ieee80211_parent_xmitpkt() (and free the ref) else 2335254082Sadrian * call ieee80211_vap_xmitpkt(). 2336245098Sadrian */ 2337248069Sadrian if (m->m_flags & M_ENCAP) { 2338254082Sadrian if (ieee80211_parent_xmitpkt(ic, m) != 0) 2339245098Sadrian ieee80211_free_node(ni); 2340248069Sadrian } else { 2341254082Sadrian (void) ieee80211_vap_xmitpkt(vap, m); 2342245098Sadrian } 2343178354Ssam} 2344