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: stable/11/sys/net80211/ieee80211_adhoc.c 344969 2019-03-09 12:54:10Z avos $"); 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> 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_adhoc.h> 60178354Ssam#include <net80211/ieee80211_input.h> 61190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 62190391Ssam#include <net80211/ieee80211_superg.h> 63190391Ssam#endif 64186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 65186904Ssam#include <net80211/ieee80211_tdma.h> 66186904Ssam#endif 67244060Sadrian#include <net80211/ieee80211_sta.h> 68178354Ssam 69178354Ssam#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) 70178354Ssam 71178354Ssamstatic void adhoc_vattach(struct ieee80211vap *); 72178354Ssamstatic int adhoc_newstate(struct ieee80211vap *, enum ieee80211_state, int); 73283535Sadrianstatic int adhoc_input(struct ieee80211_node *, struct mbuf *, 74283535Sadrian const struct ieee80211_rx_stats *, int, int); 75178354Ssamstatic void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *, 76283535Sadrian int subtype, const struct ieee80211_rx_stats *, int, int); 77184276Ssamstatic void ahdemo_recv_mgmt(struct ieee80211_node *, struct mbuf *, 78283535Sadrian int subtype, const struct ieee80211_rx_stats *rxs, int, int); 79191546Ssamstatic void adhoc_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype); 80178354Ssam 81178354Ssamvoid 82178354Ssamieee80211_adhoc_attach(struct ieee80211com *ic) 83178354Ssam{ 84178354Ssam ic->ic_vattach[IEEE80211_M_IBSS] = adhoc_vattach; 85178354Ssam ic->ic_vattach[IEEE80211_M_AHDEMO] = adhoc_vattach; 86178354Ssam} 87178354Ssam 88178354Ssamvoid 89178354Ssamieee80211_adhoc_detach(struct ieee80211com *ic) 90178354Ssam{ 91178354Ssam} 92178354Ssam 93178354Ssamstatic void 94178354Ssamadhoc_vdetach(struct ieee80211vap *vap) 95178354Ssam{ 96178354Ssam} 97178354Ssam 98178354Ssamstatic void 99178354Ssamadhoc_vattach(struct ieee80211vap *vap) 100178354Ssam{ 101178354Ssam vap->iv_newstate = adhoc_newstate; 102178354Ssam vap->iv_input = adhoc_input; 103184276Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) 104184276Ssam vap->iv_recv_mgmt = adhoc_recv_mgmt; 105184276Ssam else 106184276Ssam vap->iv_recv_mgmt = ahdemo_recv_mgmt; 107191546Ssam vap->iv_recv_ctl = adhoc_recv_ctl; 108178354Ssam vap->iv_opdetach = adhoc_vdetach; 109186904Ssam#ifdef IEEE80211_SUPPORT_TDMA 110186904Ssam /* 111186904Ssam * Throw control to tdma support. Note we do this 112186904Ssam * after setting up our callbacks so it can piggyback 113186904Ssam * on top of us. 114186904Ssam */ 115186904Ssam if (vap->iv_caps & IEEE80211_C_TDMA) 116186904Ssam ieee80211_tdma_vattach(vap); 117186904Ssam#endif 118178354Ssam} 119178354Ssam 120188466Ssamstatic void 121188466Ssamsta_leave(void *arg, struct ieee80211_node *ni) 122188466Ssam{ 123188466Ssam struct ieee80211vap *vap = arg; 124188466Ssam 125188466Ssam if (ni->ni_vap == vap && ni != vap->iv_bss) 126188466Ssam ieee80211_node_leave(ni); 127188466Ssam} 128188466Ssam 129178354Ssam/* 130178354Ssam * IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler. 131178354Ssam */ 132178354Ssamstatic int 133178354Ssamadhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 134178354Ssam{ 135178354Ssam struct ieee80211com *ic = vap->iv_ic; 136178354Ssam struct ieee80211_node *ni; 137178354Ssam enum ieee80211_state ostate; 138178354Ssam 139178354Ssam IEEE80211_LOCK_ASSERT(vap->iv_ic); 140178354Ssam 141178354Ssam ostate = vap->iv_state; 142178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", 143178354Ssam __func__, ieee80211_state_name[ostate], 144178354Ssam ieee80211_state_name[nstate], arg); 145178354Ssam vap->iv_state = nstate; /* state transition */ 146178354Ssam if (ostate != IEEE80211_S_SCAN) 147178354Ssam ieee80211_cancel_scan(vap); /* background scan */ 148178354Ssam ni = vap->iv_bss; /* NB: no reference held */ 149178354Ssam switch (nstate) { 150178354Ssam case IEEE80211_S_INIT: 151178354Ssam switch (ostate) { 152178354Ssam case IEEE80211_S_SCAN: 153178354Ssam ieee80211_cancel_scan(vap); 154178354Ssam break; 155178354Ssam default: 156178354Ssam break; 157178354Ssam } 158178354Ssam if (ostate != IEEE80211_S_INIT) { 159178354Ssam /* NB: optimize INIT -> INIT case */ 160178354Ssam ieee80211_reset_bss(vap); 161178354Ssam } 162178354Ssam break; 163178354Ssam case IEEE80211_S_SCAN: 164178354Ssam switch (ostate) { 165188466Ssam case IEEE80211_S_RUN: /* beacon miss */ 166188466Ssam /* purge station table; entries are stale */ 167188466Ssam ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap); 168188466Ssam /* fall thru... */ 169178354Ssam case IEEE80211_S_INIT: 170178354Ssam if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && 171178354Ssam !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { 172178354Ssam /* 173178354Ssam * Already have a channel; bypass the 174178354Ssam * scan and startup immediately. 175178354Ssam */ 176244078Sadrian ieee80211_create_ibss(vap, 177244078Sadrian ieee80211_ht_adjust_channel(ic, 178244078Sadrian vap->iv_des_chan, vap->iv_flags_ht)); 179178354Ssam break; 180178354Ssam } 181178354Ssam /* 182178354Ssam * Initiate a scan. We can come here as a result 183178354Ssam * of an IEEE80211_IOC_SCAN_REQ too in which case 184178354Ssam * the vap will be marked with IEEE80211_FEXT_SCANREQ 185178354Ssam * and the scan request parameters will be present 186178354Ssam * in iv_scanreq. Otherwise we do the default. 187178354Ssam */ 188178354Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { 189178354Ssam ieee80211_check_scan(vap, 190178354Ssam vap->iv_scanreq_flags, 191178354Ssam vap->iv_scanreq_duration, 192178354Ssam vap->iv_scanreq_mindwell, 193178354Ssam vap->iv_scanreq_maxdwell, 194178354Ssam vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); 195178354Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; 196178354Ssam } else 197178354Ssam ieee80211_check_scan_current(vap); 198178354Ssam break; 199178354Ssam case IEEE80211_S_SCAN: 200178354Ssam /* 201178354Ssam * This can happen because of a change in state 202178354Ssam * that requires a reset. Trigger a new scan 203178354Ssam * unless we're in manual roaming mode in which 204178354Ssam * case an application must issue an explicit request. 205178354Ssam */ 206178354Ssam if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) 207178354Ssam ieee80211_check_scan_current(vap); 208178354Ssam break; 209178354Ssam default: 210178354Ssam goto invalid; 211178354Ssam } 212178354Ssam break; 213178354Ssam case IEEE80211_S_RUN: 214178354Ssam if (vap->iv_flags & IEEE80211_F_WPA) { 215178354Ssam /* XXX validate prerequisites */ 216178354Ssam } 217178354Ssam switch (ostate) { 218178354Ssam case IEEE80211_S_SCAN: 219178354Ssam#ifdef IEEE80211_DEBUG 220178354Ssam if (ieee80211_msg_debug(vap)) { 221178354Ssam ieee80211_note(vap, 222178354Ssam "synchronized with %s ssid ", 223178354Ssam ether_sprintf(ni->ni_bssid)); 224178354Ssam ieee80211_print_essid(vap->iv_bss->ni_essid, 225178354Ssam ni->ni_esslen); 226178354Ssam /* XXX MCS/HT */ 227178354Ssam printf(" channel %d start %uMb\n", 228178354Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), 229178354Ssam IEEE80211_RATE2MBS(ni->ni_txrate)); 230178354Ssam } 231178354Ssam#endif 232178354Ssam break; 233282405Sadrian case IEEE80211_S_RUN: /* IBSS merge */ 234282405Sadrian break; 235178354Ssam default: 236178354Ssam goto invalid; 237178354Ssam } 238178354Ssam /* 239178354Ssam * When 802.1x is not in use mark the port authorized 240178354Ssam * at this point so traffic can flow. 241178354Ssam */ 242178354Ssam if (ni->ni_authmode != IEEE80211_AUTH_8021X) 243178354Ssam ieee80211_node_authorize(ni); 244184345Ssam /* 245184345Ssam * Fake association when joining an existing bss. 246184345Ssam */ 247184345Ssam if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, vap->iv_myaddr) && 248184345Ssam ic->ic_newassoc != NULL) 249184345Ssam ic->ic_newassoc(ni, ostate != IEEE80211_S_RUN); 250178354Ssam break; 251178354Ssam case IEEE80211_S_SLEEP: 252241138Sadrian vap->iv_sta_ps(vap, 0); 253178354Ssam break; 254178354Ssam default: 255178354Ssam invalid: 256184268Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 257184268Ssam "%s: unexpected state transition %s -> %s\n", __func__, 258178354Ssam ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 259178354Ssam break; 260178354Ssam } 261178354Ssam return 0; 262178354Ssam} 263178354Ssam 264178354Ssam/* 265178354Ssam * Decide if a received management frame should be 266178354Ssam * printed when debugging is enabled. This filters some 267178354Ssam * of the less interesting frames that come frequently 268178354Ssam * (e.g. beacons). 269178354Ssam */ 270178354Ssamstatic __inline int 271178354Ssamdoprint(struct ieee80211vap *vap, int subtype) 272178354Ssam{ 273178354Ssam switch (subtype) { 274178354Ssam case IEEE80211_FC0_SUBTYPE_BEACON: 275178354Ssam return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); 276178354Ssam case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 277178354Ssam return 1; 278178354Ssam } 279178354Ssam return 1; 280178354Ssam} 281178354Ssam 282178354Ssam/* 283178354Ssam * Process a received frame. The node associated with the sender 284178354Ssam * should be supplied. If nothing was found in the node table then 285178354Ssam * the caller is assumed to supply a reference to iv_bss instead. 286178354Ssam * The RSSI and a timestamp are also supplied. The RSSI data is used 287178354Ssam * during AP scanning to select a AP to associate with; it can have 288178354Ssam * any units so long as values have consistent units and higher values 289178354Ssam * mean ``better signal''. The receive timestamp is currently not used 290178354Ssam * by the 802.11 layer. 291178354Ssam */ 292178354Ssamstatic int 293283535Sadrianadhoc_input(struct ieee80211_node *ni, struct mbuf *m, 294283535Sadrian const struct ieee80211_rx_stats *rxs, int rssi, int nf) 295178354Ssam{ 296178354Ssam struct ieee80211vap *vap = ni->ni_vap; 297178354Ssam struct ieee80211com *ic = ni->ni_ic; 298178354Ssam struct ifnet *ifp = vap->iv_ifp; 299178354Ssam struct ieee80211_frame *wh; 300178354Ssam struct ieee80211_key *key; 301178354Ssam struct ether_header *eh; 302203422Srpaulo int hdrspace, need_tap = 1; /* mbuf need to be tapped. */ 303178354Ssam uint8_t dir, type, subtype, qos; 304178354Ssam uint8_t *bssid; 305178354Ssam 306183247Ssam if (m->m_flags & M_AMPDU_MPDU) { 307178354Ssam /* 308178354Ssam * Fastpath for A-MPDU reorder q resubmission. Frames 309183247Ssam * w/ M_AMPDU_MPDU marked have already passed through 310183247Ssam * here but were received out of order and been held on 311183247Ssam * the reorder queue. When resubmitted they are marked 312183247Ssam * with the M_AMPDU_MPDU flag and we can bypass most of 313183247Ssam * the normal processing. 314178354Ssam */ 315178354Ssam wh = mtod(m, struct ieee80211_frame *); 316178354Ssam type = IEEE80211_FC0_TYPE_DATA; 317178354Ssam dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 318178354Ssam subtype = IEEE80211_FC0_SUBTYPE_QOS; 319178354Ssam hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */ 320178354Ssam goto resubmit_ampdu; 321178354Ssam } 322178354Ssam 323178354Ssam KASSERT(ni != NULL, ("null node")); 324178354Ssam ni->ni_inact = ni->ni_inact_reload; 325178354Ssam 326178354Ssam type = -1; /* undefined */ 327178354Ssam 328178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { 329178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 330178354Ssam ni->ni_macaddr, NULL, 331178354Ssam "too short (1): len %u", m->m_pkthdr.len); 332178354Ssam vap->iv_stats.is_rx_tooshort++; 333178354Ssam goto out; 334178354Ssam } 335178354Ssam /* 336178354Ssam * Bit of a cheat here, we use a pointer for a 3-address 337178354Ssam * frame format but don't reference fields past outside 338178354Ssam * ieee80211_frame_min w/o first validating the data is 339178354Ssam * present. 340178354Ssam */ 341178354Ssam wh = mtod(m, struct ieee80211_frame *); 342178354Ssam 343178354Ssam if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 344178354Ssam IEEE80211_FC0_VERSION_0) { 345178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 346191547Ssam ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x", 347191547Ssam wh->i_fc[0], wh->i_fc[1]); 348178354Ssam vap->iv_stats.is_rx_badversion++; 349178354Ssam goto err; 350178354Ssam } 351178354Ssam 352178354Ssam dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; 353178354Ssam type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 354178354Ssam subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 355178354Ssam if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 356178354Ssam if (dir != IEEE80211_FC1_DIR_NODS) 357178354Ssam bssid = wh->i_addr1; 358178354Ssam else if (type == IEEE80211_FC0_TYPE_CTL) 359178354Ssam bssid = wh->i_addr1; 360178354Ssam else { 361178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 362178354Ssam IEEE80211_DISCARD_MAC(vap, 363178354Ssam IEEE80211_MSG_ANY, ni->ni_macaddr, 364178354Ssam NULL, "too short (2): len %u", 365178354Ssam m->m_pkthdr.len); 366178354Ssam vap->iv_stats.is_rx_tooshort++; 367178354Ssam goto out; 368178354Ssam } 369178354Ssam bssid = wh->i_addr3; 370178354Ssam } 371178354Ssam /* 372178354Ssam * Validate the bssid. 373178354Ssam */ 374302202Sadrian if (!(type == IEEE80211_FC0_TYPE_MGT && 375302202Sadrian (subtype == IEEE80211_FC0_SUBTYPE_BEACON || 376302202Sadrian subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) && 377302202Sadrian !IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && 378178354Ssam !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { 379178354Ssam /* not interested in */ 380178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 381178354Ssam bssid, NULL, "%s", "not to bss"); 382178354Ssam vap->iv_stats.is_rx_wrongbss++; 383178354Ssam goto out; 384178354Ssam } 385178354Ssam /* 386178354Ssam * Data frame, cons up a node when it doesn't 387178354Ssam * exist. This should probably done after an ACL check. 388178354Ssam */ 389178354Ssam if (type == IEEE80211_FC0_TYPE_DATA && 390178354Ssam ni == vap->iv_bss && 391178354Ssam !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 392178354Ssam /* 393186904Ssam * Beware of frames that come in too early; we 394186904Ssam * can receive broadcast frames and creating sta 395186904Ssam * entries will blow up because there is no bss 396186904Ssam * channel yet. 397186904Ssam */ 398186904Ssam if (vap->iv_state != IEEE80211_S_RUN) { 399186904Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 400186904Ssam wh, "data", "not in RUN state (%s)", 401186904Ssam ieee80211_state_name[vap->iv_state]); 402186904Ssam vap->iv_stats.is_rx_badstate++; 403186904Ssam goto err; 404186904Ssam } 405186904Ssam /* 406178354Ssam * Fake up a node for this newly 407178354Ssam * discovered member of the IBSS. 408178354Ssam */ 409178354Ssam ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2); 410178354Ssam if (ni == NULL) { 411178354Ssam /* NB: stat kept for alloc failure */ 412178354Ssam goto err; 413178354Ssam } 414178354Ssam } 415178354Ssam IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 416192468Ssam ni->ni_noise = nf; 417282820Sadrian if (IEEE80211_HAS_SEQ(type, subtype) && 418282820Sadrian IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 419178354Ssam uint8_t tid = ieee80211_gettid(wh); 420178354Ssam if (IEEE80211_QOS_HAS_SEQ(wh) && 421178354Ssam TID_TO_WME_AC(tid) >= WME_AC_VI) 422178354Ssam ic->ic_wme.wme_hipri_traffic++; 423296254Savos if (! ieee80211_check_rxseq(ni, wh, bssid)) 424178354Ssam goto out; 425178354Ssam } 426178354Ssam } 427178354Ssam 428178354Ssam switch (type) { 429178354Ssam case IEEE80211_FC0_TYPE_DATA: 430178354Ssam hdrspace = ieee80211_hdrspace(ic, wh); 431178354Ssam if (m->m_len < hdrspace && 432178354Ssam (m = m_pullup(m, hdrspace)) == NULL) { 433178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 434178354Ssam ni->ni_macaddr, NULL, 435178354Ssam "data too short: expecting %u", hdrspace); 436178354Ssam vap->iv_stats.is_rx_tooshort++; 437178354Ssam goto out; /* XXX */ 438178354Ssam } 439178354Ssam if (dir != IEEE80211_FC1_DIR_NODS) { 440178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 441178354Ssam wh, "data", "incorrect dir 0x%x", dir); 442178354Ssam vap->iv_stats.is_rx_wrongdir++; 443178354Ssam goto out; 444178354Ssam } 445178354Ssam /* XXX no power-save support */ 446178354Ssam 447178354Ssam /* 448183247Ssam * Handle A-MPDU re-ordering. If the frame is to be 449183247Ssam * processed directly then ieee80211_ampdu_reorder 450178354Ssam * will return 0; otherwise it has consumed the mbuf 451178354Ssam * and we should do nothing more with it. 452178354Ssam */ 453183247Ssam if ((m->m_flags & M_AMPDU) && 454178354Ssam ieee80211_ampdu_reorder(ni, m) != 0) { 455178354Ssam m = NULL; 456178354Ssam goto out; 457178354Ssam } 458178354Ssam resubmit_ampdu: 459178354Ssam 460178354Ssam /* 461178354Ssam * Handle privacy requirements. Note that we 462178354Ssam * must not be preempted from here until after 463178354Ssam * we (potentially) call ieee80211_crypto_demic; 464178354Ssam * otherwise we may violate assumptions in the 465178354Ssam * crypto cipher modules used to do delayed update 466178354Ssam * of replay sequence numbers. 467178354Ssam */ 468260444Skevlo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 469178354Ssam if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { 470178354Ssam /* 471178354Ssam * Discard encrypted frames when privacy is off. 472178354Ssam */ 473178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 474178354Ssam wh, "WEP", "%s", "PRIVACY off"); 475178354Ssam vap->iv_stats.is_rx_noprivacy++; 476178354Ssam IEEE80211_NODE_STAT(ni, rx_noprivacy); 477178354Ssam goto out; 478178354Ssam } 479178354Ssam key = ieee80211_crypto_decap(ni, m, hdrspace); 480178354Ssam if (key == NULL) { 481178354Ssam /* NB: stats+msgs handled in crypto_decap */ 482178354Ssam IEEE80211_NODE_STAT(ni, rx_wepfail); 483178354Ssam goto out; 484178354Ssam } 485178354Ssam wh = mtod(m, struct ieee80211_frame *); 486260444Skevlo wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; 487178354Ssam } else { 488178354Ssam /* XXX M_WEP and IEEE80211_F_PRIVACY */ 489178354Ssam key = NULL; 490178354Ssam } 491178354Ssam 492178354Ssam /* 493178354Ssam * Save QoS bits for use below--before we strip the header. 494178354Ssam */ 495344969Savos if (subtype == IEEE80211_FC0_SUBTYPE_QOS) 496344969Savos qos = ieee80211_getqos(wh)[0]; 497344969Savos else 498178354Ssam qos = 0; 499178354Ssam 500178354Ssam /* 501178354Ssam * Next up, any fragmentation. 502178354Ssam */ 503178354Ssam if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 504178354Ssam m = ieee80211_defrag(ni, m, hdrspace); 505178354Ssam if (m == NULL) { 506178354Ssam /* Fragment dropped or frame not complete yet */ 507178354Ssam goto out; 508178354Ssam } 509178354Ssam } 510178354Ssam wh = NULL; /* no longer valid, catch any uses */ 511178354Ssam 512178354Ssam /* 513178354Ssam * Next strip any MSDU crypto bits. 514178354Ssam */ 515178354Ssam if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) { 516178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 517178354Ssam ni->ni_macaddr, "data", "%s", "demic error"); 518178354Ssam vap->iv_stats.is_rx_demicfail++; 519178354Ssam IEEE80211_NODE_STAT(ni, rx_demicfail); 520178354Ssam goto out; 521178354Ssam } 522178354Ssam 523178354Ssam /* copy to listener after decrypt */ 524192468Ssam if (ieee80211_radiotap_active_vap(vap)) 525192468Ssam ieee80211_radiotap_rx(vap, m); 526178354Ssam need_tap = 0; 527178354Ssam 528178354Ssam /* 529178354Ssam * Finally, strip the 802.11 header. 530178354Ssam */ 531178354Ssam m = ieee80211_decap(vap, m, hdrspace); 532178354Ssam if (m == NULL) { 533178354Ssam /* XXX mask bit to check for both */ 534178354Ssam /* don't count Null data frames as errors */ 535178354Ssam if (subtype == IEEE80211_FC0_SUBTYPE_NODATA || 536178354Ssam subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) 537178354Ssam goto out; 538178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 539178354Ssam ni->ni_macaddr, "data", "%s", "decap error"); 540178354Ssam vap->iv_stats.is_rx_decap++; 541178354Ssam IEEE80211_NODE_STAT(ni, rx_decap); 542178354Ssam goto err; 543178354Ssam } 544178354Ssam eh = mtod(m, struct ether_header *); 545178354Ssam if (!ieee80211_node_is_authorized(ni)) { 546178354Ssam /* 547178354Ssam * Deny any non-PAE frames received prior to 548178354Ssam * authorization. For open/shared-key 549178354Ssam * authentication the port is mark authorized 550178354Ssam * after authentication completes. For 802.1x 551178354Ssam * the port is not marked authorized by the 552178354Ssam * authenticator until the handshake has completed. 553178354Ssam */ 554178354Ssam if (eh->ether_type != htons(ETHERTYPE_PAE)) { 555178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, 556178354Ssam eh->ether_shost, "data", 557178354Ssam "unauthorized port: ether type 0x%x len %u", 558178354Ssam eh->ether_type, m->m_pkthdr.len); 559178354Ssam vap->iv_stats.is_rx_unauth++; 560178354Ssam IEEE80211_NODE_STAT(ni, rx_unauth); 561178354Ssam goto err; 562178354Ssam } 563178354Ssam } else { 564178354Ssam /* 565178354Ssam * When denying unencrypted frames, discard 566178354Ssam * any non-PAE frames received without encryption. 567178354Ssam */ 568178354Ssam if ((vap->iv_flags & IEEE80211_F_DROPUNENC) && 569178354Ssam (key == NULL && (m->m_flags & M_WEP) == 0) && 570178354Ssam eh->ether_type != htons(ETHERTYPE_PAE)) { 571178354Ssam /* 572178354Ssam * Drop unencrypted frames. 573178354Ssam */ 574178354Ssam vap->iv_stats.is_rx_unencrypted++; 575178354Ssam IEEE80211_NODE_STAT(ni, rx_unencrypted); 576178354Ssam goto out; 577178354Ssam } 578178354Ssam } 579178354Ssam /* XXX require HT? */ 580178354Ssam if (qos & IEEE80211_QOS_AMSDU) { 581178354Ssam m = ieee80211_decap_amsdu(ni, m); 582178354Ssam if (m == NULL) 583178354Ssam return IEEE80211_FC0_TYPE_DATA; 584190391Ssam } else { 585190391Ssam#ifdef IEEE80211_SUPPORT_SUPERG 586190391Ssam m = ieee80211_decap_fastframe(vap, ni, m); 587190391Ssam if (m == NULL) 588178354Ssam return IEEE80211_FC0_TYPE_DATA; 589190391Ssam#endif 590178354Ssam } 591178354Ssam if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL) 592178354Ssam ieee80211_deliver_data(ni->ni_wdsvap, ni, m); 593178354Ssam else 594178354Ssam ieee80211_deliver_data(vap, ni, m); 595178354Ssam return IEEE80211_FC0_TYPE_DATA; 596178354Ssam 597178354Ssam case IEEE80211_FC0_TYPE_MGT: 598178354Ssam vap->iv_stats.is_rx_mgmt++; 599178354Ssam IEEE80211_NODE_STAT(ni, rx_mgmt); 600178354Ssam if (dir != IEEE80211_FC1_DIR_NODS) { 601178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 602178354Ssam wh, "data", "incorrect dir 0x%x", dir); 603178354Ssam vap->iv_stats.is_rx_wrongdir++; 604178354Ssam goto err; 605178354Ssam } 606178354Ssam if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { 607178354Ssam IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, 608178354Ssam ni->ni_macaddr, "mgt", "too short: len %u", 609178354Ssam m->m_pkthdr.len); 610178354Ssam vap->iv_stats.is_rx_tooshort++; 611178354Ssam goto out; 612178354Ssam } 613178354Ssam#ifdef IEEE80211_DEBUG 614178354Ssam if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || 615178354Ssam ieee80211_msg_dumppkts(vap)) { 616178354Ssam if_printf(ifp, "received %s from %s rssi %d\n", 617298376Savos ieee80211_mgt_subtype_name(subtype), 618178354Ssam ether_sprintf(wh->i_addr2), rssi); 619178354Ssam } 620178354Ssam#endif 621260444Skevlo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 622178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 623178354Ssam wh, NULL, "%s", "WEP set but not permitted"); 624178354Ssam vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ 625178354Ssam goto out; 626178354Ssam } 627283535Sadrian vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); 628191534Ssam goto out; 629178354Ssam 630178354Ssam case IEEE80211_FC0_TYPE_CTL: 631178354Ssam vap->iv_stats.is_rx_ctl++; 632178354Ssam IEEE80211_NODE_STAT(ni, rx_ctrl); 633191546Ssam vap->iv_recv_ctl(ni, m, subtype); 634178354Ssam goto out; 635191754Ssam 636178354Ssam default: 637178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 638178354Ssam wh, "bad", "frame type 0x%x", type); 639178354Ssam /* should not come here */ 640178354Ssam break; 641178354Ssam } 642178354Ssamerr: 643271861Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 644178354Ssamout: 645178354Ssam if (m != NULL) { 646192765Ssam if (need_tap && ieee80211_radiotap_active_vap(vap)) 647192468Ssam ieee80211_radiotap_rx(vap, m); 648178354Ssam m_freem(m); 649178354Ssam } 650178354Ssam return type; 651178354Ssam} 652178354Ssam 653178354Ssamstatic int 654178354Ssamis11bclient(const uint8_t *rates, const uint8_t *xrates) 655178354Ssam{ 656178354Ssam static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11); 657178354Ssam int i; 658178354Ssam 659178354Ssam /* NB: the 11b clients we care about will not have xrates */ 660178354Ssam if (xrates != NULL || rates == NULL) 661178354Ssam return 0; 662178354Ssam for (i = 0; i < rates[1]; i++) { 663178354Ssam int r = rates[2+i] & IEEE80211_RATE_VAL; 664178354Ssam if (r > 2*11 || ((1<<r) & brates) == 0) 665178354Ssam return 0; 666178354Ssam } 667178354Ssam return 1; 668178354Ssam} 669178354Ssam 670178354Ssamstatic void 671178354Ssamadhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 672283535Sadrian int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) 673178354Ssam{ 674178354Ssam struct ieee80211vap *vap = ni->ni_vap; 675178354Ssam struct ieee80211com *ic = ni->ni_ic; 676283535Sadrian struct ieee80211_channel *rxchan = ic->ic_curchan; 677178354Ssam struct ieee80211_frame *wh; 678299575Savos uint8_t *frm, *efrm; 679178354Ssam uint8_t *ssid, *rates, *xrates; 680246930Sadrian#if 0 681245928Sadrian int ht_state_change = 0; 682246930Sadrian#endif 683178354Ssam 684178354Ssam wh = mtod(m0, struct ieee80211_frame *); 685178354Ssam frm = (uint8_t *)&wh[1]; 686178354Ssam efrm = mtod(m0, uint8_t *) + m0->m_len; 687178354Ssam switch (subtype) { 688178354Ssam case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 689178354Ssam case IEEE80211_FC0_SUBTYPE_BEACON: { 690178354Ssam struct ieee80211_scanparams scan; 691283535Sadrian struct ieee80211_channel *c; 692178354Ssam /* 693178354Ssam * We process beacon/probe response 694178354Ssam * frames to discover neighbors. 695178354Ssam */ 696283535Sadrian if (rxs != NULL) { 697283535Sadrian c = ieee80211_lookup_channel_rxstatus(vap, rxs); 698283535Sadrian if (c != NULL) 699283535Sadrian rxchan = c; 700283535Sadrian } 701283535Sadrian if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0) 702178354Ssam return; 703178354Ssam /* 704178354Ssam * Count frame now that we know it's to be processed. 705178354Ssam */ 706178354Ssam if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { 707178354Ssam vap->iv_stats.is_rx_beacon++; /* XXX remove */ 708178354Ssam IEEE80211_NODE_STAT(ni, rx_beacons); 709178354Ssam } else 710178354Ssam IEEE80211_NODE_STAT(ni, rx_proberesp); 711178354Ssam /* 712178354Ssam * If scanning, just pass information to the scan module. 713178354Ssam */ 714178354Ssam if (ic->ic_flags & IEEE80211_F_SCAN) { 715178354Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { 716178354Ssam /* 717178354Ssam * Actively scanning a channel marked passive; 718178354Ssam * send a probe request now that we know there 719178354Ssam * is 802.11 traffic present. 720178354Ssam * 721178354Ssam * XXX check if the beacon we recv'd gives 722178354Ssam * us what we need and suppress the probe req 723178354Ssam */ 724178354Ssam ieee80211_probe_curchan(vap, 1); 725178354Ssam ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; 726178354Ssam } 727283535Sadrian ieee80211_add_scan(vap, rxchan, &scan, wh, 728282742Sadrian subtype, rssi, nf); 729178354Ssam return; 730178354Ssam } 731178354Ssam if (scan.capinfo & IEEE80211_CAPINFO_IBSS) { 732178354Ssam if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 733178354Ssam /* 734178354Ssam * Create a new entry in the neighbor table. 735330458Seadler * 736330458Seadler * XXX TODO: 737330458Seadler * 738330458Seadler * Here we're not scanning; so if we have an 739330458Seadler * SSID then make sure it matches our SSID. 740330458Seadler * Otherwise this code will match on all IBSS 741330458Seadler * beacons/probe requests for all SSIDs, 742330458Seadler * filling the node table with nodes that 743330458Seadler * aren't ours. 744178354Ssam */ 745330458Seadler if (ieee80211_ibss_node_check_new(ni, &scan)) 746330458Seadler ni = ieee80211_add_neighbor(vap, wh, &scan); 747330458Seadler else 748330458Seadler ni = NULL; 749178354Ssam } else if (ni->ni_capinfo == 0) { 750178354Ssam /* 751178354Ssam * Update faked node created on transmit. 752178354Ssam * Note this also updates the tsf. 753178354Ssam */ 754178354Ssam ieee80211_init_neighbor(ni, wh, &scan); 755178354Ssam } else { 756178354Ssam /* 757178354Ssam * Record tsf for potential resync. 758178354Ssam */ 759178354Ssam memcpy(ni->ni_tstamp.data, scan.tstamp, 760178354Ssam sizeof(ni->ni_tstamp)); 761178354Ssam } 762245928Sadrian /* 763245928Sadrian * This isn't enabled yet - otherwise it would 764245928Sadrian * update the HT parameters and channel width 765245928Sadrian * from any node, which could lead to lots of 766245928Sadrian * strange behaviour if the 11n nodes aren't 767245928Sadrian * exactly configured to match. 768245928Sadrian */ 769245928Sadrian#if 0 770245928Sadrian if (scan.htcap != NULL && scan.htinfo != NULL && 771245928Sadrian (vap->iv_flags_ht & IEEE80211_FHT_HT)) { 772245928Sadrian if (ieee80211_ht_updateparams(ni, 773245928Sadrian scan.htcap, scan.htinfo)) 774245928Sadrian ht_state_change = 1; 775245928Sadrian } 776245928Sadrian#endif 777178354Ssam if (ni != NULL) { 778178354Ssam IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); 779192468Ssam ni->ni_noise = nf; 780178354Ssam } 781246927Sadrian /* 782246927Sadrian * Same here - the channel width change should 783246927Sadrian * be applied to the specific peer node, not 784246927Sadrian * to the ic. Ie, the interface configuration 785246927Sadrian * should stay in its current channel width; 786246927Sadrian * but it should change the rate control and 787246927Sadrian * any queued frames for the given node only. 788246927Sadrian * 789246927Sadrian * Since there's no (current) way to inform 790246927Sadrian * the driver that a channel width change has 791298995Spfg * occurred for a single node, just stub this 792246927Sadrian * out. 793246927Sadrian */ 794246927Sadrian#if 0 795245928Sadrian if (ht_state_change) 796245928Sadrian ieee80211_update_chw(ic); 797246927Sadrian#endif 798178354Ssam } 799178354Ssam break; 800178354Ssam } 801178354Ssam 802178354Ssam case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 803178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 804184268Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 805184268Ssam wh, NULL, "wrong state %s", 806184268Ssam ieee80211_state_name[vap->iv_state]); 807178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 808178354Ssam return; 809178354Ssam } 810178354Ssam if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { 811178354Ssam /* frame must be directed */ 812184268Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 813184268Ssam wh, NULL, "%s", "not unicast"); 814178354Ssam vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ 815178354Ssam return; 816178354Ssam } 817178354Ssam 818178354Ssam /* 819178354Ssam * prreq frame format 820178354Ssam * [tlv] ssid 821178354Ssam * [tlv] supported rates 822178354Ssam * [tlv] extended supported rates 823178354Ssam */ 824178354Ssam ssid = rates = xrates = NULL; 825178354Ssam while (efrm - frm > 1) { 826178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 827178354Ssam switch (*frm) { 828178354Ssam case IEEE80211_ELEMID_SSID: 829178354Ssam ssid = frm; 830178354Ssam break; 831178354Ssam case IEEE80211_ELEMID_RATES: 832178354Ssam rates = frm; 833178354Ssam break; 834178354Ssam case IEEE80211_ELEMID_XRATES: 835178354Ssam xrates = frm; 836178354Ssam break; 837178354Ssam } 838178354Ssam frm += frm[1] + 2; 839178354Ssam } 840178354Ssam IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); 841178354Ssam if (xrates != NULL) 842178354Ssam IEEE80211_VERIFY_ELEMENT(xrates, 843178354Ssam IEEE80211_RATE_MAXSIZE - rates[1], return); 844178354Ssam IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); 845178354Ssam IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); 846178354Ssam if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) { 847178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 848178354Ssam wh, NULL, 849178354Ssam "%s", "no ssid with ssid suppression enabled"); 850178354Ssam vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/ 851178354Ssam return; 852178354Ssam } 853178354Ssam 854178354Ssam /* XXX find a better class or define it's own */ 855178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, 856178354Ssam "%s", "recv probe req"); 857178354Ssam /* 858178354Ssam * Some legacy 11b clients cannot hack a complete 859178354Ssam * probe response frame. When the request includes 860178354Ssam * only a bare-bones rate set, communicate this to 861178354Ssam * the transmit side. 862178354Ssam */ 863178354Ssam ieee80211_send_proberesp(vap, wh->i_addr2, 864178354Ssam is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0); 865178354Ssam break; 866178354Ssam 867218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ACTION: 868218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: 869298757Sadrian if ((ni == vap->iv_bss) && 870298757Sadrian !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { 871184268Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 872218958Sbschmidt wh, NULL, "%s", "unknown node"); 873218958Sbschmidt vap->iv_stats.is_rx_mgtdiscard++; 874218958Sbschmidt } else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && 875218958Sbschmidt !IEEE80211_IS_MULTICAST(wh->i_addr1)) { 876218958Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 877218958Sbschmidt wh, NULL, "%s", "not for us"); 878218958Sbschmidt vap->iv_stats.is_rx_mgtdiscard++; 879218958Sbschmidt } else if (vap->iv_state != IEEE80211_S_RUN) { 880218958Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 881184268Ssam wh, NULL, "wrong state %s", 882184268Ssam ieee80211_state_name[vap->iv_state]); 883178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 884218958Sbschmidt } else { 885218958Sbschmidt if (ieee80211_parse_action(ni, m0) == 0) 886218958Sbschmidt (void)ic->ic_recv_action(ni, wh, frm, efrm); 887178354Ssam } 888178354Ssam break; 889178354Ssam 890178354Ssam case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 891218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 892178354Ssam case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 893178354Ssam case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 894295795Savos case IEEE80211_FC0_SUBTYPE_TIMING_ADV: 895218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ATIM: 896218927Sbschmidt case IEEE80211_FC0_SUBTYPE_DISASSOC: 897218927Sbschmidt case IEEE80211_FC0_SUBTYPE_AUTH: 898178354Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 899184268Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 900218927Sbschmidt wh, NULL, "%s", "not handled"); 901178354Ssam vap->iv_stats.is_rx_mgtdiscard++; 902218927Sbschmidt break; 903178354Ssam 904178354Ssam default: 905178354Ssam IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 906218927Sbschmidt wh, "mgt", "subtype 0x%x not handled", subtype); 907178354Ssam vap->iv_stats.is_rx_badsubtype++; 908178354Ssam break; 909178354Ssam } 910178354Ssam} 911178354Ssam#undef IEEE80211_VERIFY_LENGTH 912178354Ssam#undef IEEE80211_VERIFY_ELEMENT 913184276Ssam 914184276Ssamstatic void 915184276Ssamahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 916283535Sadrian int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) 917184276Ssam{ 918184276Ssam struct ieee80211vap *vap = ni->ni_vap; 919184276Ssam struct ieee80211com *ic = ni->ni_ic; 920218927Sbschmidt struct ieee80211_frame *wh; 921184276Ssam 922184276Ssam /* 923184276Ssam * Process management frames when scanning; useful for doing 924184276Ssam * a site-survey. 925184276Ssam */ 926184276Ssam if (ic->ic_flags & IEEE80211_F_SCAN) 927283535Sadrian adhoc_recv_mgmt(ni, m0, subtype, rxs, rssi, nf); 928218927Sbschmidt else { 929218927Sbschmidt wh = mtod(m0, struct ieee80211_frame *); 930218927Sbschmidt switch (subtype) { 931218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 932218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 933218927Sbschmidt case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 934218927Sbschmidt case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 935218927Sbschmidt case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 936218927Sbschmidt case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 937295795Savos case IEEE80211_FC0_SUBTYPE_TIMING_ADV: 938218927Sbschmidt case IEEE80211_FC0_SUBTYPE_BEACON: 939218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ATIM: 940218927Sbschmidt case IEEE80211_FC0_SUBTYPE_DISASSOC: 941218927Sbschmidt case IEEE80211_FC0_SUBTYPE_AUTH: 942218927Sbschmidt case IEEE80211_FC0_SUBTYPE_DEAUTH: 943218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ACTION: 944218927Sbschmidt case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: 945218927Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, 946218927Sbschmidt wh, NULL, "%s", "not handled"); 947218927Sbschmidt vap->iv_stats.is_rx_mgtdiscard++; 948218927Sbschmidt break; 949218927Sbschmidt default: 950218927Sbschmidt IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, 951218927Sbschmidt wh, "mgt", "subtype 0x%x not handled", subtype); 952218927Sbschmidt vap->iv_stats.is_rx_badsubtype++; 953218927Sbschmidt break; 954218927Sbschmidt } 955218927Sbschmidt } 956184276Ssam} 957191546Ssam 958191546Ssamstatic void 959205277Srpauloadhoc_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) 960191546Ssam{ 961205277Srpaulo 962205277Srpaulo switch (subtype) { 963205277Srpaulo case IEEE80211_FC0_SUBTYPE_BAR: 964205277Srpaulo ieee80211_recv_bar(ni, m); 965205277Srpaulo break; 966205277Srpaulo } 967191546Ssam} 968