ieee80211_adhoc.c revision 191754
1178354Ssam/*- 2186904Ssam * Copyright (c) 2007-2009 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_adhoc.c 191754 2009-05-02 20:18:18Z sam $"); 29178354Ssam#endif 30178354Ssam 31178354Ssam/* 32178354Ssam * IEEE 802.11 IBSS 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> 51178354Ssam#include <net/if_media.h> 52178354Ssam#include <net/if_llc.h> 53178354Ssam#include <net/ethernet.h> 54178354Ssam 55178354Ssam#include <net/bpf.h> 56178354Ssam 57178354Ssam#include <net80211/ieee80211_var.h> 58178354Ssam#include <net80211/ieee80211_adhoc.h> 59178354Ssam#include <net80211/ieee80211_input.h> 60190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 61190391Ssam#include <net80211/ieee80211_superg.h> 62190391Ssam#endif 63186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 64186904Ssam#include <net80211/ieee80211_tdma.h> 65186904Ssam#endif 66178354Ssam 67178354Ssam#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) 68178354Ssam 69178354Ssamstatic void adhoc_vattach(struct ieee80211vap *); 70178354Ssamstatic int adhoc_newstate(struct ieee80211vap *, enum ieee80211_state, int); 71178354Ssamstatic int adhoc_input(struct ieee80211_node *, struct mbuf *, 72178354Ssam int rssi, int noise, uint32_t rstamp); 73178354Ssamstatic void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *, 74178354Ssam int subtype, int rssi, int noise, uint32_t rstamp); 75184276Ssamstatic void ahdemo_recv_mgmt(struct ieee80211_node *, struct mbuf *, 76184276Ssam int subtype, int rssi, int noise, uint32_t rstamp); 77191546Ssamstatic void adhoc_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype); 78178354Ssam 79178354Ssamvoid 80178354Ssamieee80211_adhoc_attach(struct ieee80211com *ic) 81178354Ssam{ 82178354Ssam ic->ic_vattach[IEEE80211_M_IBSS] = adhoc_vattach; 83178354Ssam ic->ic_vattach[IEEE80211_M_AHDEMO] = adhoc_vattach; 84178354Ssam} 85178354Ssam 86178354Ssamvoid 87178354Ssamieee80211_adhoc_detach(struct ieee80211com *ic) 88178354Ssam{ 89178354Ssam} 90178354Ssam 91178354Ssamstatic void 92178354Ssamadhoc_vdetach(struct ieee80211vap *vap) 93178354Ssam{ 94178354Ssam} 95178354Ssam 96178354Ssamstatic void 97178354Ssamadhoc_vattach(struct ieee80211vap *vap) 98178354Ssam{ 99178354Ssam vap->iv_newstate = adhoc_newstate; 100178354Ssam vap->iv_input = adhoc_input; 101184276Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) 102184276Ssam vap->iv_recv_mgmt = adhoc_recv_mgmt; 103184276Ssam else 104184276Ssam vap->iv_recv_mgmt = ahdemo_recv_mgmt; 105191546Ssam vap->iv_recv_ctl = adhoc_recv_ctl; 106178354Ssam vap->iv_opdetach = adhoc_vdetach; 107186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 108186904Ssam /* 109186904Ssam * Throw control to tdma support. Note we do this 110186904Ssam * after setting up our callbacks so it can piggyback 111186904Ssam * on top of us. 112186904Ssam */ 113186904Ssam if (vap->iv_caps & IEEE80211_C_TDMA) 114186904Ssam ieee80211_tdma_vattach(vap); 115186904Ssam#endif 116178354Ssam} 117178354Ssam 118188466Ssamstatic void 119188466Ssamsta_leave(void *arg, struct ieee80211_node *ni) 120188466Ssam{ 121188466Ssam struct ieee80211vap *vap = arg; 122188466Ssam 123188466Ssam if (ni->ni_vap == vap && ni != vap->iv_bss) 124188466Ssam ieee80211_node_leave(ni); 125188466Ssam} 126188466Ssam 127178354Ssam/* 128178354Ssam * IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler. 129178354Ssam */ 130178354Ssamstatic int 131178354Ssamadhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 132178354Ssam{ 133178354Ssam struct ieee80211com *ic = vap->iv_ic; 134178354Ssam struct ieee80211_node *ni; 135178354Ssam enum ieee80211_state ostate; 136178354Ssam 137178354Ssam IEEE80211_LOCK_ASSERT(vap->iv_ic); 138178354Ssam 139178354Ssam ostate = vap->iv_state; 140178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 141178354Ssam __func__, ieee80211_state_name[ostate], 142178354Ssam ieee80211_state_name[nstate], arg); 143178354Ssam vap->iv_state = nstate; /* state transition */ 144178354Ssam if (ostate != IEEE80211_S_SCAN) 145178354Ssam ieee80211_cancel_scan(vap); /* background scan */ 146178354Ssam ni = vap->iv_bss; /* NB: no reference held */ 147178354Ssam switch (nstate) { 148178354Ssam case IEEE80211_S_INIT: 149178354Ssam switch (ostate) { 150178354Ssam case IEEE80211_S_SCAN: 151178354Ssam ieee80211_cancel_scan(vap); 152178354Ssam break; 153178354Ssam default: 154178354Ssam break; 155178354Ssam } 156178354Ssam if (ostate != IEEE80211_S_INIT) { 157178354Ssam /* NB: optimize INIT -> INIT case */ 158178354Ssam ieee80211_reset_bss(vap); 159178354Ssam } 160178354Ssam break; 161178354Ssam case IEEE80211_S_SCAN: 162178354Ssam switch (ostate) { 163188466Ssam case IEEE80211_S_RUN: /* beacon miss */ 164188466Ssam /* purge station table; entries are stale */ 165188466Ssam ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap); 166188466Ssam /* fall thru... */ 167178354Ssam case IEEE80211_S_INIT: 168178354Ssam if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && 169178354Ssam !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { 170178354Ssam /* 171178354Ssam * Already have a channel; bypass the 172178354Ssam * scan and startup immediately. 173178354Ssam */ 174178354Ssam ieee80211_create_ibss(vap, vap->iv_des_chan); 175178354Ssam break; 176178354Ssam } 177178354Ssam /* 178178354Ssam * Initiate a scan. We can come here as a result 179178354Ssam * of an IEEE80211_IOC_SCAN_REQ too in which case 180178354Ssam * the vap will be marked with IEEE80211_FEXT_SCANREQ 181178354Ssam * and the scan request parameters will be present 182178354Ssam * in iv_scanreq. Otherwise we do the default. 183178354Ssam */ 184178354Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 185178354Ssam ieee80211_check_scan(vap, 186178354Ssam vap->iv_scanreq_flags, 187178354Ssam vap->iv_scanreq_duration, 188178354Ssam vap->iv_scanreq_mindwell, 189178354Ssam vap->iv_scanreq_maxdwell, 190178354Ssam vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 191178354Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 192178354Ssam } else 193178354Ssam ieee80211_check_scan_current(vap); 194178354Ssam break; 195178354Ssam case IEEE80211_S_SCAN: 196178354Ssam /* 197178354Ssam * This can happen because of a change in state 198178354Ssam * that requires a reset. Trigger a new scan 199178354Ssam * unless we're in manual roaming mode in which 200178354Ssam * case an application must issue an explicit request. 201178354Ssam */ 202178354Ssam if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) 203178354Ssam ieee80211_check_scan_current(vap); 204178354Ssam break; 205178354Ssam default: 206178354Ssam goto invalid; 207178354Ssam } 208178354Ssam break; 209178354Ssam case IEEE80211_S_RUN: 210178354Ssam if (vap->iv_flags & IEEE80211_F_WPA) { 211178354Ssam /* XXX validate prerequisites */ 212178354Ssam } 213178354Ssam switch (ostate) { 214178354Ssam case IEEE80211_S_SCAN: 215178354Ssam#ifdef IEEE80211_DEBUG 216178354Ssam if (ieee80211_msg_debug(vap)) { 217178354Ssam ieee80211_note(vap, 218178354Ssam "synchronized with %s ssid ", 219178354Ssam ether_sprintf(ni->ni_bssid)); 220178354Ssam ieee80211_print_essid(vap->iv_bss->ni_essid, 221178354Ssam ni->ni_esslen); 222178354Ssam /* XXX MCS/HT */ 223178354Ssam printf(" channel %d start %uMb\n", 224178354Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), 225178354Ssam IEEE80211_RATE2MBS(ni->ni_txrate)); 226178354Ssam } 227178354Ssam#endif 228178354Ssam break; 229178354Ssam default: 230178354Ssam goto invalid; 231178354Ssam } 232178354Ssam /* 233178354Ssam * When 802.1x is not in use mark the port authorized 234178354Ssam * at this point so traffic can flow. 235178354Ssam */ 236178354Ssam if (ni->ni_authmode != IEEE80211_AUTH_8021X) 237178354Ssam ieee80211_node_authorize(ni); 238184345Ssam /* 239184345Ssam * Fake association when joining an existing bss. 240184345Ssam */ 241184345Ssam if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, vap->iv_myaddr) && 242184345Ssam ic->ic_newassoc != NULL) 243184345Ssam ic->ic_newassoc(ni, ostate != IEEE80211_S_RUN); 244178354Ssam break; 245178354Ssam case IEEE80211_S_SLEEP: 246178354Ssam ieee80211_sta_pwrsave(vap, 0); 247178354Ssam break; 248178354Ssam default: 249178354Ssam invalid: 250184268Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 251184268Ssam "%s: unexpected state transition %s -> %s\n", __func__, 252178354Ssam ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 253178354Ssam break; 254178354Ssam } 255178354Ssam return 0; 256178354Ssam} 257178354Ssam 258178354Ssam/* 259178354Ssam * Decide if a received management frame should be 260178354Ssam * printed when debugging is enabled. This filters some 261178354Ssam * of the less interesting frames that come frequently 262178354Ssam * (e.g. beacons). 263178354Ssam */ 264178354Ssamstatic __inline int 265178354Ssamdoprint(struct ieee80211vap *vap, int subtype) 266178354Ssam{ 267178354Ssam switch (subtype) { 268178354Ssam case IEEE80211_FC0_SUBTYPE_BEACON: 269178354Ssam return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); 270178354Ssam case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 271178354Ssam return 1; 272178354Ssam } 273178354Ssam return 1; 274178354Ssam} 275178354Ssam 276178354Ssam/* 277178354Ssam * Process a received frame. The node associated with the sender 278178354Ssam * should be supplied. If nothing was found in the node table then 279178354Ssam * the caller is assumed to supply a reference to iv_bss instead. 280178354Ssam * The RSSI and a timestamp are also supplied. The RSSI data is used 281178354Ssam * during AP scanning to select a AP to associate with; it can have 282178354Ssam * any units so long as values have consistent units and higher values 283178354Ssam * mean ``better signal''. The receive timestamp is currently not used 284178354Ssam * by the 802.11 layer. 285178354Ssam */ 286178354Ssamstatic int 287178354Ssamadhoc_input(struct ieee80211_node *ni, struct mbuf *m, 288178354Ssam int rssi, int noise, uint32_t rstamp) 289178354Ssam{ 290178354Ssam#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) 291178354Ssam#define HAS_SEQ(type) ((type & 0x4) == 0) 292178354Ssam struct ieee80211vap *vap = ni->ni_vap; 293178354Ssam struct ieee80211com *ic = ni->ni_ic; 294178354Ssam struct ifnet *ifp = vap->iv_ifp; 295178354Ssam struct ieee80211_frame *wh; 296178354Ssam struct ieee80211_key *key; 297178354Ssam struct ether_header *eh; 298178354Ssam int hdrspace, need_tap; 299178354Ssam uint8_t dir, type, subtype, qos; 300178354Ssam uint8_t *bssid; 301178354Ssam uint16_t rxseq; 302178354Ssam 303183247Ssam if (m->m_flags & M_AMPDU_MPDU) { 304178354Ssam /* 305178354Ssam * Fastpath for A-MPDU reorder q resubmission. Frames 306183247Ssam * w/ M_AMPDU_MPDU marked have already passed through 307183247Ssam * here but were received out of order and been held on 308183247Ssam * the reorder queue. When resubmitted they are marked 309183247Ssam * with the M_AMPDU_MPDU flag and we can bypass most of 310183247Ssam * the normal processing. 311178354Ssam */ 312178354Ssam wh = mtod(m, struct ieee80211_frame *); 313178354Ssam type = IEEE80211_FC0_TYPE_DATA; 314178354Ssam dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 315178354Ssam subtype = IEEE80211_FC0_SUBTYPE_QOS; 316178354Ssam hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ 317178354Ssam goto resubmit_ampdu; 318178354Ssam } 319178354Ssam 320178354Ssam KASSERT(ni != NULL, ("null node")); 321178354Ssam ni->ni_inact = ni->ni_inact_reload; 322178354Ssam 323178354Ssam need_tap = 1; /* mbuf need to be tapped. */ 324178354Ssam type = -1; /* undefined */ 325178354Ssam 326178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { 327178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 328178354Ssam ni->ni_macaddr, NULL, 329178354Ssam "too short (1): len %u", m->m_pkthdr.len); 330178354Ssam vap->iv_stats.is_rx_tooshort++; 331178354Ssam goto out; 332178354Ssam } 333178354Ssam /* 334178354Ssam * Bit of a cheat here, we use a pointer for a 3-address 335178354Ssam * frame format but don't reference fields past outside 336178354Ssam * ieee80211_frame_min w/o first validating the data is 337178354Ssam * present. 338178354Ssam */ 339178354Ssam wh = mtod(m, struct ieee80211_frame *); 340178354Ssam 341178354Ssam if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 342178354Ssam IEEE80211_FC0_VERSION_0) { 343178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 344191547Ssam ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x", 345191547Ssam wh->i_fc[0], wh->i_fc[1]); 346178354Ssam vap->iv_stats.is_rx_badversion++; 347178354Ssam goto err; 348178354Ssam } 349178354Ssam 350178354Ssam dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 351178354Ssam type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 352178354Ssam subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 353178354Ssam if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 354178354Ssam if (dir != IEEE80211_FC1_DIR_NODS) 355178354Ssam bssid = wh->i_addr1; 356178354Ssam else if (type == IEEE80211_FC0_TYPE_CTL) 357178354Ssam bssid = wh->i_addr1; 358178354Ssam else { 359178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 360178354Ssam IEEE80211_DISCARD_MAC(vap, 361178354Ssam IEEE80211_MSG_ANY, ni->ni_macaddr, 362178354Ssam NULL, "too short (2): len %u", 363178354Ssam m->m_pkthdr.len); 364178354Ssam vap->iv_stats.is_rx_tooshort++; 365178354Ssam goto out; 366178354Ssam } 367178354Ssam bssid = wh->i_addr3; 368178354Ssam } 369178354Ssam /* 370178354Ssam * Validate the bssid. 371178354Ssam */ 372178354Ssam if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && 373178354Ssam !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { 374178354Ssam /* not interested in */ 375178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 376178354Ssam bssid, NULL, "%s", "not to bss"); 377178354Ssam vap->iv_stats.is_rx_wrongbss++; 378178354Ssam goto out; 379178354Ssam } 380178354Ssam /* 381178354Ssam * Data frame, cons up a node when it doesn't 382178354Ssam * exist. This should probably done after an ACL check. 383178354Ssam */ 384178354Ssam if (type == IEEE80211_FC0_TYPE_DATA && 385178354Ssam ni == vap->iv_bss && 386178354Ssam !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 387178354Ssam /* 388186904Ssam * Beware of frames that come in too early; we 389186904Ssam * can receive broadcast frames and creating sta 390186904Ssam * entries will blow up because there is no bss 391186904Ssam * channel yet. 392186904Ssam */ 393186904Ssam if (vap->iv_state != IEEE80211_S_RUN) { 394186904Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 395186904Ssam wh, "data", "not in RUN state (%s)", 396186904Ssam ieee80211_state_name[vap->iv_state]); 397186904Ssam vap->iv_stats.is_rx_badstate++; 398186904Ssam goto err; 399186904Ssam } 400186904Ssam /* 401178354Ssam * Fake up a node for this newly 402178354Ssam * discovered member of the IBSS. 403178354Ssam */ 404178354Ssam ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2); 405178354Ssam if (ni == NULL) { 406178354Ssam /* NB: stat kept for alloc failure */ 407178354Ssam goto err; 408178354Ssam } 409178354Ssam } 410178354Ssam IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 411178354Ssam ni->ni_noise = noise; 412178354Ssam ni->ni_rstamp = rstamp; 413178354Ssam if (HAS_SEQ(type)) { 414178354Ssam uint8_t tid = ieee80211_gettid(wh); 415178354Ssam if (IEEE80211_QOS_HAS_SEQ(wh) && 416178354Ssam TID_TO_WME_AC(tid) >= WME_AC_VI) 417178354Ssam ic->ic_wme.wme_hipri_traffic++; 418178354Ssam rxseq = le16toh(*(uint16_t *)wh->i_seq); 419178354Ssam if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 && 420178354Ssam (wh->i_fc[1] & IEEE80211_FC1_RETRY) && 421178354Ssam SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) { 422178354Ssam /* duplicate, discard */ 423178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 424178354Ssam bssid, "duplicate", 425178354Ssam "seqno <%u,%u> fragno <%u,%u> tid %u", 426178354Ssam rxseq >> IEEE80211_SEQ_SEQ_SHIFT, 427178354Ssam ni->ni_rxseqs[tid] >> 428178354Ssam IEEE80211_SEQ_SEQ_SHIFT, 429178354Ssam rxseq & IEEE80211_SEQ_FRAG_MASK, 430178354Ssam ni->ni_rxseqs[tid] & 431178354Ssam IEEE80211_SEQ_FRAG_MASK, 432178354Ssam tid); 433178354Ssam vap->iv_stats.is_rx_dup++; 434178354Ssam IEEE80211_NODE_STAT(ni, rx_dup); 435178354Ssam goto out; 436178354Ssam } 437178354Ssam ni->ni_rxseqs[tid] = rxseq; 438178354Ssam } 439178354Ssam } 440178354Ssam 441178354Ssam switch (type) { 442178354Ssam case IEEE80211_FC0_TYPE_DATA: 443178354Ssam hdrspace = ieee80211_hdrspace(ic, wh); 444178354Ssam if (m->m_len < hdrspace && 445178354Ssam (m = m_pullup(m, hdrspace)) == NULL) { 446178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 447178354Ssam ni->ni_macaddr, NULL, 448178354Ssam "data too short: expecting %u", hdrspace); 449178354Ssam vap->iv_stats.is_rx_tooshort++; 450178354Ssam goto out; /* XXX */ 451178354Ssam } 452178354Ssam if (dir != IEEE80211_FC1_DIR_NODS) { 453178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 454178354Ssam wh, "data", "incorrect dir 0x%x", dir); 455178354Ssam vap->iv_stats.is_rx_wrongdir++; 456178354Ssam goto out; 457178354Ssam } 458178354Ssam /* XXX no power-save support */ 459178354Ssam 460178354Ssam /* 461183247Ssam * Handle A-MPDU re-ordering. If the frame is to be 462183247Ssam * processed directly then ieee80211_ampdu_reorder 463178354Ssam * will return 0; otherwise it has consumed the mbuf 464178354Ssam * and we should do nothing more with it. 465178354Ssam */ 466183247Ssam if ((m->m_flags & M_AMPDU) && 467178354Ssam ieee80211_ampdu_reorder(ni, m) != 0) { 468178354Ssam m = NULL; 469178354Ssam goto out; 470178354Ssam } 471178354Ssam resubmit_ampdu: 472178354Ssam 473178354Ssam /* 474178354Ssam * Handle privacy requirements. Note that we 475178354Ssam * must not be preempted from here until after 476178354Ssam * we (potentially) call ieee80211_crypto_demic; 477178354Ssam * otherwise we may violate assumptions in the 478178354Ssam * crypto cipher modules used to do delayed update 479178354Ssam * of replay sequence numbers. 480178354Ssam */ 481178354Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 482178354Ssam if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 483178354Ssam /* 484178354Ssam * Discard encrypted frames when privacy is off. 485178354Ssam */ 486178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 487178354Ssam wh, "WEP", "%s", "PRIVACY off"); 488178354Ssam vap->iv_stats.is_rx_noprivacy++; 489178354Ssam IEEE80211_NODE_STAT(ni, rx_noprivacy); 490178354Ssam goto out; 491178354Ssam } 492178354Ssam key = ieee80211_crypto_decap(ni, m, hdrspace); 493178354Ssam if (key == NULL) { 494178354Ssam /* NB: stats+msgs handled in crypto_decap */ 495178354Ssam IEEE80211_NODE_STAT(ni, rx_wepfail); 496178354Ssam goto out; 497178354Ssam } 498178354Ssam wh = mtod(m, struct ieee80211_frame *); 499178354Ssam wh->i_fc[1] &= ~IEEE80211_FC1_WEP; 500178354Ssam } else { 501178354Ssam /* XXX M_WEP and IEEE80211_F_PRIVACY */ 502178354Ssam key = NULL; 503178354Ssam } 504178354Ssam 505178354Ssam /* 506178354Ssam * Save QoS bits for use below--before we strip the header. 507178354Ssam */ 508178354Ssam if (subtype == IEEE80211_FC0_SUBTYPE_QOS) { 509178354Ssam qos = (dir == IEEE80211_FC1_DIR_DSTODS) ? 510178354Ssam ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] : 511178354Ssam ((struct ieee80211_qosframe *)wh)->i_qos[0]; 512178354Ssam } else 513178354Ssam qos = 0; 514178354Ssam 515178354Ssam /* 516178354Ssam * Next up, any fragmentation. 517178354Ssam */ 518178354Ssam if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 519178354Ssam m = ieee80211_defrag(ni, m, hdrspace); 520178354Ssam if (m == NULL) { 521178354Ssam /* Fragment dropped or frame not complete yet */ 522178354Ssam goto out; 523178354Ssam } 524178354Ssam } 525178354Ssam wh = NULL; /* no longer valid, catch any uses */ 526178354Ssam 527178354Ssam /* 528178354Ssam * Next strip any MSDU crypto bits. 529178354Ssam */ 530178354Ssam if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) { 531178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 532178354Ssam ni->ni_macaddr, "data", "%s", "demic error"); 533178354Ssam vap->iv_stats.is_rx_demicfail++; 534178354Ssam IEEE80211_NODE_STAT(ni, rx_demicfail); 535178354Ssam goto out; 536178354Ssam } 537178354Ssam 538178354Ssam /* copy to listener after decrypt */ 539178354Ssam if (bpf_peers_present(vap->iv_rawbpf)) 540178354Ssam bpf_mtap(vap->iv_rawbpf, m); 541178354Ssam need_tap = 0; 542178354Ssam 543178354Ssam /* 544178354Ssam * Finally, strip the 802.11 header. 545178354Ssam */ 546178354Ssam m = ieee80211_decap(vap, m, hdrspace); 547178354Ssam if (m == NULL) { 548178354Ssam /* XXX mask bit to check for both */ 549178354Ssam /* don't count Null data frames as errors */ 550178354Ssam if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || 551178354Ssam subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) 552178354Ssam goto out; 553178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 554178354Ssam ni->ni_macaddr, "data", "%s", "decap error"); 555178354Ssam vap->iv_stats.is_rx_decap++; 556178354Ssam IEEE80211_NODE_STAT(ni, rx_decap); 557178354Ssam goto err; 558178354Ssam } 559178354Ssam eh = mtod(m, struct ether_header *); 560178354Ssam if (!ieee80211_node_is_authorized(ni)) { 561178354Ssam /* 562178354Ssam * Deny any non-PAE frames received prior to 563178354Ssam * authorization. For open/shared-key 564178354Ssam * authentication the port is mark authorized 565178354Ssam * after authentication completes. For 802.1x 566178354Ssam * the port is not marked authorized by the 567178354Ssam * authenticator until the handshake has completed. 568178354Ssam */ 569178354Ssam if (eh->ether_type != htons(ETHERTYPE_PAE)) { 570178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 571178354Ssam eh->ether_shost, "data", 572178354Ssam "unauthorized port: ether type 0x%x len %u", 573178354Ssam eh->ether_type, m->m_pkthdr.len); 574178354Ssam vap->iv_stats.is_rx_unauth++; 575178354Ssam IEEE80211_NODE_STAT(ni, rx_unauth); 576178354Ssam goto err; 577178354Ssam } 578178354Ssam } else { 579178354Ssam /* 580178354Ssam * When denying unencrypted frames, discard 581178354Ssam * any non-PAE frames received without encryption. 582178354Ssam */ 583178354Ssam if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && 584178354Ssam (key == NULL && (m->m_flags & M_WEP) == 0) && 585178354Ssam eh->ether_type != htons(ETHERTYPE_PAE)) { 586178354Ssam /* 587178354Ssam * Drop unencrypted frames. 588178354Ssam */ 589178354Ssam vap->iv_stats.is_rx_unencrypted++; 590178354Ssam IEEE80211_NODE_STAT(ni, rx_unencrypted); 591178354Ssam goto out; 592178354Ssam } 593178354Ssam } 594178354Ssam /* XXX require HT? */ 595178354Ssam if (qos & IEEE80211_QOS_AMSDU) { 596178354Ssam m = ieee80211_decap_amsdu(ni, m); 597178354Ssam if (m == NULL) 598178354Ssam return IEEE80211_FC0_TYPE_DATA; 599190391Ssam } else { 600190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 601190391Ssam m = ieee80211_decap_fastframe(vap, ni, m); 602190391Ssam if (m == NULL) 603178354Ssam return IEEE80211_FC0_TYPE_DATA; 604190391Ssam#endif 605178354Ssam } 606178354Ssam if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL) 607178354Ssam ieee80211_deliver_data(ni->ni_wdsvap, ni, m); 608178354Ssam else 609178354Ssam ieee80211_deliver_data(vap, ni, m); 610178354Ssam return IEEE80211_FC0_TYPE_DATA; 611178354Ssam 612178354Ssam case IEEE80211_FC0_TYPE_MGT: 613178354Ssam vap->iv_stats.is_rx_mgmt++; 614178354Ssam IEEE80211_NODE_STAT(ni, rx_mgmt); 615178354Ssam if (dir != IEEE80211_FC1_DIR_NODS) { 616178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 617178354Ssam wh, "data", "incorrect dir 0x%x", dir); 618178354Ssam vap->iv_stats.is_rx_wrongdir++; 619178354Ssam goto err; 620178354Ssam } 621178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 622178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 623178354Ssam ni->ni_macaddr, "mgt", "too short: len %u", 624178354Ssam m->m_pkthdr.len); 625178354Ssam vap->iv_stats.is_rx_tooshort++; 626178354Ssam goto out; 627178354Ssam } 628178354Ssam#ifdef IEEE80211_DEBUG 629178354Ssam if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || 630178354Ssam ieee80211_msg_dumppkts(vap)) { 631178354Ssam if_printf(ifp, "received %s from %s rssi %d\n", 632178354Ssam ieee80211_mgt_subtype_name[subtype >> 633178354Ssam IEEE80211_FC0_SUBTYPE_SHIFT], 634178354Ssam ether_sprintf(wh->i_addr2), rssi); 635178354Ssam } 636178354Ssam#endif 637178354Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 638178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 639178354Ssam wh, NULL, "%s", "WEP set but not permitted"); 640178354Ssam vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ 641178354Ssam goto out; 642178354Ssam } 643184276Ssam vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); 644191534Ssam goto out; 645178354Ssam 646178354Ssam case IEEE80211_FC0_TYPE_CTL: 647178354Ssam vap->iv_stats.is_rx_ctl++; 648178354Ssam IEEE80211_NODE_STAT(ni, rx_ctrl); 649191546Ssam vap->iv_recv_ctl(ni, m, subtype); 650178354Ssam goto out; 651191754Ssam 652178354Ssam default: 653178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 654178354Ssam wh, "bad", "frame type 0x%x", type); 655178354Ssam /* should not come here */ 656178354Ssam break; 657178354Ssam } 658178354Ssamerr: 659178354Ssam ifp->if_ierrors++; 660178354Ssamout: 661178354Ssam if (m != NULL) { 662191534Ssam if (need_tap && bpf_peers_present(vap->iv_rawbpf)) 663178354Ssam bpf_mtap(vap->iv_rawbpf, m); 664178354Ssam m_freem(m); 665178354Ssam } 666178354Ssam return type; 667178354Ssam#undef SEQ_LEQ 668178354Ssam} 669178354Ssam 670178354Ssamstatic int 671178354Ssamis11bclient(const uint8_t *rates, const uint8_t *xrates) 672178354Ssam{ 673178354Ssam static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11); 674178354Ssam int i; 675178354Ssam 676178354Ssam /* NB: the 11b clients we care about will not have xrates */ 677178354Ssam if (xrates != NULL || rates == NULL) 678178354Ssam return 0; 679178354Ssam for (i = 0; i < rates[1]; i++) { 680178354Ssam int r = rates[2+i] & IEEE80211_RATE_VAL; 681178354Ssam if (r > 2*11 || ((1<<r) & brates) == 0) 682178354Ssam return 0; 683178354Ssam } 684178354Ssam return 1; 685178354Ssam} 686178354Ssam 687178354Ssamstatic void 688178354Ssamadhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 689178354Ssam int subtype, int rssi, int noise, uint32_t rstamp) 690178354Ssam{ 691178354Ssam struct ieee80211vap *vap = ni->ni_vap; 692178354Ssam struct ieee80211com *ic = ni->ni_ic; 693178354Ssam struct ieee80211_frame *wh; 694178354Ssam uint8_t *frm, *efrm, *sfrm; 695178354Ssam uint8_t *ssid, *rates, *xrates; 696178354Ssam 697178354Ssam wh = mtod(m0, struct ieee80211_frame *); 698178354Ssam frm = (uint8_t *)&wh[1]; 699178354Ssam efrm = mtod(m0, uint8_t *) + m0->m_len; 700178354Ssam switch (subtype) { 701178354Ssam case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 702178354Ssam case IEEE80211_FC0_SUBTYPE_BEACON: { 703178354Ssam struct ieee80211_scanparams scan; 704178354Ssam /* 705178354Ssam * We process beacon/probe response 706178354Ssam * frames to discover neighbors. 707178354Ssam */ 708178354Ssam if (ieee80211_parse_beacon(ni, m0, &scan) != 0) 709178354Ssam return; 710178354Ssam /* 711178354Ssam * Count frame now that we know it's to be processed. 712178354Ssam */ 713178354Ssam if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { 714178354Ssam vap->iv_stats.is_rx_beacon++; /* XXX remove */ 715178354Ssam IEEE80211_NODE_STAT(ni, rx_beacons); 716178354Ssam } else 717178354Ssam IEEE80211_NODE_STAT(ni, rx_proberesp); 718178354Ssam /* 719178354Ssam * If scanning, just pass information to the scan module. 720178354Ssam */ 721178354Ssam if (ic->ic_flags & IEEE80211_F_SCAN) { 722178354Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { 723178354Ssam /* 724178354Ssam * Actively scanning a channel marked passive; 725178354Ssam * send a probe request now that we know there 726178354Ssam * is 802.11 traffic present. 727178354Ssam * 728178354Ssam * XXX check if the beacon we recv'd gives 729178354Ssam * us what we need and suppress the probe req 730178354Ssam */ 731178354Ssam ieee80211_probe_curchan(vap, 1); 732178354Ssam ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; 733178354Ssam } 734178354Ssam ieee80211_add_scan(vap, &scan, wh, 735178354Ssam subtype, rssi, noise, rstamp); 736178354Ssam return; 737178354Ssam } 738178354Ssam if (scan.capinfo & IEEE80211_CAPINFO_IBSS) { 739178354Ssam if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 740178354Ssam /* 741178354Ssam * Create a new entry in the neighbor table. 742178354Ssam */ 743178354Ssam ni = ieee80211_add_neighbor(vap, wh, &scan); 744178354Ssam } else if (ni->ni_capinfo == 0) { 745178354Ssam /* 746178354Ssam * Update faked node created on transmit. 747178354Ssam * Note this also updates the tsf. 748178354Ssam */ 749178354Ssam ieee80211_init_neighbor(ni, wh, &scan); 750178354Ssam } else { 751178354Ssam /* 752178354Ssam * Record tsf for potential resync. 753178354Ssam */ 754178354Ssam memcpy(ni->ni_tstamp.data, scan.tstamp, 755178354Ssam sizeof(ni->ni_tstamp)); 756178354Ssam } 757178354Ssam if (ni != NULL) { 758178354Ssam IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 759178354Ssam ni->ni_noise = noise; 760178354Ssam ni->ni_rstamp = rstamp; 761178354Ssam } 762178354Ssam } 763178354Ssam break; 764178354Ssam } 765178354Ssam 766178354Ssam case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 767178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 768184268Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 769184268Ssam wh, NULL, "wrong state %s", 770184268Ssam ieee80211_state_name[vap->iv_state]); 771178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 772178354Ssam return; 773178354Ssam } 774178354Ssam if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { 775178354Ssam /* frame must be directed */ 776184268Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 777184268Ssam wh, NULL, "%s", "not unicast"); 778178354Ssam vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ 779178354Ssam return; 780178354Ssam } 781178354Ssam 782178354Ssam /* 783178354Ssam * prreq frame format 784178354Ssam * [tlv] ssid 785178354Ssam * [tlv] supported rates 786178354Ssam * [tlv] extended supported rates 787178354Ssam */ 788178354Ssam ssid = rates = xrates = NULL; 789178354Ssam sfrm = frm; 790178354Ssam while (efrm - frm > 1) { 791178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 792178354Ssam switch (*frm) { 793178354Ssam case IEEE80211_ELEMID_SSID: 794178354Ssam ssid = frm; 795178354Ssam break; 796178354Ssam case IEEE80211_ELEMID_RATES: 797178354Ssam rates = frm; 798178354Ssam break; 799178354Ssam case IEEE80211_ELEMID_XRATES: 800178354Ssam xrates = frm; 801178354Ssam break; 802178354Ssam } 803178354Ssam frm += frm[1] + 2; 804178354Ssam } 805178354Ssam IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); 806178354Ssam if (xrates != NULL) 807178354Ssam IEEE80211_VERIFY_ELEMENT(xrates, 808178354Ssam IEEE80211_RATE_MAXSIZE - rates[1], return); 809178354Ssam IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); 810178354Ssam IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); 811178354Ssam if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { 812178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 813178354Ssam wh, NULL, 814178354Ssam "%s", "no ssid with ssid suppression enabled"); 815178354Ssam vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/ 816178354Ssam return; 817178354Ssam } 818178354Ssam 819178354Ssam /* XXX find a better class or define it's own */ 820178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, 821178354Ssam "%s", "recv probe req"); 822178354Ssam /* 823178354Ssam * Some legacy 11b clients cannot hack a complete 824178354Ssam * probe response frame. When the request includes 825178354Ssam * only a bare-bones rate set, communicate this to 826178354Ssam * the transmit side. 827178354Ssam */ 828178354Ssam ieee80211_send_proberesp(vap, wh->i_addr2, 829178354Ssam is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0); 830178354Ssam break; 831178354Ssam 832178354Ssam case IEEE80211_FC0_SUBTYPE_ACTION: { 833178354Ssam const struct ieee80211_action *ia; 834178354Ssam 835178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 836184268Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 837184268Ssam wh, NULL, "wrong state %s", 838184268Ssam ieee80211_state_name[vap->iv_state]); 839178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 840178354Ssam return; 841178354Ssam } 842178354Ssam /* 843178354Ssam * action frame format: 844178354Ssam * [1] category 845178354Ssam * [1] action 846178354Ssam * [tlv] parameters 847178354Ssam */ 848178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, 849178354Ssam sizeof(struct ieee80211_action), return); 850178354Ssam ia = (const struct ieee80211_action *) frm; 851178354Ssam 852178354Ssam vap->iv_stats.is_rx_action++; 853178354Ssam IEEE80211_NODE_STAT(ni, rx_action); 854178354Ssam 855178354Ssam /* verify frame payloads but defer processing */ 856178354Ssam /* XXX maybe push this to method */ 857178354Ssam switch (ia->ia_category) { 858178354Ssam case IEEE80211_ACTION_CAT_BA: 859178354Ssam switch (ia->ia_action) { 860178354Ssam case IEEE80211_ACTION_BA_ADDBA_REQUEST: 861178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, 862178354Ssam sizeof(struct ieee80211_action_ba_addbarequest), 863178354Ssam return); 864178354Ssam break; 865178354Ssam case IEEE80211_ACTION_BA_ADDBA_RESPONSE: 866178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, 867178354Ssam sizeof(struct ieee80211_action_ba_addbaresponse), 868178354Ssam return); 869178354Ssam break; 870178354Ssam case IEEE80211_ACTION_BA_DELBA: 871178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, 872178354Ssam sizeof(struct ieee80211_action_ba_delba), 873178354Ssam return); 874178354Ssam break; 875178354Ssam } 876178354Ssam break; 877178354Ssam case IEEE80211_ACTION_CAT_HT: 878178354Ssam switch (ia->ia_action) { 879178354Ssam case IEEE80211_ACTION_HT_TXCHWIDTH: 880178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, 881178354Ssam sizeof(struct ieee80211_action_ht_txchwidth), 882178354Ssam return); 883178354Ssam break; 884178354Ssam } 885178354Ssam break; 886178354Ssam } 887178354Ssam ic->ic_recv_action(ni, frm, efrm); 888178354Ssam break; 889178354Ssam } 890178354Ssam 891178354Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 892178354Ssam case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 893178354Ssam case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 894178354Ssam case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 895178354Ssam case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 896178354Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 897178354Ssam case IEEE80211_FC0_SUBTYPE_DISASSOC: 898184268Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 899184268Ssam wh, NULL, "%s", "not handled"); 900178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 901178354Ssam return; 902178354Ssam 903178354Ssam default: 904178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 905178354Ssam wh, "mgt", "subtype 0x%x not handled", subtype); 906178354Ssam vap->iv_stats.is_rx_badsubtype++; 907178354Ssam break; 908178354Ssam } 909178354Ssam} 910178354Ssam#undef IEEE80211_VERIFY_LENGTH 911178354Ssam#undef IEEE80211_VERIFY_ELEMENT 912184276Ssam 913184276Ssamstatic void 914184276Ssamahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 915184276Ssam int subtype, int rssi, int noise, uint32_t rstamp) 916184276Ssam{ 917184276Ssam struct ieee80211vap *vap = ni->ni_vap; 918184276Ssam struct ieee80211com *ic = ni->ni_ic; 919184276Ssam 920184276Ssam /* 921184276Ssam * Process management frames when scanning; useful for doing 922184276Ssam * a site-survey. 923184276Ssam */ 924184276Ssam if (ic->ic_flags & IEEE80211_F_SCAN) 925184276Ssam adhoc_recv_mgmt(ni, m0, subtype, rssi, noise, rstamp); 926184276Ssam else 927184276Ssam vap->iv_stats.is_rx_mgtdiscard++; 928184276Ssam} 929191546Ssam 930191546Ssamstatic void 931191546Ssamadhoc_recv_ctl(struct ieee80211_node *ni, struct mbuf *m0, int subtype) 932191546Ssam{ 933191546Ssam} 934