132176cfdSRui Paulo /*- 2f186073cSJoerg Sonnenberger * Copyright (c) 2001 Atsushi Onoe 332176cfdSRui Paulo * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 4f186073cSJoerg Sonnenberger * All rights reserved. 5f186073cSJoerg Sonnenberger * 6f186073cSJoerg Sonnenberger * Redistribution and use in source and binary forms, with or without 7f186073cSJoerg Sonnenberger * modification, are permitted provided that the following conditions 8f186073cSJoerg Sonnenberger * are met: 9f186073cSJoerg Sonnenberger * 1. Redistributions of source code must retain the above copyright 10f186073cSJoerg Sonnenberger * notice, this list of conditions and the following disclaimer. 11f186073cSJoerg Sonnenberger * 2. Redistributions in binary form must reproduce the above copyright 12f186073cSJoerg Sonnenberger * notice, this list of conditions and the following disclaimer in the 13f186073cSJoerg Sonnenberger * documentation and/or other materials provided with the distribution. 14f186073cSJoerg Sonnenberger * 15f186073cSJoerg Sonnenberger * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16f186073cSJoerg Sonnenberger * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17f186073cSJoerg Sonnenberger * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18f186073cSJoerg Sonnenberger * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19f186073cSJoerg Sonnenberger * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20f186073cSJoerg Sonnenberger * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21f186073cSJoerg Sonnenberger * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22f186073cSJoerg Sonnenberger * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23f186073cSJoerg Sonnenberger * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24f186073cSJoerg Sonnenberger * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25f186073cSJoerg Sonnenberger */ 26f186073cSJoerg Sonnenberger 27085ff963SMatthew Dillon #include <sys/cdefs.h> 28085ff963SMatthew Dillon __FBSDID("$FreeBSD$"); 29085ff963SMatthew Dillon 30f186073cSJoerg Sonnenberger #include "opt_inet.h" 3132176cfdSRui Paulo #include "opt_inet6.h" 3232176cfdSRui Paulo #include "opt_wlan.h" 33f186073cSJoerg Sonnenberger 34f186073cSJoerg Sonnenberger #include <sys/param.h> 35f186073cSJoerg Sonnenberger #include <sys/systm.h> 36f186073cSJoerg Sonnenberger #include <sys/kernel.h> 374f655ef5SMatthew Dillon #include <sys/malloc.h> 384f655ef5SMatthew Dillon #include <sys/mbuf.h> 39f186073cSJoerg Sonnenberger #include <sys/endian.h> 40f186073cSJoerg Sonnenberger 41841ab66cSSepherosa Ziehau #include <sys/socket.h> 42f186073cSJoerg Sonnenberger 43f186073cSJoerg Sonnenberger #include <net/bpf.h> 44841ab66cSSepherosa Ziehau #include <net/ethernet.h> 45841ab66cSSepherosa Ziehau #include <net/if.h> 46085ff963SMatthew Dillon #include <net/if_var.h> 47841ab66cSSepherosa Ziehau #include <net/if_llc.h> 48841ab66cSSepherosa Ziehau #include <net/if_media.h> 49085ff963SMatthew Dillon #include <net/vlan/if_vlan_var.h> 50085ff963SMatthew Dillon 51085ff963SMatthew Dillon #if defined(__DragonFly__) 5234a60cf6SRui Paulo #include <net/ifq_var.h> 53085ff963SMatthew Dillon #endif 54841ab66cSSepherosa Ziehau 55841ab66cSSepherosa Ziehau #include <netproto/802_11/ieee80211_var.h> 5632176cfdSRui Paulo #include <netproto/802_11/ieee80211_regdomain.h> 5732176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 5832176cfdSRui Paulo #include <netproto/802_11/ieee80211_superg.h> 5932176cfdSRui Paulo #endif 6032176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_TDMA 6132176cfdSRui Paulo #include <netproto/802_11/ieee80211_tdma.h> 6232176cfdSRui Paulo #endif 6332176cfdSRui Paulo #include <netproto/802_11/ieee80211_wds.h> 6432176cfdSRui Paulo #include <netproto/802_11/ieee80211_mesh.h> 65f186073cSJoerg Sonnenberger 66085ff963SMatthew Dillon #if defined(INET) || defined(INET6) 67f186073cSJoerg Sonnenberger #include <netinet/in.h> 68085ff963SMatthew Dillon #endif 69085ff963SMatthew Dillon 70085ff963SMatthew Dillon #ifdef INET 71f186073cSJoerg Sonnenberger #include <netinet/if_ether.h> 72841ab66cSSepherosa Ziehau #include <netinet/in_systm.h> 73841ab66cSSepherosa Ziehau #include <netinet/ip.h> 74f186073cSJoerg Sonnenberger #endif 7532176cfdSRui Paulo #ifdef INET6 7632176cfdSRui Paulo #include <netinet/ip6.h> 7732176cfdSRui Paulo #endif 7832176cfdSRui Paulo 794f655ef5SMatthew Dillon #if defined(__DragonFly__) 804f655ef5SMatthew Dillon #else 814f655ef5SMatthew Dillon #include <security/mac/mac_framework.h> 824f655ef5SMatthew Dillon #endif 83085ff963SMatthew Dillon 8432176cfdSRui Paulo #define ETHER_HEADER_COPY(dst, src) \ 8532176cfdSRui Paulo memcpy(dst, src, sizeof(struct ether_header)) 8632176cfdSRui Paulo 8732176cfdSRui Paulo static int ieee80211_fragment(struct ieee80211vap *, struct mbuf *, 8832176cfdSRui Paulo u_int hdrsize, u_int ciphdrsize, u_int mtu); 8932176cfdSRui Paulo static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); 90f186073cSJoerg Sonnenberger 91841ab66cSSepherosa Ziehau #ifdef IEEE80211_DEBUG 92841ab66cSSepherosa Ziehau /* 93841ab66cSSepherosa Ziehau * Decide if an outbound management frame should be 94841ab66cSSepherosa Ziehau * printed when debugging is enabled. This filters some 95841ab66cSSepherosa Ziehau * of the less interesting frames that come frequently 96841ab66cSSepherosa Ziehau * (e.g. beacons). 97841ab66cSSepherosa Ziehau */ 98841ab66cSSepherosa Ziehau static __inline int 9932176cfdSRui Paulo doprint(struct ieee80211vap *vap, int subtype) 100841ab66cSSepherosa Ziehau { 101841ab66cSSepherosa Ziehau switch (subtype) { 102841ab66cSSepherosa Ziehau case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 10332176cfdSRui Paulo return (vap->iv_opmode == IEEE80211_M_IBSS); 104841ab66cSSepherosa Ziehau } 105841ab66cSSepherosa Ziehau return 1; 106841ab66cSSepherosa Ziehau } 107841ab66cSSepherosa Ziehau #endif 108841ab66cSSepherosa Ziehau 109841ab66cSSepherosa Ziehau /* 110085ff963SMatthew Dillon * Transmit a frame to the given destination on the given VAP. 111085ff963SMatthew Dillon * 112085ff963SMatthew Dillon * It's up to the caller to figure out the details of who this 113085ff963SMatthew Dillon * is going to and resolving the node. 114085ff963SMatthew Dillon * 115085ff963SMatthew Dillon * This routine takes care of queuing it for power save, 116085ff963SMatthew Dillon * A-MPDU state stuff, fast-frames state stuff, encapsulation 117085ff963SMatthew Dillon * if required, then passing it up to the driver layer. 118085ff963SMatthew Dillon * 119085ff963SMatthew Dillon * This routine (for now) consumes the mbuf and frees the node 120085ff963SMatthew Dillon * reference; it ideally will return a TX status which reflects 121085ff963SMatthew Dillon * whether the mbuf was consumed or not, so the caller can 122085ff963SMatthew Dillon * free the mbuf (if appropriate) and the node reference (again, 123085ff963SMatthew Dillon * if appropriate.) 124841ab66cSSepherosa Ziehau */ 125085ff963SMatthew Dillon int 126085ff963SMatthew Dillon ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m, 127085ff963SMatthew Dillon struct ieee80211_node *ni) 12832176cfdSRui Paulo { 12932176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 130085ff963SMatthew Dillon struct ifnet *ifp = vap->iv_ifp; 1314f655ef5SMatthew Dillon int len, mcast; 13232176cfdSRui Paulo 13332176cfdSRui Paulo if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && 13432176cfdSRui Paulo (m->m_flags & M_PWR_SAV) == 0) { 13532176cfdSRui Paulo /* 13632176cfdSRui Paulo * Station in power save mode; pass the frame 13732176cfdSRui Paulo * to the 802.11 layer and continue. We'll get 13832176cfdSRui Paulo * the frame back when the time is right. 13932176cfdSRui Paulo * XXX lose WDS vap linkage? 14032176cfdSRui Paulo */ 1414f898719SImre Vadász if (ieee80211_pwrsave(ni, m) != 0) 1424f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 14332176cfdSRui Paulo ieee80211_free_node(ni); 144085ff963SMatthew Dillon 145085ff963SMatthew Dillon /* 146085ff963SMatthew Dillon * We queued it fine, so tell the upper layer 147085ff963SMatthew Dillon * that we consumed it. 148085ff963SMatthew Dillon */ 149085ff963SMatthew Dillon return (0); 15032176cfdSRui Paulo } 15132176cfdSRui Paulo /* calculate priority so drivers can find the tx queue */ 15232176cfdSRui Paulo if (ieee80211_classify(ni, m)) { 15332176cfdSRui Paulo IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, 154085ff963SMatthew Dillon ni->ni_macaddr, NULL, 15532176cfdSRui Paulo "%s", "classification failure"); 15632176cfdSRui Paulo vap->iv_stats.is_tx_classify++; 1574f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 15832176cfdSRui Paulo m_freem(m); 15932176cfdSRui Paulo ieee80211_free_node(ni); 160085ff963SMatthew Dillon 161085ff963SMatthew Dillon /* XXX better status? */ 162085ff963SMatthew Dillon return (0); 16332176cfdSRui Paulo } 16432176cfdSRui Paulo /* 16532176cfdSRui Paulo * Stash the node pointer. Note that we do this after 16632176cfdSRui Paulo * any call to ieee80211_dwds_mcast because that code 16732176cfdSRui Paulo * uses any existing value for rcvif to identify the 16832176cfdSRui Paulo * interface it (might have been) received on. 16932176cfdSRui Paulo */ 17032176cfdSRui Paulo m->m_pkthdr.rcvif = (void *)ni; 1714f898719SImre Vadász mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1: 0; 1724f898719SImre Vadász len = m->m_pkthdr.len; 17332176cfdSRui Paulo 17432176cfdSRui Paulo BPF_MTAP(ifp, m); /* 802.3 tx */ 17532176cfdSRui Paulo 17632176cfdSRui Paulo /* 17732176cfdSRui Paulo * Check if A-MPDU tx aggregation is setup or if we 17832176cfdSRui Paulo * should try to enable it. The sta must be associated 17932176cfdSRui Paulo * with HT and A-MPDU enabled for use. When the policy 18032176cfdSRui Paulo * routine decides we should enable A-MPDU we issue an 18132176cfdSRui Paulo * ADDBA request and wait for a reply. The frame being 18232176cfdSRui Paulo * encapsulated will go out w/o using A-MPDU, or possibly 18332176cfdSRui Paulo * it might be collected by the driver and held/retransmit. 18432176cfdSRui Paulo * The default ic_ampdu_enable routine handles staggering 18532176cfdSRui Paulo * ADDBA requests in case the receiver NAK's us or we are 18632176cfdSRui Paulo * otherwise unable to establish a BA stream. 18732176cfdSRui Paulo */ 18832176cfdSRui Paulo if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && 1894f655ef5SMatthew Dillon (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX)) { 1904f655ef5SMatthew Dillon if ((m->m_flags & M_EAPOL) == 0) { 191085ff963SMatthew Dillon int tid = WME_AC_TO_TID(M_WME_GETAC(m)); 192085ff963SMatthew Dillon struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid]; 19332176cfdSRui Paulo 19432176cfdSRui Paulo ieee80211_txampdu_count_packet(tap); 19532176cfdSRui Paulo if (IEEE80211_AMPDU_RUNNING(tap)) { 19632176cfdSRui Paulo /* 19732176cfdSRui Paulo * Operational, mark frame for aggregation. 19832176cfdSRui Paulo * 19932176cfdSRui Paulo * XXX do tx aggregation here 20032176cfdSRui Paulo */ 20132176cfdSRui Paulo m->m_flags |= M_AMPDU_MPDU; 20232176cfdSRui Paulo } else if (!IEEE80211_AMPDU_REQUESTED(tap) && 20332176cfdSRui Paulo ic->ic_ampdu_enable(ni, tap)) { 20432176cfdSRui Paulo /* 20532176cfdSRui Paulo * Not negotiated yet, request service. 20632176cfdSRui Paulo */ 20732176cfdSRui Paulo ieee80211_ampdu_request(ni, tap); 20832176cfdSRui Paulo /* XXX hold frame for reply? */ 20932176cfdSRui Paulo } 21032176cfdSRui Paulo } 2114f655ef5SMatthew Dillon } 212085ff963SMatthew Dillon 21332176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 2144f655ef5SMatthew Dillon /* 2154f655ef5SMatthew Dillon * Check for AMSDU/FF; queue for aggregation 2164f655ef5SMatthew Dillon * 2174f655ef5SMatthew Dillon * Note: we don't bother trying to do fast frames or 2184f655ef5SMatthew Dillon * A-MSDU encapsulation for 802.3 drivers. Now, we 2194f655ef5SMatthew Dillon * likely could do it for FF (because it's a magic 2204f655ef5SMatthew Dillon * atheros tunnel LLC type) but I don't think we're going 2214f655ef5SMatthew Dillon * to really need to. For A-MSDU we'd have to set the 2224f655ef5SMatthew Dillon * A-MSDU QoS bit in the wifi header, so we just plain 2234f655ef5SMatthew Dillon * can't do it. 2244f655ef5SMatthew Dillon * 2254f655ef5SMatthew Dillon * Strictly speaking, we could actually /do/ A-MSDU / FF 2264f655ef5SMatthew Dillon * with A-MPDU together which for certain circumstances 2274f655ef5SMatthew Dillon * is beneficial (eg A-MSDU of TCK ACKs.) However, 2284f655ef5SMatthew Dillon * I'll ignore that for now so existing behaviour is maintained. 2294f655ef5SMatthew Dillon * Later on it would be good to make "amsdu + ampdu" configurable. 2304f655ef5SMatthew Dillon */ 2314f655ef5SMatthew Dillon else if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { 2324f655ef5SMatthew Dillon if ((! mcast) && ieee80211_amsdu_tx_ok(ni)) { 2334f655ef5SMatthew Dillon m = ieee80211_amsdu_check(ni, m); 2344f655ef5SMatthew Dillon if (m == NULL) { 2354f655ef5SMatthew Dillon /* NB: any ni ref held on stageq */ 2364f655ef5SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, 2374f655ef5SMatthew Dillon "%s: amsdu_check queued frame\n", 2384f655ef5SMatthew Dillon __func__); 2394f655ef5SMatthew Dillon return (0); 2404f655ef5SMatthew Dillon } 2414f655ef5SMatthew Dillon } else if ((! mcast) && IEEE80211_ATH_CAP(vap, ni, 2424f655ef5SMatthew Dillon IEEE80211_NODE_FF)) { 24332176cfdSRui Paulo m = ieee80211_ff_check(ni, m); 24432176cfdSRui Paulo if (m == NULL) { 24532176cfdSRui Paulo /* NB: any ni ref held on stageq */ 2464f655ef5SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, 2474f655ef5SMatthew Dillon "%s: ff_check queued frame\n", 2484f655ef5SMatthew Dillon __func__); 249085ff963SMatthew Dillon return (0); 25032176cfdSRui Paulo } 25132176cfdSRui Paulo } 2524f655ef5SMatthew Dillon } 25332176cfdSRui Paulo #endif /* IEEE80211_SUPPORT_SUPERG */ 254085ff963SMatthew Dillon 255085ff963SMatthew Dillon /* 256085ff963SMatthew Dillon * Grab the TX lock - serialise the TX process from this 257085ff963SMatthew Dillon * point (where TX state is being checked/modified) 258085ff963SMatthew Dillon * through to driver queue. 259085ff963SMatthew Dillon */ 260085ff963SMatthew Dillon IEEE80211_TX_LOCK(ic); 261085ff963SMatthew Dillon 2624f655ef5SMatthew Dillon /* 2634f655ef5SMatthew Dillon * XXX make the encap and transmit code a separate function 2644f655ef5SMatthew Dillon * so things like the FF (and later A-MSDU) path can just call 2654f655ef5SMatthew Dillon * it for flushed frames. 2664f655ef5SMatthew Dillon */ 26732176cfdSRui Paulo if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { 26832176cfdSRui Paulo /* 26932176cfdSRui Paulo * Encapsulate the packet in prep for transmission. 27032176cfdSRui Paulo */ 27132176cfdSRui Paulo m = ieee80211_encap(vap, ni, m); 27232176cfdSRui Paulo if (m == NULL) { 27332176cfdSRui Paulo /* NB: stat+msg handled in ieee80211_encap */ 274085ff963SMatthew Dillon IEEE80211_TX_UNLOCK(ic); 27532176cfdSRui Paulo ieee80211_free_node(ni); 2764f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 277085ff963SMatthew Dillon return (ENOBUFS); 27832176cfdSRui Paulo } 27932176cfdSRui Paulo } 2804f655ef5SMatthew Dillon (void) ieee80211_parent_xmitpkt(ic, m); 28132176cfdSRui Paulo 282085ff963SMatthew Dillon /* 283085ff963SMatthew Dillon * Unlock at this point - no need to hold it across 284085ff963SMatthew Dillon * ieee80211_free_node() (ie, the comlock) 285085ff963SMatthew Dillon */ 286085ff963SMatthew Dillon IEEE80211_TX_UNLOCK(ic); 28732176cfdSRui Paulo ic->ic_lastdata = ticks; 288085ff963SMatthew Dillon 289085ff963SMatthew Dillon return (0); 29032176cfdSRui Paulo } 291085ff963SMatthew Dillon 292085ff963SMatthew Dillon 293085ff963SMatthew Dillon 294085ff963SMatthew Dillon /* 295085ff963SMatthew Dillon * Send the given mbuf through the given vap. 296085ff963SMatthew Dillon * 297085ff963SMatthew Dillon * This consumes the mbuf regardless of whether the transmit 298085ff963SMatthew Dillon * was successful or not. 299085ff963SMatthew Dillon * 300085ff963SMatthew Dillon * This does none of the initial checks that ieee80211_start() 301085ff963SMatthew Dillon * does (eg CAC timeout, interface wakeup) - the caller must 302085ff963SMatthew Dillon * do this first. 303085ff963SMatthew Dillon */ 304085ff963SMatthew Dillon static int 305085ff963SMatthew Dillon ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) 306085ff963SMatthew Dillon { 307085ff963SMatthew Dillon #define IS_DWDS(vap) \ 308085ff963SMatthew Dillon (vap->iv_opmode == IEEE80211_M_WDS && \ 309085ff963SMatthew Dillon (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) 310085ff963SMatthew Dillon struct ieee80211com *ic = vap->iv_ic; 311085ff963SMatthew Dillon struct ifnet *ifp = vap->iv_ifp; 312085ff963SMatthew Dillon struct ieee80211_node *ni; 313085ff963SMatthew Dillon struct ether_header *eh; 314085ff963SMatthew Dillon 315085ff963SMatthew Dillon /* 316085ff963SMatthew Dillon * Cancel any background scan. 317085ff963SMatthew Dillon */ 318085ff963SMatthew Dillon if (ic->ic_flags & IEEE80211_F_SCAN) 319085ff963SMatthew Dillon ieee80211_cancel_anyscan(vap); 320085ff963SMatthew Dillon /* 321085ff963SMatthew Dillon * Find the node for the destination so we can do 322085ff963SMatthew Dillon * things like power save and fast frames aggregation. 323085ff963SMatthew Dillon * 324085ff963SMatthew Dillon * NB: past this point various code assumes the first 325085ff963SMatthew Dillon * mbuf has the 802.3 header present (and contiguous). 326085ff963SMatthew Dillon */ 327085ff963SMatthew Dillon ni = NULL; 328085ff963SMatthew Dillon if (m->m_len < sizeof(struct ether_header) && 329085ff963SMatthew Dillon (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { 330085ff963SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 331085ff963SMatthew Dillon "discard frame, %s\n", "m_pullup failed"); 332085ff963SMatthew Dillon vap->iv_stats.is_tx_nobuf++; /* XXX */ 3334f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 334085ff963SMatthew Dillon return (ENOBUFS); 335085ff963SMatthew Dillon } 336085ff963SMatthew Dillon eh = mtod(m, struct ether_header *); 337085ff963SMatthew Dillon if (ETHER_IS_MULTICAST(eh->ether_dhost)) { 338085ff963SMatthew Dillon if (IS_DWDS(vap)) { 339085ff963SMatthew Dillon /* 340085ff963SMatthew Dillon * Only unicast frames from the above go out 341085ff963SMatthew Dillon * DWDS vaps; multicast frames are handled by 342085ff963SMatthew Dillon * dispatching the frame as it comes through 343085ff963SMatthew Dillon * the AP vap (see below). 344085ff963SMatthew Dillon */ 345085ff963SMatthew Dillon IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS, 346085ff963SMatthew Dillon eh->ether_dhost, "mcast", "%s", "on DWDS"); 347085ff963SMatthew Dillon vap->iv_stats.is_dwds_mcast++; 348085ff963SMatthew Dillon m_freem(m); 3494f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 350085ff963SMatthew Dillon /* XXX better status? */ 351085ff963SMatthew Dillon return (ENOBUFS); 352085ff963SMatthew Dillon } 353085ff963SMatthew Dillon if (vap->iv_opmode == IEEE80211_M_HOSTAP) { 354085ff963SMatthew Dillon /* 355085ff963SMatthew Dillon * Spam DWDS vap's w/ multicast traffic. 356085ff963SMatthew Dillon */ 357085ff963SMatthew Dillon /* XXX only if dwds in use? */ 358085ff963SMatthew Dillon ieee80211_dwds_mcast(vap, m); 359085ff963SMatthew Dillon } 360085ff963SMatthew Dillon } 361085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_MESH 362085ff963SMatthew Dillon if (vap->iv_opmode != IEEE80211_M_MBSS) { 363085ff963SMatthew Dillon #endif 364085ff963SMatthew Dillon ni = ieee80211_find_txnode(vap, eh->ether_dhost); 365085ff963SMatthew Dillon if (ni == NULL) { 366085ff963SMatthew Dillon /* NB: ieee80211_find_txnode does stat+msg */ 3674f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 368085ff963SMatthew Dillon m_freem(m); 369085ff963SMatthew Dillon /* XXX better status? */ 370085ff963SMatthew Dillon return (ENOBUFS); 371085ff963SMatthew Dillon } 372085ff963SMatthew Dillon if (ni->ni_associd == 0 && 373085ff963SMatthew Dillon (ni->ni_flags & IEEE80211_NODE_ASSOCID)) { 374085ff963SMatthew Dillon IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, 375085ff963SMatthew Dillon eh->ether_dhost, NULL, 376085ff963SMatthew Dillon "sta not associated (type 0x%04x)", 377085ff963SMatthew Dillon htons(eh->ether_type)); 378085ff963SMatthew Dillon vap->iv_stats.is_tx_notassoc++; 3794f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 380085ff963SMatthew Dillon m_freem(m); 381085ff963SMatthew Dillon ieee80211_free_node(ni); 382085ff963SMatthew Dillon /* XXX better status? */ 383085ff963SMatthew Dillon return (ENOBUFS); 384085ff963SMatthew Dillon } 385085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_MESH 386085ff963SMatthew Dillon } else { 387085ff963SMatthew Dillon if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) { 388085ff963SMatthew Dillon /* 389085ff963SMatthew Dillon * Proxy station only if configured. 390085ff963SMatthew Dillon */ 391085ff963SMatthew Dillon if (!ieee80211_mesh_isproxyena(vap)) { 392085ff963SMatthew Dillon IEEE80211_DISCARD_MAC(vap, 393085ff963SMatthew Dillon IEEE80211_MSG_OUTPUT | 394085ff963SMatthew Dillon IEEE80211_MSG_MESH, 395085ff963SMatthew Dillon eh->ether_dhost, NULL, 396085ff963SMatthew Dillon "%s", "proxy not enabled"); 397085ff963SMatthew Dillon vap->iv_stats.is_mesh_notproxy++; 3984f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 399085ff963SMatthew Dillon m_freem(m); 400085ff963SMatthew Dillon /* XXX better status? */ 401085ff963SMatthew Dillon return (ENOBUFS); 402085ff963SMatthew Dillon } 4034f655ef5SMatthew Dillon #if defined(__DragonFly__) 404085ff963SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 405f92fae3fSSascha Wildner "forward frame from DS SA(%s), DA(%s)\n", 406f92fae3fSSascha Wildner ether_sprintf(eh->ether_shost), 407f92fae3fSSascha Wildner ether_sprintf(eh->ether_dhost)); 4084f655ef5SMatthew Dillon #else 4094f655ef5SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 4104f655ef5SMatthew Dillon "forward frame from DS SA(%6D), DA(%6D)\n", 4114f655ef5SMatthew Dillon eh->ether_shost, ":", 4124f655ef5SMatthew Dillon eh->ether_dhost, ":"); 4134f655ef5SMatthew Dillon #endif 414085ff963SMatthew Dillon ieee80211_mesh_proxy_check(vap, eh->ether_shost); 415085ff963SMatthew Dillon } 416085ff963SMatthew Dillon ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m); 417085ff963SMatthew Dillon if (ni == NULL) { 418085ff963SMatthew Dillon /* 419085ff963SMatthew Dillon * NB: ieee80211_mesh_discover holds/disposes 420085ff963SMatthew Dillon * frame (e.g. queueing on path discovery). 421085ff963SMatthew Dillon */ 4224f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 423085ff963SMatthew Dillon /* XXX better status? */ 424085ff963SMatthew Dillon return (ENOBUFS); 425085ff963SMatthew Dillon } 426085ff963SMatthew Dillon } 427085ff963SMatthew Dillon #endif 428085ff963SMatthew Dillon 429085ff963SMatthew Dillon /* 430085ff963SMatthew Dillon * We've resolved the sender, so attempt to transmit it. 431085ff963SMatthew Dillon */ 432085ff963SMatthew Dillon 433085ff963SMatthew Dillon if (vap->iv_state == IEEE80211_S_SLEEP) { 434085ff963SMatthew Dillon /* 435085ff963SMatthew Dillon * In power save; queue frame and then wakeup device 436085ff963SMatthew Dillon * for transmit. 437085ff963SMatthew Dillon */ 438085ff963SMatthew Dillon ic->ic_lastdata = ticks; 4394f898719SImre Vadász if (ieee80211_pwrsave(ni, m) != 0) 4404f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 441085ff963SMatthew Dillon ieee80211_free_node(ni); 442085ff963SMatthew Dillon ieee80211_new_state(vap, IEEE80211_S_RUN, 0); 443085ff963SMatthew Dillon return (0); 444085ff963SMatthew Dillon } 445085ff963SMatthew Dillon 446085ff963SMatthew Dillon if (ieee80211_vap_pkt_send_dest(vap, m, ni) != 0) 447085ff963SMatthew Dillon return (ENOBUFS); 448085ff963SMatthew Dillon return (0); 44932176cfdSRui Paulo #undef IS_DWDS 45032176cfdSRui Paulo } 45132176cfdSRui Paulo 452085ff963SMatthew Dillon /* 453085ff963SMatthew Dillon * Start method for vap's. All packets from the stack come 454085ff963SMatthew Dillon * through here. We handle common processing of the packets 455085ff963SMatthew Dillon * before dispatching them to the underlying device. 456085ff963SMatthew Dillon * 457085ff963SMatthew Dillon * if_transmit() requires that the mbuf be consumed by this call 458085ff963SMatthew Dillon * regardless of the return condition. 459085ff963SMatthew Dillon */ 460085ff963SMatthew Dillon 461085ff963SMatthew Dillon #if defined(__DragonFly__) 462085ff963SMatthew Dillon 463085ff963SMatthew Dillon void 464085ff963SMatthew Dillon ieee80211_vap_start(struct ifnet *ifp, struct ifaltq_subque *ifsq) 465085ff963SMatthew Dillon { 466085ff963SMatthew Dillon struct ieee80211vap *vap = ifp->if_softc; 467085ff963SMatthew Dillon struct ieee80211com *ic = vap->iv_ic; 4684f655ef5SMatthew Dillon struct ifnet *parent = vap->iv_ifp; 469085ff963SMatthew Dillon struct mbuf *m = NULL; 470085ff963SMatthew Dillon 471085ff963SMatthew Dillon /* NB: parent must be up and running */ 472085ff963SMatthew Dillon if (!IFNET_IS_UP_RUNNING(parent)) { 473085ff963SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 474085ff963SMatthew Dillon "%s: ignore queue, parent %s not up+running\n", 475085ff963SMatthew Dillon __func__, parent->if_xname); 476085ff963SMatthew Dillon /* XXX stat */ 477085ff963SMatthew Dillon /*m_freem(m);*/ 478085ff963SMatthew Dillon /*return (EINVAL);*/ 479085ff963SMatthew Dillon return; 480085ff963SMatthew Dillon } 481085ff963SMatthew Dillon 482085ff963SMatthew Dillon wlan_assert_serialized(); 483085ff963SMatthew Dillon ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq); 484085ff963SMatthew Dillon 485085ff963SMatthew Dillon /* 486085ff963SMatthew Dillon * No data frames go out unless we're running. 487085ff963SMatthew Dillon * Note in particular this covers CAC and CSA 488085ff963SMatthew Dillon * states (though maybe we should check muting 489085ff963SMatthew Dillon * for CSA). 490085ff963SMatthew Dillon */ 491085ff963SMatthew Dillon if (vap->iv_state != IEEE80211_S_RUN && 492085ff963SMatthew Dillon vap->iv_state != IEEE80211_S_SLEEP) { 493085ff963SMatthew Dillon IEEE80211_LOCK(ic); 494085ff963SMatthew Dillon /* re-check under the com lock to avoid races */ 495085ff963SMatthew Dillon if (vap->iv_state != IEEE80211_S_RUN && 496085ff963SMatthew Dillon vap->iv_state != IEEE80211_S_SLEEP) { 497085ff963SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 498085ff963SMatthew Dillon "%s: ignore queue, in %s state\n", 499085ff963SMatthew Dillon __func__, ieee80211_state_name[vap->iv_state]); 500085ff963SMatthew Dillon vap->iv_stats.is_tx_badstate++; 501085ff963SMatthew Dillon IEEE80211_UNLOCK(ic); 502085ff963SMatthew Dillon ifsq_set_oactive(ifsq); 503085ff963SMatthew Dillon /*m_freem(m);*/ 504085ff963SMatthew Dillon /* return (EINVAL); */ 505085ff963SMatthew Dillon return; 506085ff963SMatthew Dillon } 507085ff963SMatthew Dillon IEEE80211_UNLOCK(ic); 508085ff963SMatthew Dillon } 509085ff963SMatthew Dillon 510085ff963SMatthew Dillon wlan_serialize_exit(); 511085ff963SMatthew Dillon for (;;) { 512085ff963SMatthew Dillon m = ifsq_dequeue(ifsq); 513085ff963SMatthew Dillon if (m == NULL) 514085ff963SMatthew Dillon break; 515085ff963SMatthew Dillon 516085ff963SMatthew Dillon /* 517085ff963SMatthew Dillon * Sanitize mbuf flags for net80211 use. We cannot 518085ff963SMatthew Dillon * clear M_PWR_SAV or M_MORE_DATA because these may 519085ff963SMatthew Dillon * be set for frames that are re-submitted from the 520085ff963SMatthew Dillon * power save queue. 521085ff963SMatthew Dillon * 522085ff963SMatthew Dillon * NB: This must be done before ieee80211_classify as 523085ff963SMatthew Dillon * it marks EAPOL in frames with M_EAPOL. 524085ff963SMatthew Dillon */ 525085ff963SMatthew Dillon m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); 526085ff963SMatthew Dillon 527085ff963SMatthew Dillon /* 528085ff963SMatthew Dillon * Bump to the packet transmission path. 529085ff963SMatthew Dillon * The mbuf will be consumed here. 530085ff963SMatthew Dillon */ 531085ff963SMatthew Dillon ieee80211_start_pkt(vap, m); 532085ff963SMatthew Dillon } 533085ff963SMatthew Dillon wlan_serialize_enter(); 534085ff963SMatthew Dillon } 535085ff963SMatthew Dillon 536085ff963SMatthew Dillon #else 537085ff963SMatthew Dillon 538085ff963SMatthew Dillon int 539085ff963SMatthew Dillon ieee80211_vap_transmit(struct ifnet *ifp, struct mbuf *m) 540085ff963SMatthew Dillon { 541085ff963SMatthew Dillon struct ieee80211vap *vap = ifp->if_softc; 542085ff963SMatthew Dillon struct ieee80211com *ic = vap->iv_ic; 543085ff963SMatthew Dillon 544085ff963SMatthew Dillon /* 545085ff963SMatthew Dillon * No data frames go out unless we're running. 546085ff963SMatthew Dillon * Note in particular this covers CAC and CSA 547085ff963SMatthew Dillon * states (though maybe we should check muting 548085ff963SMatthew Dillon * for CSA). 549085ff963SMatthew Dillon */ 550085ff963SMatthew Dillon if (vap->iv_state != IEEE80211_S_RUN && 551085ff963SMatthew Dillon vap->iv_state != IEEE80211_S_SLEEP) { 552085ff963SMatthew Dillon IEEE80211_LOCK(ic); 553085ff963SMatthew Dillon /* re-check under the com lock to avoid races */ 554085ff963SMatthew Dillon if (vap->iv_state != IEEE80211_S_RUN && 555085ff963SMatthew Dillon vap->iv_state != IEEE80211_S_SLEEP) { 556085ff963SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 557085ff963SMatthew Dillon "%s: ignore queue, in %s state\n", 558085ff963SMatthew Dillon __func__, ieee80211_state_name[vap->iv_state]); 559085ff963SMatthew Dillon vap->iv_stats.is_tx_badstate++; 560085ff963SMatthew Dillon IEEE80211_UNLOCK(ic); 561085ff963SMatthew Dillon ifp->if_drv_flags |= IFF_DRV_OACTIVE; 562085ff963SMatthew Dillon m_freem(m); 5634f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 5644f898719SImre Vadász return (ENETDOWN); 565085ff963SMatthew Dillon } 566085ff963SMatthew Dillon IEEE80211_UNLOCK(ic); 567085ff963SMatthew Dillon } 568085ff963SMatthew Dillon 569085ff963SMatthew Dillon /* 570085ff963SMatthew Dillon * Sanitize mbuf flags for net80211 use. We cannot 571085ff963SMatthew Dillon * clear M_PWR_SAV or M_MORE_DATA because these may 572085ff963SMatthew Dillon * be set for frames that are re-submitted from the 573085ff963SMatthew Dillon * power save queue. 574085ff963SMatthew Dillon * 575085ff963SMatthew Dillon * NB: This must be done before ieee80211_classify as 576085ff963SMatthew Dillon * it marks EAPOL in frames with M_EAPOL. 577085ff963SMatthew Dillon */ 578085ff963SMatthew Dillon m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); 579085ff963SMatthew Dillon 580085ff963SMatthew Dillon /* 581085ff963SMatthew Dillon * Bump to the packet transmission path. 582085ff963SMatthew Dillon * The mbuf will be consumed here. 583085ff963SMatthew Dillon */ 584085ff963SMatthew Dillon return (ieee80211_start_pkt(vap, m)); 585085ff963SMatthew Dillon } 586085ff963SMatthew Dillon 587085ff963SMatthew Dillon void 588085ff963SMatthew Dillon ieee80211_vap_qflush(struct ifnet *ifp) 589085ff963SMatthew Dillon { 590085ff963SMatthew Dillon 591085ff963SMatthew Dillon /* Empty for now */ 592085ff963SMatthew Dillon } 593085ff963SMatthew Dillon 594085ff963SMatthew Dillon #endif 595085ff963SMatthew Dillon 596085ff963SMatthew Dillon /* 597085ff963SMatthew Dillon * 802.11 raw output routine. 598085ff963SMatthew Dillon * 599085ff963SMatthew Dillon * XXX TODO: this (and other send routines) should correctly 600085ff963SMatthew Dillon * XXX keep the pwr mgmt bit set if it decides to call into the 601085ff963SMatthew Dillon * XXX driver to send a frame whilst the state is SLEEP. 602085ff963SMatthew Dillon * 603085ff963SMatthew Dillon * Otherwise the peer may decide that we're awake and flood us 604085ff963SMatthew Dillon * with traffic we are still too asleep to receive! 605085ff963SMatthew Dillon */ 606085ff963SMatthew Dillon int 607085ff963SMatthew Dillon ieee80211_raw_output(struct ieee80211vap *vap, struct ieee80211_node *ni, 608085ff963SMatthew Dillon struct mbuf *m, const struct ieee80211_bpf_params *params) 609085ff963SMatthew Dillon { 610085ff963SMatthew Dillon struct ieee80211com *ic = vap->iv_ic; 6114f655ef5SMatthew Dillon int error; 612085ff963SMatthew Dillon 6134f898719SImre Vadász /* 6144f898719SImre Vadász * Set node - the caller has taken a reference, so ensure 6154f898719SImre Vadász * that the mbuf has the same node value that 6164f898719SImre Vadász * it would if it were going via the normal path. 6174f898719SImre Vadász */ 6184f898719SImre Vadász m->m_pkthdr.rcvif = (void *)ni; 6194f898719SImre Vadász 6204f898719SImre Vadász /* 6214f898719SImre Vadász * Attempt to add bpf transmit parameters. 6224f898719SImre Vadász * 6234f898719SImre Vadász * For now it's ok to fail; the raw_xmit api still takes 6244f898719SImre Vadász * them as an option. 6254f898719SImre Vadász * 6264f898719SImre Vadász * Later on when ic_raw_xmit() has params removed, 6274f898719SImre Vadász * they'll have to be added - so fail the transmit if 6284f898719SImre Vadász * they can't be. 6294f898719SImre Vadász */ 6304f898719SImre Vadász if (params) 6314f898719SImre Vadász (void) ieee80211_add_xmit_params(m, params); 6324f898719SImre Vadász 6334f655ef5SMatthew Dillon error = ic->ic_raw_xmit(ni, m, params); 6344f655ef5SMatthew Dillon if (error) { 6354f655ef5SMatthew Dillon if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, 1); 6364f655ef5SMatthew Dillon ieee80211_free_node(ni); 6374f655ef5SMatthew Dillon } 6384f655ef5SMatthew Dillon return (error); 639085ff963SMatthew Dillon } 640cc0d8938SRui Paulo 64132176cfdSRui Paulo /* 64232176cfdSRui Paulo * 802.11 output routine. This is (currently) used only to 64332176cfdSRui Paulo * connect bpf write calls to the 802.11 layer for injecting 64432176cfdSRui Paulo * raw 802.11 frames. 64532176cfdSRui Paulo */ 646085ff963SMatthew Dillon #if defined(__DragonFly__) 64732176cfdSRui Paulo int 64832176cfdSRui Paulo ieee80211_output(struct ifnet *ifp, struct mbuf *m, 64934a60cf6SRui Paulo struct sockaddr *dst, struct rtentry *rt) 650085ff963SMatthew Dillon #else 651085ff963SMatthew Dillon int 652085ff963SMatthew Dillon ieee80211_output(struct ifnet *ifp, struct mbuf *m, 653294727bfSImre Vadász const struct sockaddr *dst, struct route *ro) 654085ff963SMatthew Dillon #endif 65532176cfdSRui Paulo { 65632176cfdSRui Paulo #define senderr(e) do { error = (e); goto bad;} while (0) 65732176cfdSRui Paulo struct ieee80211_node *ni = NULL; 65832176cfdSRui Paulo struct ieee80211vap *vap; 65932176cfdSRui Paulo struct ieee80211_frame *wh; 660085ff963SMatthew Dillon struct ieee80211com *ic = NULL; 66132176cfdSRui Paulo int error; 662085ff963SMatthew Dillon int ret; 66332176cfdSRui Paulo 664085ff963SMatthew Dillon #if defined(__DragonFly__) 665085ff963SMatthew Dillon struct ifaltq_subque *ifsq; 666f0a26983SSepherosa Ziehau ifsq = ifq_get_subq_default(&ifp->if_snd); 6674f655ef5SMatthew Dillon if (ifsq_is_oactive(ifsq)) { 668085ff963SMatthew Dillon #else 6694f655ef5SMatthew Dillon if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { 670085ff963SMatthew Dillon #endif 67132176cfdSRui Paulo /* 67232176cfdSRui Paulo * Short-circuit requests if the vap is marked OACTIVE 67332176cfdSRui Paulo * as this can happen because a packet came down through 67432176cfdSRui Paulo * ieee80211_start before the vap entered RUN state in 67532176cfdSRui Paulo * which case it's ok to just drop the frame. This 67632176cfdSRui Paulo * should not be necessary but callers of if_output don't 67732176cfdSRui Paulo * check OACTIVE. 67832176cfdSRui Paulo */ 67932176cfdSRui Paulo senderr(ENETDOWN); 68032176cfdSRui Paulo } 68132176cfdSRui Paulo vap = ifp->if_softc; 682085ff963SMatthew Dillon ic = vap->iv_ic; 68332176cfdSRui Paulo /* 68432176cfdSRui Paulo * Hand to the 802.3 code if not tagged as 68532176cfdSRui Paulo * a raw 802.11 frame. 68632176cfdSRui Paulo */ 687085ff963SMatthew Dillon #if defined(__DragonFly__) 68832176cfdSRui Paulo if (dst->sa_family != AF_IEEE80211) 68934a60cf6SRui Paulo return vap->iv_output(ifp, m, dst, rt); 690085ff963SMatthew Dillon #else 691085ff963SMatthew Dillon if (dst->sa_family != AF_IEEE80211) 692085ff963SMatthew Dillon return vap->iv_output(ifp, m, dst, ro); 693085ff963SMatthew Dillon #endif 69432176cfdSRui Paulo #ifdef MAC 69532176cfdSRui Paulo error = mac_ifnet_check_transmit(ifp, m); 69632176cfdSRui Paulo if (error) 69732176cfdSRui Paulo senderr(error); 69832176cfdSRui Paulo #endif 69932176cfdSRui Paulo if (ifp->if_flags & IFF_MONITOR) 70032176cfdSRui Paulo senderr(ENETDOWN); 70132176cfdSRui Paulo if (!IFNET_IS_UP_RUNNING(ifp)) 70232176cfdSRui Paulo senderr(ENETDOWN); 70332176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_CAC) { 70432176cfdSRui Paulo IEEE80211_DPRINTF(vap, 70532176cfdSRui Paulo IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, 70632176cfdSRui Paulo "block %s frame in CAC state\n", "raw data"); 70732176cfdSRui Paulo vap->iv_stats.is_tx_badstate++; 70832176cfdSRui Paulo senderr(EIO); /* XXX */ 709085ff963SMatthew Dillon } else if (vap->iv_state == IEEE80211_S_SCAN) 710085ff963SMatthew Dillon senderr(EIO); 71132176cfdSRui Paulo /* XXX bypass bridge, pfil, carp, etc. */ 71232176cfdSRui Paulo 71332176cfdSRui Paulo if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) 71432176cfdSRui Paulo senderr(EIO); /* XXX */ 71532176cfdSRui Paulo wh = mtod(m, struct ieee80211_frame *); 71632176cfdSRui Paulo if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 71732176cfdSRui Paulo IEEE80211_FC0_VERSION_0) 71832176cfdSRui Paulo senderr(EIO); /* XXX */ 71932176cfdSRui Paulo 72032176cfdSRui Paulo /* locate destination node */ 72132176cfdSRui Paulo switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 72232176cfdSRui Paulo case IEEE80211_FC1_DIR_NODS: 72332176cfdSRui Paulo case IEEE80211_FC1_DIR_FROMDS: 72432176cfdSRui Paulo ni = ieee80211_find_txnode(vap, wh->i_addr1); 72532176cfdSRui Paulo break; 72632176cfdSRui Paulo case IEEE80211_FC1_DIR_TODS: 72732176cfdSRui Paulo case IEEE80211_FC1_DIR_DSTODS: 72832176cfdSRui Paulo if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) 72932176cfdSRui Paulo senderr(EIO); /* XXX */ 73032176cfdSRui Paulo ni = ieee80211_find_txnode(vap, wh->i_addr3); 73132176cfdSRui Paulo break; 73232176cfdSRui Paulo default: 73332176cfdSRui Paulo senderr(EIO); /* XXX */ 73432176cfdSRui Paulo } 73532176cfdSRui Paulo if (ni == NULL) { 73632176cfdSRui Paulo /* 73732176cfdSRui Paulo * Permit packets w/ bpf params through regardless 73832176cfdSRui Paulo * (see below about sa_len). 73932176cfdSRui Paulo */ 74032176cfdSRui Paulo if (dst->sa_len == 0) 74132176cfdSRui Paulo senderr(EHOSTUNREACH); 74232176cfdSRui Paulo ni = ieee80211_ref_node(vap->iv_bss); 74332176cfdSRui Paulo } 74432176cfdSRui Paulo 74532176cfdSRui Paulo /* 74632176cfdSRui Paulo * Sanitize mbuf for net80211 flags leaked from above. 74732176cfdSRui Paulo * 74832176cfdSRui Paulo * NB: This must be done before ieee80211_classify as 74932176cfdSRui Paulo * it marks EAPOL in frames with M_EAPOL. 75032176cfdSRui Paulo */ 75132176cfdSRui Paulo m->m_flags &= ~M_80211_TX; 75232176cfdSRui Paulo 75332176cfdSRui Paulo /* calculate priority so drivers can find the tx queue */ 75432176cfdSRui Paulo /* XXX assumes an 802.3 frame */ 75532176cfdSRui Paulo if (ieee80211_classify(ni, m)) 75632176cfdSRui Paulo senderr(EIO); /* XXX */ 75732176cfdSRui Paulo 7584f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 75932176cfdSRui Paulo IEEE80211_NODE_STAT(ni, tx_data); 76032176cfdSRui Paulo if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 76132176cfdSRui Paulo IEEE80211_NODE_STAT(ni, tx_mcast); 76232176cfdSRui Paulo m->m_flags |= M_MCAST; 76332176cfdSRui Paulo } else 76432176cfdSRui Paulo IEEE80211_NODE_STAT(ni, tx_ucast); 76532176cfdSRui Paulo /* NB: ieee80211_encap does not include 802.11 header */ 76632176cfdSRui Paulo IEEE80211_NODE_STAT_ADD(ni, tx_bytes, m->m_pkthdr.len); 76732176cfdSRui Paulo 768085ff963SMatthew Dillon IEEE80211_TX_LOCK(ic); 769085ff963SMatthew Dillon 77032176cfdSRui Paulo /* 77132176cfdSRui Paulo * NB: DLT_IEEE802_11_RADIO identifies the parameters are 77232176cfdSRui Paulo * present by setting the sa_len field of the sockaddr (yes, 77332176cfdSRui Paulo * this is a hack). 77432176cfdSRui Paulo * NB: we assume sa_data is suitably aligned to cast. 77532176cfdSRui Paulo */ 776085ff963SMatthew Dillon ret = ieee80211_raw_output(vap, ni, m, 77732176cfdSRui Paulo (const struct ieee80211_bpf_params *)(dst->sa_len ? 77832176cfdSRui Paulo dst->sa_data : NULL)); 779085ff963SMatthew Dillon IEEE80211_TX_UNLOCK(ic); 780085ff963SMatthew Dillon return (ret); 78132176cfdSRui Paulo bad: 78232176cfdSRui Paulo if (m != NULL) 78332176cfdSRui Paulo m_freem(m); 78432176cfdSRui Paulo if (ni != NULL) 78532176cfdSRui Paulo ieee80211_free_node(ni); 7864f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 78732176cfdSRui Paulo return error; 78832176cfdSRui Paulo #undef senderr 78932176cfdSRui Paulo } 79032176cfdSRui Paulo 79132176cfdSRui Paulo /* 79232176cfdSRui Paulo * Set the direction field and address fields of an outgoing 79332176cfdSRui Paulo * frame. Note this should be called early on in constructing 79432176cfdSRui Paulo * a frame as it sets i_fc[1]; other bits can then be or'd in. 79532176cfdSRui Paulo */ 79632176cfdSRui Paulo void 79732176cfdSRui Paulo ieee80211_send_setup( 798841ab66cSSepherosa Ziehau struct ieee80211_node *ni, 79932176cfdSRui Paulo struct mbuf *m, 80032176cfdSRui Paulo int type, int tid, 801841ab66cSSepherosa Ziehau const uint8_t sa[IEEE80211_ADDR_LEN], 802841ab66cSSepherosa Ziehau const uint8_t da[IEEE80211_ADDR_LEN], 803841ab66cSSepherosa Ziehau const uint8_t bssid[IEEE80211_ADDR_LEN]) 804841ab66cSSepherosa Ziehau { 805841ab66cSSepherosa Ziehau #define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) 80632176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 807085ff963SMatthew Dillon struct ieee80211_tx_ampdu *tap; 80832176cfdSRui Paulo struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); 80932176cfdSRui Paulo ieee80211_seq seqno; 810841ab66cSSepherosa Ziehau 811085ff963SMatthew Dillon IEEE80211_TX_LOCK_ASSERT(ni->ni_ic); 812085ff963SMatthew Dillon 813841ab66cSSepherosa Ziehau wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; 814841ab66cSSepherosa Ziehau if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { 81532176cfdSRui Paulo switch (vap->iv_opmode) { 816841ab66cSSepherosa Ziehau case IEEE80211_M_STA: 817841ab66cSSepherosa Ziehau wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 818841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr1, bssid); 819841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr2, sa); 820841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr3, da); 821841ab66cSSepherosa Ziehau break; 822841ab66cSSepherosa Ziehau case IEEE80211_M_IBSS: 823841ab66cSSepherosa Ziehau case IEEE80211_M_AHDEMO: 824841ab66cSSepherosa Ziehau wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 825841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr1, da); 826841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr2, sa); 827841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr3, bssid); 828841ab66cSSepherosa Ziehau break; 829841ab66cSSepherosa Ziehau case IEEE80211_M_HOSTAP: 830841ab66cSSepherosa Ziehau wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 831841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr1, da); 832841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr2, bssid); 833841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr3, sa); 834841ab66cSSepherosa Ziehau break; 83532176cfdSRui Paulo case IEEE80211_M_WDS: 83632176cfdSRui Paulo wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; 83732176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr1, da); 83832176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 83932176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr3, da); 84032176cfdSRui Paulo IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); 84132176cfdSRui Paulo break; 84232176cfdSRui Paulo case IEEE80211_M_MBSS: 84332176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 84432176cfdSRui Paulo if (IEEE80211_IS_MULTICAST(da)) { 84532176cfdSRui Paulo wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 84632176cfdSRui Paulo /* XXX next hop */ 84732176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr1, da); 84832176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr2, 84932176cfdSRui Paulo vap->iv_myaddr); 85032176cfdSRui Paulo } else { 85132176cfdSRui Paulo wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; 85232176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr1, da); 85332176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr2, 85432176cfdSRui Paulo vap->iv_myaddr); 85532176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr3, da); 85632176cfdSRui Paulo IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); 85732176cfdSRui Paulo } 85832176cfdSRui Paulo #endif 85932176cfdSRui Paulo break; 860841ab66cSSepherosa Ziehau case IEEE80211_M_MONITOR: /* NB: to quiet compiler */ 861841ab66cSSepherosa Ziehau break; 862841ab66cSSepherosa Ziehau } 863841ab66cSSepherosa Ziehau } else { 864841ab66cSSepherosa Ziehau wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 865841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr1, da); 866841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr2, sa); 86732176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 86832176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_MBSS) 86932176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr3, sa); 87032176cfdSRui Paulo else 87132176cfdSRui Paulo #endif 872841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr3, bssid); 873841ab66cSSepherosa Ziehau } 874841ab66cSSepherosa Ziehau *(uint16_t *)&wh->i_dur[0] = 0; 87532176cfdSRui Paulo 876085ff963SMatthew Dillon tap = &ni->ni_tx_ampdu[tid]; 877085ff963SMatthew Dillon if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) 878085ff963SMatthew Dillon m->m_flags |= M_AMPDU_MPDU; 879085ff963SMatthew Dillon else { 8804f898719SImre Vadász if (IEEE80211_HAS_SEQ(type & IEEE80211_FC0_TYPE_MASK, 8814f898719SImre Vadász type & IEEE80211_FC0_SUBTYPE_MASK)) 88232176cfdSRui Paulo seqno = ni->ni_txseqs[tid]++; 8834f898719SImre Vadász else 8844f898719SImre Vadász seqno = 0; 8854f655ef5SMatthew Dillon 886085ff963SMatthew Dillon *(uint16_t *)&wh->i_seq[0] = 887085ff963SMatthew Dillon htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); 88832176cfdSRui Paulo M_SEQNO_SET(m, seqno); 889085ff963SMatthew Dillon } 89032176cfdSRui Paulo 89132176cfdSRui Paulo if (IEEE80211_IS_MULTICAST(wh->i_addr1)) 89232176cfdSRui Paulo m->m_flags |= M_MCAST; 893841ab66cSSepherosa Ziehau #undef WH4 894841ab66cSSepherosa Ziehau } 895841ab66cSSepherosa Ziehau 896f186073cSJoerg Sonnenberger /* 897f186073cSJoerg Sonnenberger * Send a management frame to the specified node. The node pointer 898f186073cSJoerg Sonnenberger * must have a reference as the pointer will be passed to the driver 899f186073cSJoerg Sonnenberger * and potentially held for a long time. If the frame is successfully 9004f655ef5SMatthew Dillon * dispatched to the driver, then it is responsible forkfreeing the 9014f655ef5SMatthew Dillon * reference (and potentiallykfree'ing up any associated storage); 90232176cfdSRui Paulo * otherwise deal with reclaiming any reference (on error). 903f186073cSJoerg Sonnenberger */ 90432176cfdSRui Paulo int 90532176cfdSRui Paulo ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type, 90632176cfdSRui Paulo struct ieee80211_bpf_params *params) 907f186073cSJoerg Sonnenberger { 90832176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 90932176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic; 910f186073cSJoerg Sonnenberger struct ieee80211_frame *wh; 911085ff963SMatthew Dillon int ret; 912085ff963SMatthew Dillon 913f186073cSJoerg Sonnenberger KASSERT(ni != NULL, ("null node")); 914f186073cSJoerg Sonnenberger 91532176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_CAC) { 91632176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, 91732176cfdSRui Paulo ni, "block %s frame in CAC state", 9184f655ef5SMatthew Dillon ieee80211_mgt_subtype_name(type)); 91932176cfdSRui Paulo vap->iv_stats.is_tx_badstate++; 92032176cfdSRui Paulo ieee80211_free_node(ni); 92132176cfdSRui Paulo m_freem(m); 92232176cfdSRui Paulo return EIO; /* XXX */ 92332176cfdSRui Paulo } 92432176cfdSRui Paulo 925b5523eacSSascha Wildner M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); 92632176cfdSRui Paulo if (m == NULL) { 92732176cfdSRui Paulo ieee80211_free_node(ni); 928f186073cSJoerg Sonnenberger return ENOMEM; 92932176cfdSRui Paulo } 930f186073cSJoerg Sonnenberger 931085ff963SMatthew Dillon IEEE80211_TX_LOCK(ic); 932085ff963SMatthew Dillon 933f186073cSJoerg Sonnenberger wh = mtod(m, struct ieee80211_frame *); 93432176cfdSRui Paulo ieee80211_send_setup(ni, m, 93532176cfdSRui Paulo IEEE80211_FC0_TYPE_MGT | type, IEEE80211_NONQOS_TID, 93632176cfdSRui Paulo vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); 93732176cfdSRui Paulo if (params->ibp_flags & IEEE80211_BPF_CRYPTO) { 93832176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1, 93932176cfdSRui Paulo "encrypting frame (%s)", __func__); 940085ff963SMatthew Dillon wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; 941f186073cSJoerg Sonnenberger } 94232176cfdSRui Paulo m->m_flags |= M_ENCAP; /* mark encapsulated */ 94332176cfdSRui Paulo 94432176cfdSRui Paulo KASSERT(type != IEEE80211_FC0_SUBTYPE_PROBE_RESP, ("probe response?")); 94532176cfdSRui Paulo M_WME_SETAC(m, params->ibp_pri); 94632176cfdSRui Paulo 947841ab66cSSepherosa Ziehau #ifdef IEEE80211_DEBUG 948841ab66cSSepherosa Ziehau /* avoid printing too many frames */ 94932176cfdSRui Paulo if ((ieee80211_msg_debug(vap) && doprint(vap, type)) || 95032176cfdSRui Paulo ieee80211_msg_dumppkts(vap)) { 9511e290df3SAntonio Huete Jimenez kprintf("[%s] send %s on channel %u\n", 952085ff963SMatthew Dillon ether_sprintf(wh->i_addr1), 9534f655ef5SMatthew Dillon ieee80211_mgt_subtype_name(type), 954841ab66cSSepherosa Ziehau ieee80211_chan2ieee(ic, ic->ic_curchan)); 955841ab66cSSepherosa Ziehau } 956841ab66cSSepherosa Ziehau #endif 957841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_mgmt); 95832176cfdSRui Paulo 959085ff963SMatthew Dillon ret = ieee80211_raw_output(vap, ni, m, params); 960085ff963SMatthew Dillon IEEE80211_TX_UNLOCK(ic); 961085ff963SMatthew Dillon return (ret); 962f186073cSJoerg Sonnenberger } 963f186073cSJoerg Sonnenberger 9644f655ef5SMatthew Dillon static void 9654f655ef5SMatthew Dillon ieee80211_nulldata_transmitted(struct ieee80211_node *ni, void *arg, 9664f655ef5SMatthew Dillon int status) 9674f655ef5SMatthew Dillon { 9684f655ef5SMatthew Dillon struct ieee80211vap *vap = ni->ni_vap; 9694f655ef5SMatthew Dillon 9704f655ef5SMatthew Dillon wakeup(vap); 9714f655ef5SMatthew Dillon } 9724f655ef5SMatthew Dillon 973f186073cSJoerg Sonnenberger /* 97432176cfdSRui Paulo * Send a null data frame to the specified node. If the station 97532176cfdSRui Paulo * is setup for QoS then a QoS Null Data frame is constructed. 97632176cfdSRui Paulo * If this is a WDS station then a 4-address frame is constructed. 977f186073cSJoerg Sonnenberger * 978841ab66cSSepherosa Ziehau * NB: the caller is assumed to have setup a node reference 979841ab66cSSepherosa Ziehau * for use; this is necessary to deal with a race condition 98032176cfdSRui Paulo * when probing for inactive stations. Like ieee80211_mgmt_output 98132176cfdSRui Paulo * we must cleanup any node reference on error; however we 98232176cfdSRui Paulo * can safely just unref it as we know it will never be the 98332176cfdSRui Paulo * last reference to the node. 984841ab66cSSepherosa Ziehau */ 985841ab66cSSepherosa Ziehau int 986841ab66cSSepherosa Ziehau ieee80211_send_nulldata(struct ieee80211_node *ni) 987841ab66cSSepherosa Ziehau { 98832176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 989841ab66cSSepherosa Ziehau struct ieee80211com *ic = ni->ni_ic; 990841ab66cSSepherosa Ziehau struct mbuf *m; 991841ab66cSSepherosa Ziehau struct ieee80211_frame *wh; 99232176cfdSRui Paulo int hdrlen; 99332176cfdSRui Paulo uint8_t *frm; 994085ff963SMatthew Dillon int ret; 995841ab66cSSepherosa Ziehau 99632176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_CAC) { 99732176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, 99832176cfdSRui Paulo ni, "block %s frame in CAC state", "null data"); 99932176cfdSRui Paulo ieee80211_unref_node(&ni); 100032176cfdSRui Paulo vap->iv_stats.is_tx_badstate++; 100132176cfdSRui Paulo return EIO; /* XXX */ 100232176cfdSRui Paulo } 100332176cfdSRui Paulo 100432176cfdSRui Paulo if (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) 100532176cfdSRui Paulo hdrlen = sizeof(struct ieee80211_qosframe); 100632176cfdSRui Paulo else 100732176cfdSRui Paulo hdrlen = sizeof(struct ieee80211_frame); 100832176cfdSRui Paulo /* NB: only WDS vap's get 4-address frames */ 100932176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_WDS) 101032176cfdSRui Paulo hdrlen += IEEE80211_ADDR_LEN; 101132176cfdSRui Paulo if (ic->ic_flags & IEEE80211_F_DATAPAD) 101232176cfdSRui Paulo hdrlen = roundup(hdrlen, sizeof(uint32_t)); 101332176cfdSRui Paulo 101432176cfdSRui Paulo m = ieee80211_getmgtframe(&frm, ic->ic_headroom + hdrlen, 0); 1015841ab66cSSepherosa Ziehau if (m == NULL) { 1016841ab66cSSepherosa Ziehau /* XXX debug msg */ 1017841ab66cSSepherosa Ziehau ieee80211_unref_node(&ni); 101832176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++; 1019841ab66cSSepherosa Ziehau return ENOMEM; 1020841ab66cSSepherosa Ziehau } 102132176cfdSRui Paulo KASSERT(M_LEADINGSPACE(m) >= hdrlen, 102232176cfdSRui Paulo ("leading space %zd", M_LEADINGSPACE(m))); 1023b5523eacSSascha Wildner M_PREPEND(m, hdrlen, M_NOWAIT); 102432176cfdSRui Paulo if (m == NULL) { 102532176cfdSRui Paulo /* NB: cannot happen */ 102632176cfdSRui Paulo ieee80211_free_node(ni); 102732176cfdSRui Paulo return ENOMEM; 102832176cfdSRui Paulo } 1029841ab66cSSepherosa Ziehau 1030085ff963SMatthew Dillon IEEE80211_TX_LOCK(ic); 1031085ff963SMatthew Dillon 103232176cfdSRui Paulo wh = mtod(m, struct ieee80211_frame *); /* NB: a little lie */ 103332176cfdSRui Paulo if (ni->ni_flags & IEEE80211_NODE_QOS) { 103432176cfdSRui Paulo const int tid = WME_AC_TO_TID(WME_AC_BE); 103532176cfdSRui Paulo uint8_t *qos; 103632176cfdSRui Paulo 103732176cfdSRui Paulo ieee80211_send_setup(ni, m, 103832176cfdSRui Paulo IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS_NULL, 103932176cfdSRui Paulo tid, vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); 104032176cfdSRui Paulo 104132176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_WDS) 104232176cfdSRui Paulo qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; 104332176cfdSRui Paulo else 104432176cfdSRui Paulo qos = ((struct ieee80211_qosframe *) wh)->i_qos; 104532176cfdSRui Paulo qos[0] = tid & IEEE80211_QOS_TID; 104632176cfdSRui Paulo if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[WME_AC_BE].wmep_noackPolicy) 104732176cfdSRui Paulo qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; 104832176cfdSRui Paulo qos[1] = 0; 104932176cfdSRui Paulo } else { 105032176cfdSRui Paulo ieee80211_send_setup(ni, m, 1051841ab66cSSepherosa Ziehau IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, 105232176cfdSRui Paulo IEEE80211_NONQOS_TID, 105332176cfdSRui Paulo vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); 105432176cfdSRui Paulo } 105532176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_WDS) { 1056841ab66cSSepherosa Ziehau /* NB: power management bit is never sent by an AP */ 1057841ab66cSSepherosa Ziehau if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && 105832176cfdSRui Paulo vap->iv_opmode != IEEE80211_M_HOSTAP) 1059841ab66cSSepherosa Ziehau wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; 106032176cfdSRui Paulo } 10614f655ef5SMatthew Dillon if ((ic->ic_flags & IEEE80211_F_SCAN) && 10624f655ef5SMatthew Dillon (ni->ni_flags & IEEE80211_NODE_PWR_MGT)) { 10634f655ef5SMatthew Dillon ieee80211_add_callback(m, ieee80211_nulldata_transmitted, 10644f655ef5SMatthew Dillon NULL); 10654f655ef5SMatthew Dillon } 106632176cfdSRui Paulo m->m_len = m->m_pkthdr.len = hdrlen; 106732176cfdSRui Paulo m->m_flags |= M_ENCAP; /* mark encapsulated */ 106832176cfdSRui Paulo 106932176cfdSRui Paulo M_WME_SETAC(m, WME_AC_BE); 1070841ab66cSSepherosa Ziehau 1071841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_data); 1072841ab66cSSepherosa Ziehau 107332176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, ni, 107432176cfdSRui Paulo "send %snull data frame on channel %u, pwr mgt %s", 107532176cfdSRui Paulo ni->ni_flags & IEEE80211_NODE_QOS ? "QoS " : "", 1076841ab66cSSepherosa Ziehau ieee80211_chan2ieee(ic, ic->ic_curchan), 1077841ab66cSSepherosa Ziehau wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); 1078841ab66cSSepherosa Ziehau 1079085ff963SMatthew Dillon ret = ieee80211_raw_output(vap, ni, m, NULL); 1080085ff963SMatthew Dillon IEEE80211_TX_UNLOCK(ic); 1081085ff963SMatthew Dillon return (ret); 1082841ab66cSSepherosa Ziehau } 1083841ab66cSSepherosa Ziehau 1084841ab66cSSepherosa Ziehau /* 1085841ab66cSSepherosa Ziehau * Assign priority to a frame based on any vlan tag assigned 1086841ab66cSSepherosa Ziehau * to the station and/or any Diffserv setting in an IP header. 1087841ab66cSSepherosa Ziehau * Finally, if an ACM policy is setup (in station mode) it's 1088841ab66cSSepherosa Ziehau * applied. 1089841ab66cSSepherosa Ziehau */ 1090841ab66cSSepherosa Ziehau int 109132176cfdSRui Paulo ieee80211_classify(struct ieee80211_node *ni, struct mbuf *m) 1092841ab66cSSepherosa Ziehau { 109332176cfdSRui Paulo const struct ether_header *eh = mtod(m, struct ether_header *); 109432176cfdSRui Paulo int v_wme_ac, d_wme_ac, ac; 1095841ab66cSSepherosa Ziehau 109632176cfdSRui Paulo /* 109732176cfdSRui Paulo * Always promote PAE/EAPOL frames to high priority. 109832176cfdSRui Paulo */ 109932176cfdSRui Paulo if (eh->ether_type == htons(ETHERTYPE_PAE)) { 110032176cfdSRui Paulo /* NB: mark so others don't need to check header */ 110132176cfdSRui Paulo m->m_flags |= M_EAPOL; 110232176cfdSRui Paulo ac = WME_AC_VO; 110332176cfdSRui Paulo goto done; 110432176cfdSRui Paulo } 110532176cfdSRui Paulo /* 110632176cfdSRui Paulo * Non-qos traffic goes to BE. 110732176cfdSRui Paulo */ 1108841ab66cSSepherosa Ziehau if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) { 1109841ab66cSSepherosa Ziehau ac = WME_AC_BE; 1110841ab66cSSepherosa Ziehau goto done; 1111841ab66cSSepherosa Ziehau } 1112841ab66cSSepherosa Ziehau 1113841ab66cSSepherosa Ziehau /* 1114841ab66cSSepherosa Ziehau * If node has a vlan tag then all traffic 1115841ab66cSSepherosa Ziehau * to it must have a matching tag. 1116841ab66cSSepherosa Ziehau */ 1117841ab66cSSepherosa Ziehau v_wme_ac = 0; 1118841ab66cSSepherosa Ziehau if (ni->ni_vlan != 0) { 111932176cfdSRui Paulo if ((m->m_flags & M_VLANTAG) == 0) { 1120841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_novlantag); 1121841ab66cSSepherosa Ziehau return 1; 1122841ab66cSSepherosa Ziehau } 1123085ff963SMatthew Dillon #if defined(__DragonFly__) 112432176cfdSRui Paulo if (EVL_VLANOFTAG(m->m_pkthdr.ether_vlantag) != 1125841ab66cSSepherosa Ziehau EVL_VLANOFTAG(ni->ni_vlan)) { 1126841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_vlanmismatch); 1127841ab66cSSepherosa Ziehau return 1; 1128841ab66cSSepherosa Ziehau } 1129085ff963SMatthew Dillon #else 1130085ff963SMatthew Dillon if (EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) != 1131085ff963SMatthew Dillon EVL_VLANOFTAG(ni->ni_vlan)) { 1132085ff963SMatthew Dillon IEEE80211_NODE_STAT(ni, tx_vlanmismatch); 1133085ff963SMatthew Dillon return 1; 1134085ff963SMatthew Dillon } 1135085ff963SMatthew Dillon #endif 1136841ab66cSSepherosa Ziehau /* map vlan priority to AC */ 113732176cfdSRui Paulo v_wme_ac = TID_TO_WME_AC(EVL_PRIOFTAG(ni->ni_vlan)); 1138841ab66cSSepherosa Ziehau } 1139841ab66cSSepherosa Ziehau 114032176cfdSRui Paulo /* XXX m_copydata may be too slow for fast path */ 1141841ab66cSSepherosa Ziehau #ifdef INET 1142841ab66cSSepherosa Ziehau if (eh->ether_type == htons(ETHERTYPE_IP)) { 114332176cfdSRui Paulo uint8_t tos; 1144841ab66cSSepherosa Ziehau /* 114532176cfdSRui Paulo * IP frame, map the DSCP bits from the TOS field. 1146841ab66cSSepherosa Ziehau */ 114732176cfdSRui Paulo /* NB: ip header may not be in first mbuf */ 114832176cfdSRui Paulo m_copydata(m, sizeof(struct ether_header) + 114932176cfdSRui Paulo offsetof(struct ip, ip_tos), sizeof(tos), &tos); 115032176cfdSRui Paulo tos >>= 5; /* NB: ECN + low 3 bits of DSCP */ 115132176cfdSRui Paulo d_wme_ac = TID_TO_WME_AC(tos); 1152841ab66cSSepherosa Ziehau } else { 1153841ab66cSSepherosa Ziehau #endif /* INET */ 115432176cfdSRui Paulo #ifdef INET6 115532176cfdSRui Paulo if (eh->ether_type == htons(ETHERTYPE_IPV6)) { 115632176cfdSRui Paulo uint32_t flow; 115732176cfdSRui Paulo uint8_t tos; 115832176cfdSRui Paulo /* 1159085ff963SMatthew Dillon * IPv6 frame, map the DSCP bits from the traffic class field. 116032176cfdSRui Paulo */ 116132176cfdSRui Paulo m_copydata(m, sizeof(struct ether_header) + 116232176cfdSRui Paulo offsetof(struct ip6_hdr, ip6_flow), sizeof(flow), 116332176cfdSRui Paulo (caddr_t) &flow); 116432176cfdSRui Paulo tos = (uint8_t)(ntohl(flow) >> 20); 116532176cfdSRui Paulo tos >>= 5; /* NB: ECN + low 3 bits of DSCP */ 116632176cfdSRui Paulo d_wme_ac = TID_TO_WME_AC(tos); 116732176cfdSRui Paulo } else { 116832176cfdSRui Paulo #endif /* INET6 */ 1169841ab66cSSepherosa Ziehau d_wme_ac = WME_AC_BE; 117032176cfdSRui Paulo #ifdef INET6 117132176cfdSRui Paulo } 117232176cfdSRui Paulo #endif 1173841ab66cSSepherosa Ziehau #ifdef INET 1174841ab66cSSepherosa Ziehau } 1175841ab66cSSepherosa Ziehau #endif 1176841ab66cSSepherosa Ziehau /* 1177841ab66cSSepherosa Ziehau * Use highest priority AC. 1178841ab66cSSepherosa Ziehau */ 1179841ab66cSSepherosa Ziehau if (v_wme_ac > d_wme_ac) 1180841ab66cSSepherosa Ziehau ac = v_wme_ac; 1181841ab66cSSepherosa Ziehau else 1182841ab66cSSepherosa Ziehau ac = d_wme_ac; 1183841ab66cSSepherosa Ziehau 1184841ab66cSSepherosa Ziehau /* 1185841ab66cSSepherosa Ziehau * Apply ACM policy. 1186841ab66cSSepherosa Ziehau */ 118732176cfdSRui Paulo if (ni->ni_vap->iv_opmode == IEEE80211_M_STA) { 1188841ab66cSSepherosa Ziehau static const int acmap[4] = { 1189841ab66cSSepherosa Ziehau WME_AC_BK, /* WME_AC_BE */ 1190841ab66cSSepherosa Ziehau WME_AC_BK, /* WME_AC_BK */ 1191841ab66cSSepherosa Ziehau WME_AC_BE, /* WME_AC_VI */ 1192841ab66cSSepherosa Ziehau WME_AC_VI, /* WME_AC_VO */ 1193841ab66cSSepherosa Ziehau }; 119432176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic; 119532176cfdSRui Paulo 1196841ab66cSSepherosa Ziehau while (ac != WME_AC_BK && 1197841ab66cSSepherosa Ziehau ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm) 1198841ab66cSSepherosa Ziehau ac = acmap[ac]; 1199841ab66cSSepherosa Ziehau } 1200841ab66cSSepherosa Ziehau done: 1201841ab66cSSepherosa Ziehau M_WME_SETAC(m, ac); 1202841ab66cSSepherosa Ziehau return 0; 1203841ab66cSSepherosa Ziehau } 1204841ab66cSSepherosa Ziehau 1205841ab66cSSepherosa Ziehau /* 1206841ab66cSSepherosa Ziehau * Insure there is sufficient contiguous space to encapsulate the 1207841ab66cSSepherosa Ziehau * 802.11 data frame. If room isn't already there, arrange for it. 1208841ab66cSSepherosa Ziehau * Drivers and cipher modules assume we have done the necessary work 1209841ab66cSSepherosa Ziehau * and fail rudely if they don't find the space they need. 1210841ab66cSSepherosa Ziehau */ 121132176cfdSRui Paulo struct mbuf * 121232176cfdSRui Paulo ieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize, 1213841ab66cSSepherosa Ziehau struct ieee80211_key *key, struct mbuf *m) 1214841ab66cSSepherosa Ziehau { 1215841ab66cSSepherosa Ziehau #define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) 121632176cfdSRui Paulo int needed_space = vap->iv_ic->ic_headroom + hdrsize; 1217841ab66cSSepherosa Ziehau 1218841ab66cSSepherosa Ziehau if (key != NULL) { 1219841ab66cSSepherosa Ziehau /* XXX belongs in crypto code? */ 1220841ab66cSSepherosa Ziehau needed_space += key->wk_cipher->ic_header; 1221841ab66cSSepherosa Ziehau /* XXX frags */ 1222841ab66cSSepherosa Ziehau /* 1223841ab66cSSepherosa Ziehau * When crypto is being done in the host we must insure 1224841ab66cSSepherosa Ziehau * the data are writable for the cipher routines; clone 1225841ab66cSSepherosa Ziehau * a writable mbuf chain. 1226841ab66cSSepherosa Ziehau * XXX handle SWMIC specially 1227841ab66cSSepherosa Ziehau */ 122832176cfdSRui Paulo if (key->wk_flags & (IEEE80211_KEY_SWENCRYPT|IEEE80211_KEY_SWENMIC)) { 1229b5523eacSSascha Wildner m = m_unshare(m, M_NOWAIT); 1230841ab66cSSepherosa Ziehau if (m == NULL) { 123132176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 1232841ab66cSSepherosa Ziehau "%s: cannot get writable mbuf\n", __func__); 123332176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++; /* XXX new stat */ 1234841ab66cSSepherosa Ziehau return NULL; 1235841ab66cSSepherosa Ziehau } 1236841ab66cSSepherosa Ziehau } 1237841ab66cSSepherosa Ziehau } 1238841ab66cSSepherosa Ziehau /* 1239841ab66cSSepherosa Ziehau * We know we are called just before stripping an Ethernet 1240841ab66cSSepherosa Ziehau * header and prepending an LLC header. This means we know 1241841ab66cSSepherosa Ziehau * there will be 1242841ab66cSSepherosa Ziehau * sizeof(struct ether_header) - sizeof(struct llc) 1243841ab66cSSepherosa Ziehau * bytes recovered to which we need additional space for the 1244841ab66cSSepherosa Ziehau * 802.11 header and any crypto header. 1245841ab66cSSepherosa Ziehau */ 1246841ab66cSSepherosa Ziehau /* XXX check trailing space and copy instead? */ 1247841ab66cSSepherosa Ziehau if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) { 1248b5523eacSSascha Wildner struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type); 1249841ab66cSSepherosa Ziehau if (n == NULL) { 125032176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, 1251841ab66cSSepherosa Ziehau "%s: cannot expand storage\n", __func__); 125232176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++; 1253841ab66cSSepherosa Ziehau m_freem(m); 1254841ab66cSSepherosa Ziehau return NULL; 1255841ab66cSSepherosa Ziehau } 1256085ff963SMatthew Dillon #if defined(__DragonFly__) 1257841ab66cSSepherosa Ziehau KASSERT(needed_space <= MHLEN, 1258085ff963SMatthew Dillon ("not enough room, need %u got %zd\n", needed_space, MHLEN)); 1259085ff963SMatthew Dillon #else 1260085ff963SMatthew Dillon KASSERT(needed_space <= MHLEN, 1261085ff963SMatthew Dillon ("not enough room, need %u got %d\n", needed_space, MHLEN)); 1262085ff963SMatthew Dillon #endif 1263841ab66cSSepherosa Ziehau /* 1264841ab66cSSepherosa Ziehau * Setup new mbuf to have leading space to prepend the 1265841ab66cSSepherosa Ziehau * 802.11 header and any crypto header bits that are 1266841ab66cSSepherosa Ziehau * required (the latter are added when the driver calls 1267841ab66cSSepherosa Ziehau * back to ieee80211_crypto_encap to do crypto encapsulation). 1268841ab66cSSepherosa Ziehau */ 1269841ab66cSSepherosa Ziehau /* NB: must be first 'cuz it clobbers m_data */ 1270841ab66cSSepherosa Ziehau m_move_pkthdr(n, m); 1271841ab66cSSepherosa Ziehau n->m_len = 0; /* NB: m_gethdr does not set */ 1272841ab66cSSepherosa Ziehau n->m_data += needed_space; 1273841ab66cSSepherosa Ziehau /* 1274841ab66cSSepherosa Ziehau * Pull up Ethernet header to create the expected layout. 1275841ab66cSSepherosa Ziehau * We could use m_pullup but that's overkill (i.e. we don't 1276841ab66cSSepherosa Ziehau * need the actual data) and it cannot fail so do it inline 1277841ab66cSSepherosa Ziehau * for speed. 1278841ab66cSSepherosa Ziehau */ 1279841ab66cSSepherosa Ziehau /* NB: struct ether_header is known to be contiguous */ 1280841ab66cSSepherosa Ziehau n->m_len += sizeof(struct ether_header); 1281841ab66cSSepherosa Ziehau m->m_len -= sizeof(struct ether_header); 1282841ab66cSSepherosa Ziehau m->m_data += sizeof(struct ether_header); 1283841ab66cSSepherosa Ziehau /* 1284841ab66cSSepherosa Ziehau * Replace the head of the chain. 1285841ab66cSSepherosa Ziehau */ 1286841ab66cSSepherosa Ziehau n->m_next = m; 1287841ab66cSSepherosa Ziehau m = n; 1288841ab66cSSepherosa Ziehau } 1289841ab66cSSepherosa Ziehau return m; 1290841ab66cSSepherosa Ziehau #undef TO_BE_RECLAIMED 1291841ab66cSSepherosa Ziehau } 1292841ab66cSSepherosa Ziehau 1293841ab66cSSepherosa Ziehau /* 1294841ab66cSSepherosa Ziehau * Return the transmit key to use in sending a unicast frame. 1295841ab66cSSepherosa Ziehau * If a unicast key is set we use that. When no unicast key is set 1296841ab66cSSepherosa Ziehau * we fall back to the default transmit key. 1297841ab66cSSepherosa Ziehau */ 1298841ab66cSSepherosa Ziehau static __inline struct ieee80211_key * 129932176cfdSRui Paulo ieee80211_crypto_getucastkey(struct ieee80211vap *vap, 130032176cfdSRui Paulo struct ieee80211_node *ni) 1301841ab66cSSepherosa Ziehau { 130232176cfdSRui Paulo if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) { 130332176cfdSRui Paulo if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || 130432176cfdSRui Paulo IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) 1305841ab66cSSepherosa Ziehau return NULL; 130632176cfdSRui Paulo return &vap->iv_nw_keys[vap->iv_def_txkey]; 1307841ab66cSSepherosa Ziehau } else { 1308841ab66cSSepherosa Ziehau return &ni->ni_ucastkey; 1309841ab66cSSepherosa Ziehau } 1310841ab66cSSepherosa Ziehau } 1311841ab66cSSepherosa Ziehau 1312841ab66cSSepherosa Ziehau /* 1313841ab66cSSepherosa Ziehau * Return the transmit key to use in sending a multicast frame. 1314841ab66cSSepherosa Ziehau * Multicast traffic always uses the group key which is installed as 1315841ab66cSSepherosa Ziehau * the default tx key. 1316841ab66cSSepherosa Ziehau */ 1317841ab66cSSepherosa Ziehau static __inline struct ieee80211_key * 131832176cfdSRui Paulo ieee80211_crypto_getmcastkey(struct ieee80211vap *vap, 131932176cfdSRui Paulo struct ieee80211_node *ni) 1320841ab66cSSepherosa Ziehau { 132132176cfdSRui Paulo if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || 132232176cfdSRui Paulo IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) 1323841ab66cSSepherosa Ziehau return NULL; 132432176cfdSRui Paulo return &vap->iv_nw_keys[vap->iv_def_txkey]; 1325841ab66cSSepherosa Ziehau } 1326841ab66cSSepherosa Ziehau 1327841ab66cSSepherosa Ziehau /* 1328841ab66cSSepherosa Ziehau * Encapsulate an outbound data frame. The mbuf chain is updated. 1329841ab66cSSepherosa Ziehau * If an error is encountered NULL is returned. The caller is required 1330841ab66cSSepherosa Ziehau * to provide a node reference and pullup the ethernet header in the 1331841ab66cSSepherosa Ziehau * first mbuf. 133232176cfdSRui Paulo * 133332176cfdSRui Paulo * NB: Packet is assumed to be processed by ieee80211_classify which 133432176cfdSRui Paulo * marked EAPOL frames w/ M_EAPOL. 1335f186073cSJoerg Sonnenberger */ 1336f186073cSJoerg Sonnenberger struct mbuf * 133732176cfdSRui Paulo ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, 133832176cfdSRui Paulo struct mbuf *m) 1339f186073cSJoerg Sonnenberger { 134032176cfdSRui Paulo #define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh)) 1341085ff963SMatthew Dillon #define MC01(mc) ((struct ieee80211_meshcntl_ae01 *)mc) 134232176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic; 134332176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 134432176cfdSRui Paulo struct ieee80211_mesh_state *ms = vap->iv_mesh; 134532176cfdSRui Paulo struct ieee80211_meshcntl_ae10 *mc; 1346085ff963SMatthew Dillon struct ieee80211_mesh_route *rt = NULL; 1347085ff963SMatthew Dillon int dir = -1; 134832176cfdSRui Paulo #endif 1349f186073cSJoerg Sonnenberger struct ether_header eh; 1350f186073cSJoerg Sonnenberger struct ieee80211_frame *wh; 1351841ab66cSSepherosa Ziehau struct ieee80211_key *key; 1352f186073cSJoerg Sonnenberger struct llc *llc; 135332176cfdSRui Paulo int hdrsize, hdrspace, datalen, addqos, txfrag, is4addr; 135432176cfdSRui Paulo ieee80211_seq seqno; 135532176cfdSRui Paulo int meshhdrsize, meshae; 135632176cfdSRui Paulo uint8_t *qos; 13574f655ef5SMatthew Dillon int is_amsdu = 0; 1358f186073cSJoerg Sonnenberger 1359085ff963SMatthew Dillon IEEE80211_TX_LOCK_ASSERT(ic); 1360085ff963SMatthew Dillon 136132176cfdSRui Paulo /* 136232176cfdSRui Paulo * Copy existing Ethernet header to a safe place. The 136332176cfdSRui Paulo * rest of the code assumes it's ok to strip it when 136432176cfdSRui Paulo * reorganizing state for the final encapsulation. 136532176cfdSRui Paulo */ 1366841ab66cSSepherosa Ziehau KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); 136732176cfdSRui Paulo ETHER_HEADER_COPY(&eh, mtod(m, caddr_t)); 1368f186073cSJoerg Sonnenberger 1369841ab66cSSepherosa Ziehau /* 1370841ab66cSSepherosa Ziehau * Insure space for additional headers. First identify 1371841ab66cSSepherosa Ziehau * transmit key to use in calculating any buffer adjustments 1372841ab66cSSepherosa Ziehau * required. This is also used below to do privacy 1373841ab66cSSepherosa Ziehau * encapsulation work. Then calculate the 802.11 header 1374841ab66cSSepherosa Ziehau * size and any padding required by the driver. 1375841ab66cSSepherosa Ziehau * 1376841ab66cSSepherosa Ziehau * Note key may be NULL if we fall back to the default 1377841ab66cSSepherosa Ziehau * transmit key and that is not set. In that case the 1378841ab66cSSepherosa Ziehau * buffer may not be expanded as needed by the cipher 1379841ab66cSSepherosa Ziehau * routines, but they will/should discard it. 1380841ab66cSSepherosa Ziehau */ 138132176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_PRIVACY) { 138232176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA || 138332176cfdSRui Paulo !IEEE80211_IS_MULTICAST(eh.ether_dhost) || 138432176cfdSRui Paulo (vap->iv_opmode == IEEE80211_M_WDS && 138532176cfdSRui Paulo (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))) 138632176cfdSRui Paulo key = ieee80211_crypto_getucastkey(vap, ni); 1387841ab66cSSepherosa Ziehau else 138832176cfdSRui Paulo key = ieee80211_crypto_getmcastkey(vap, ni); 138932176cfdSRui Paulo if (key == NULL && (m->m_flags & M_EAPOL) == 0) { 139032176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, 139132176cfdSRui Paulo eh.ether_dhost, 139232176cfdSRui Paulo "no default transmit key (%s) deftxkey %u", 139332176cfdSRui Paulo __func__, vap->iv_def_txkey); 139432176cfdSRui Paulo vap->iv_stats.is_tx_nodefkey++; 1395a92bce5eSSepherosa Ziehau goto bad; 1396841ab66cSSepherosa Ziehau } 1397841ab66cSSepherosa Ziehau } else 1398841ab66cSSepherosa Ziehau key = NULL; 1399841ab66cSSepherosa Ziehau /* 1400841ab66cSSepherosa Ziehau * XXX Some ap's don't handle QoS-encapsulated EAPOL 1401841ab66cSSepherosa Ziehau * frames so suppress use. This may be an issue if other 1402841ab66cSSepherosa Ziehau * ap's require all data frames to be QoS-encapsulated 1403841ab66cSSepherosa Ziehau * once negotiated in which case we'll need to make this 1404841ab66cSSepherosa Ziehau * configurable. 1405085ff963SMatthew Dillon * NB: mesh data frames are QoS. 1406841ab66cSSepherosa Ziehau */ 1407085ff963SMatthew Dillon addqos = ((ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) || 1408085ff963SMatthew Dillon (vap->iv_opmode == IEEE80211_M_MBSS)) && 140932176cfdSRui Paulo (m->m_flags & M_EAPOL) == 0; 1410841ab66cSSepherosa Ziehau if (addqos) 1411841ab66cSSepherosa Ziehau hdrsize = sizeof(struct ieee80211_qosframe); 1412841ab66cSSepherosa Ziehau else 1413841ab66cSSepherosa Ziehau hdrsize = sizeof(struct ieee80211_frame); 141432176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 141532176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_MBSS) { 141632176cfdSRui Paulo /* 141732176cfdSRui Paulo * Mesh data frames are encapsulated according to the 141832176cfdSRui Paulo * rules of Section 11B.8.5 (p.139 of D3.0 spec). 141932176cfdSRui Paulo * o Group Addressed data (aka multicast) originating 142032176cfdSRui Paulo * at the local sta are sent w/ 3-address format and 142132176cfdSRui Paulo * address extension mode 00 142232176cfdSRui Paulo * o Individually Addressed data (aka unicast) originating 142332176cfdSRui Paulo * at the local sta are sent w/ 4-address format and 142432176cfdSRui Paulo * address extension mode 00 142532176cfdSRui Paulo * o Group Addressed data forwarded from a non-mesh sta are 142632176cfdSRui Paulo * sent w/ 3-address format and address extension mode 01 142732176cfdSRui Paulo * o Individually Address data from another sta are sent 142832176cfdSRui Paulo * w/ 4-address format and address extension mode 10 142932176cfdSRui Paulo */ 143032176cfdSRui Paulo is4addr = 0; /* NB: don't use, disable */ 1431085ff963SMatthew Dillon if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) { 1432085ff963SMatthew Dillon rt = ieee80211_mesh_rt_find(vap, eh.ether_dhost); 1433085ff963SMatthew Dillon KASSERT(rt != NULL, ("route is NULL")); 1434085ff963SMatthew Dillon dir = IEEE80211_FC1_DIR_DSTODS; 1435085ff963SMatthew Dillon hdrsize += IEEE80211_ADDR_LEN; 1436085ff963SMatthew Dillon if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) { 1437085ff963SMatthew Dillon if (IEEE80211_ADDR_EQ(rt->rt_mesh_gate, 1438085ff963SMatthew Dillon vap->iv_myaddr)) { 1439085ff963SMatthew Dillon IEEE80211_NOTE_MAC(vap, 1440085ff963SMatthew Dillon IEEE80211_MSG_MESH, 1441085ff963SMatthew Dillon eh.ether_dhost, 1442085ff963SMatthew Dillon "%s", "trying to send to ourself"); 1443085ff963SMatthew Dillon goto bad; 1444085ff963SMatthew Dillon } 1445085ff963SMatthew Dillon meshae = IEEE80211_MESH_AE_10; 1446085ff963SMatthew Dillon meshhdrsize = 1447085ff963SMatthew Dillon sizeof(struct ieee80211_meshcntl_ae10); 144832176cfdSRui Paulo } else { 1449085ff963SMatthew Dillon meshae = IEEE80211_MESH_AE_00; 1450085ff963SMatthew Dillon meshhdrsize = 1451085ff963SMatthew Dillon sizeof(struct ieee80211_meshcntl); 1452085ff963SMatthew Dillon } 1453085ff963SMatthew Dillon } else { 1454085ff963SMatthew Dillon dir = IEEE80211_FC1_DIR_FROMDS; 1455085ff963SMatthew Dillon if (!IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) { 1456085ff963SMatthew Dillon /* proxy group */ 1457085ff963SMatthew Dillon meshae = IEEE80211_MESH_AE_01; 1458085ff963SMatthew Dillon meshhdrsize = 1459085ff963SMatthew Dillon sizeof(struct ieee80211_meshcntl_ae01); 1460085ff963SMatthew Dillon } else { 1461085ff963SMatthew Dillon /* group */ 1462085ff963SMatthew Dillon meshae = IEEE80211_MESH_AE_00; 1463085ff963SMatthew Dillon meshhdrsize = sizeof(struct ieee80211_meshcntl); 1464085ff963SMatthew Dillon } 146532176cfdSRui Paulo } 146632176cfdSRui Paulo } else { 146732176cfdSRui Paulo #endif 146832176cfdSRui Paulo /* 146932176cfdSRui Paulo * 4-address frames need to be generated for: 147032176cfdSRui Paulo * o packets sent through a WDS vap (IEEE80211_M_WDS) 147132176cfdSRui Paulo * o packets sent through a vap marked for relaying 147232176cfdSRui Paulo * (e.g. a station operating with dynamic WDS) 147332176cfdSRui Paulo */ 147432176cfdSRui Paulo is4addr = vap->iv_opmode == IEEE80211_M_WDS || 147532176cfdSRui Paulo ((vap->iv_flags_ext & IEEE80211_FEXT_4ADDR) && 147632176cfdSRui Paulo !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)); 147732176cfdSRui Paulo if (is4addr) 147832176cfdSRui Paulo hdrsize += IEEE80211_ADDR_LEN; 147932176cfdSRui Paulo meshhdrsize = meshae = 0; 148032176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 148132176cfdSRui Paulo } 148232176cfdSRui Paulo #endif 148332176cfdSRui Paulo /* 148432176cfdSRui Paulo * Honor driver DATAPAD requirement. 148532176cfdSRui Paulo */ 1486841ab66cSSepherosa Ziehau if (ic->ic_flags & IEEE80211_F_DATAPAD) 148732176cfdSRui Paulo hdrspace = roundup(hdrsize, sizeof(uint32_t)); 148832176cfdSRui Paulo else 148932176cfdSRui Paulo hdrspace = hdrsize; 149032176cfdSRui Paulo 149132176cfdSRui Paulo if (__predict_true((m->m_flags & M_FF) == 0)) { 149232176cfdSRui Paulo /* 149332176cfdSRui Paulo * Normal frame. 149432176cfdSRui Paulo */ 149532176cfdSRui Paulo m = ieee80211_mbuf_adjust(vap, hdrspace + meshhdrsize, key, m); 1496841ab66cSSepherosa Ziehau if (m == NULL) { 1497841ab66cSSepherosa Ziehau /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ 1498f186073cSJoerg Sonnenberger goto bad; 1499f186073cSJoerg Sonnenberger } 150032176cfdSRui Paulo /* NB: this could be optimized 'cuz of ieee80211_mbuf_adjust */ 1501f186073cSJoerg Sonnenberger m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); 1502f186073cSJoerg Sonnenberger llc = mtod(m, struct llc *); 1503f186073cSJoerg Sonnenberger llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; 1504f186073cSJoerg Sonnenberger llc->llc_control = LLC_UI; 1505f186073cSJoerg Sonnenberger llc->llc_snap.org_code[0] = 0; 1506f186073cSJoerg Sonnenberger llc->llc_snap.org_code[1] = 0; 1507f186073cSJoerg Sonnenberger llc->llc_snap.org_code[2] = 0; 1508f186073cSJoerg Sonnenberger llc->llc_snap.ether_type = eh.ether_type; 150932176cfdSRui Paulo } else { 151032176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 151132176cfdSRui Paulo /* 15124f655ef5SMatthew Dillon * Aggregated frame. Check if it's for AMSDU or FF. 15134f655ef5SMatthew Dillon * 15144f655ef5SMatthew Dillon * XXX TODO: IEEE80211_NODE_AMSDU* isn't implemented 15154f655ef5SMatthew Dillon * anywhere for some reason. But, since 11n requires 15164f655ef5SMatthew Dillon * AMSDU RX, we can just assume "11n" == "AMSDU". 151732176cfdSRui Paulo */ 15184f655ef5SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: called; M_FF\n", __func__); 15194f655ef5SMatthew Dillon if (ieee80211_amsdu_tx_ok(ni)) { 15204f655ef5SMatthew Dillon m = ieee80211_amsdu_encap(vap, m, hdrspace + meshhdrsize, key); 15214f655ef5SMatthew Dillon is_amsdu = 1; 15224f655ef5SMatthew Dillon } else { 152332176cfdSRui Paulo m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key); 15244f655ef5SMatthew Dillon } 152532176cfdSRui Paulo if (m == NULL) 152632176cfdSRui Paulo #endif 152732176cfdSRui Paulo goto bad; 152832176cfdSRui Paulo } 1529841ab66cSSepherosa Ziehau datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ 1530841ab66cSSepherosa Ziehau 1531b5523eacSSascha Wildner M_PREPEND(m, hdrspace + meshhdrsize, M_NOWAIT); 1532f186073cSJoerg Sonnenberger if (m == NULL) { 153332176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++; 1534f186073cSJoerg Sonnenberger goto bad; 1535f186073cSJoerg Sonnenberger } 1536f186073cSJoerg Sonnenberger wh = mtod(m, struct ieee80211_frame *); 1537f186073cSJoerg Sonnenberger wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; 1538f186073cSJoerg Sonnenberger *(uint16_t *)wh->i_dur = 0; 153932176cfdSRui Paulo qos = NULL; /* NB: quiet compiler */ 154032176cfdSRui Paulo if (is4addr) { 154132176cfdSRui Paulo wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; 154232176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); 154332176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 154432176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 154532176cfdSRui Paulo IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); 154632176cfdSRui Paulo } else switch (vap->iv_opmode) { 1547f186073cSJoerg Sonnenberger case IEEE80211_M_STA: 1548f186073cSJoerg Sonnenberger wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 1549f186073cSJoerg Sonnenberger IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); 1550f186073cSJoerg Sonnenberger IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 1551f186073cSJoerg Sonnenberger IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 1552f186073cSJoerg Sonnenberger break; 1553f186073cSJoerg Sonnenberger case IEEE80211_M_IBSS: 1554f186073cSJoerg Sonnenberger case IEEE80211_M_AHDEMO: 1555f186073cSJoerg Sonnenberger wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 1556f186073cSJoerg Sonnenberger IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 1557f186073cSJoerg Sonnenberger IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 1558841ab66cSSepherosa Ziehau /* 155932176cfdSRui Paulo * NB: always use the bssid from iv_bss as the 1560841ab66cSSepherosa Ziehau * neighbor's may be stale after an ibss merge 1561841ab66cSSepherosa Ziehau */ 156232176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_bssid); 1563f186073cSJoerg Sonnenberger break; 1564f186073cSJoerg Sonnenberger case IEEE80211_M_HOSTAP: 1565f186073cSJoerg Sonnenberger wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 1566f186073cSJoerg Sonnenberger IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 1567f186073cSJoerg Sonnenberger IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); 1568f186073cSJoerg Sonnenberger IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); 1569f186073cSJoerg Sonnenberger break; 157032176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 157132176cfdSRui Paulo case IEEE80211_M_MBSS: 157232176cfdSRui Paulo /* NB: offset by hdrspace to deal with DATAPAD */ 157332176cfdSRui Paulo mc = (struct ieee80211_meshcntl_ae10 *) 157432176cfdSRui Paulo (mtod(m, uint8_t *) + hdrspace); 1575085ff963SMatthew Dillon wh->i_fc[1] = dir; 157632176cfdSRui Paulo switch (meshae) { 1577085ff963SMatthew Dillon case IEEE80211_MESH_AE_00: /* no proxy */ 157832176cfdSRui Paulo mc->mc_flags = 0; 1579085ff963SMatthew Dillon if (dir == IEEE80211_FC1_DIR_DSTODS) { /* ucast */ 1580085ff963SMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr1, 1581085ff963SMatthew Dillon ni->ni_macaddr); 1582085ff963SMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr2, 1583085ff963SMatthew Dillon vap->iv_myaddr); 1584085ff963SMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr3, 1585085ff963SMatthew Dillon eh.ether_dhost); 1586085ff963SMatthew Dillon IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, 1587085ff963SMatthew Dillon eh.ether_shost); 1588085ff963SMatthew Dillon qos =((struct ieee80211_qosframe_addr4 *) 1589085ff963SMatthew Dillon wh)->i_qos; 1590085ff963SMatthew Dillon } else if (dir == IEEE80211_FC1_DIR_FROMDS) { 1591085ff963SMatthew Dillon /* mcast */ 1592085ff963SMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr1, 1593085ff963SMatthew Dillon eh.ether_dhost); 1594085ff963SMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr2, 1595085ff963SMatthew Dillon vap->iv_myaddr); 1596085ff963SMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr3, 1597085ff963SMatthew Dillon eh.ether_shost); 1598085ff963SMatthew Dillon qos = ((struct ieee80211_qosframe *) 1599085ff963SMatthew Dillon wh)->i_qos; 1600085ff963SMatthew Dillon } 160132176cfdSRui Paulo break; 1602085ff963SMatthew Dillon case IEEE80211_MESH_AE_01: /* mcast, proxy */ 160332176cfdSRui Paulo wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 160432176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 160532176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 160632176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_myaddr); 160732176cfdSRui Paulo mc->mc_flags = 1; 1608085ff963SMatthew Dillon IEEE80211_ADDR_COPY(MC01(mc)->mc_addr4, 1609085ff963SMatthew Dillon eh.ether_shost); 161032176cfdSRui Paulo qos = ((struct ieee80211_qosframe *) wh)->i_qos; 161132176cfdSRui Paulo break; 1612085ff963SMatthew Dillon case IEEE80211_MESH_AE_10: /* ucast, proxy */ 1613085ff963SMatthew Dillon KASSERT(rt != NULL, ("route is NULL")); 1614085ff963SMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr1, rt->rt_nexthop); 161532176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 1616085ff963SMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr3, rt->rt_mesh_gate); 161732176cfdSRui Paulo IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, vap->iv_myaddr); 1618085ff963SMatthew Dillon mc->mc_flags = IEEE80211_MESH_AE_10; 1619085ff963SMatthew Dillon IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_dhost); 1620085ff963SMatthew Dillon IEEE80211_ADDR_COPY(mc->mc_addr6, eh.ether_shost); 162132176cfdSRui Paulo qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; 162232176cfdSRui Paulo break; 162332176cfdSRui Paulo default: 162432176cfdSRui Paulo KASSERT(0, ("meshae %d", meshae)); 162532176cfdSRui Paulo break; 162632176cfdSRui Paulo } 162732176cfdSRui Paulo mc->mc_ttl = ms->ms_ttl; 162832176cfdSRui Paulo ms->ms_seq++; 16294f655ef5SMatthew Dillon le32enc(mc->mc_seq, ms->ms_seq); 163032176cfdSRui Paulo break; 163132176cfdSRui Paulo #endif 163232176cfdSRui Paulo case IEEE80211_M_WDS: /* NB: is4addr should always be true */ 163332176cfdSRui Paulo default: 1634f186073cSJoerg Sonnenberger goto bad; 1635f186073cSJoerg Sonnenberger } 1636841ab66cSSepherosa Ziehau if (m->m_flags & M_MORE_DATA) 1637841ab66cSSepherosa Ziehau wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; 1638841ab66cSSepherosa Ziehau if (addqos) { 1639841ab66cSSepherosa Ziehau int ac, tid; 1640841ab66cSSepherosa Ziehau 164132176cfdSRui Paulo if (is4addr) { 164232176cfdSRui Paulo qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; 164332176cfdSRui Paulo /* NB: mesh case handled earlier */ 164432176cfdSRui Paulo } else if (vap->iv_opmode != IEEE80211_M_MBSS) 164532176cfdSRui Paulo qos = ((struct ieee80211_qosframe *) wh)->i_qos; 1646841ab66cSSepherosa Ziehau ac = M_WME_GETAC(m); 1647841ab66cSSepherosa Ziehau /* map from access class/queue to 11e header priorty value */ 1648841ab66cSSepherosa Ziehau tid = WME_AC_TO_TID(ac); 164932176cfdSRui Paulo qos[0] = tid & IEEE80211_QOS_TID; 1650841ab66cSSepherosa Ziehau if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) 165132176cfdSRui Paulo qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; 1652085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_MESH 1653085ff963SMatthew Dillon if (vap->iv_opmode == IEEE80211_M_MBSS) 1654085ff963SMatthew Dillon qos[1] = IEEE80211_QOS_MC; 1655085ff963SMatthew Dillon else 1656085ff963SMatthew Dillon #endif 165732176cfdSRui Paulo qos[1] = 0; 165832176cfdSRui Paulo wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; 1659841ab66cSSepherosa Ziehau 16604f655ef5SMatthew Dillon /* 16614f655ef5SMatthew Dillon * If this is an A-MSDU then ensure we set the 16624f655ef5SMatthew Dillon * relevant field. 16634f655ef5SMatthew Dillon */ 16644f655ef5SMatthew Dillon if (is_amsdu) 16654f655ef5SMatthew Dillon qos[0] |= IEEE80211_QOS_AMSDU; 16664f655ef5SMatthew Dillon 166732176cfdSRui Paulo if ((m->m_flags & M_AMPDU_MPDU) == 0) { 166832176cfdSRui Paulo /* 166932176cfdSRui Paulo * NB: don't assign a sequence # to potential 167032176cfdSRui Paulo * aggregates; we expect this happens at the 167132176cfdSRui Paulo * point the frame comes off any aggregation q 167232176cfdSRui Paulo * as otherwise we may introduce holes in the 167332176cfdSRui Paulo * BA sequence space and/or make window accouting 167432176cfdSRui Paulo * more difficult. 167532176cfdSRui Paulo * 167632176cfdSRui Paulo * XXX may want to control this with a driver 167732176cfdSRui Paulo * capability; this may also change when we pull 167832176cfdSRui Paulo * aggregation up into net80211 167932176cfdSRui Paulo */ 168032176cfdSRui Paulo seqno = ni->ni_txseqs[tid]++; 1681841ab66cSSepherosa Ziehau *(uint16_t *)wh->i_seq = 168232176cfdSRui Paulo htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); 168332176cfdSRui Paulo M_SEQNO_SET(m, seqno); 1684841ab66cSSepherosa Ziehau } 168532176cfdSRui Paulo } else { 168632176cfdSRui Paulo seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; 168732176cfdSRui Paulo *(uint16_t *)wh->i_seq = 168832176cfdSRui Paulo htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); 168932176cfdSRui Paulo M_SEQNO_SET(m, seqno); 16904f655ef5SMatthew Dillon 16914f655ef5SMatthew Dillon /* 16924f655ef5SMatthew Dillon * XXX TODO: we shouldn't allow EAPOL, etc that would 16934f655ef5SMatthew Dillon * be forced to be non-QoS traffic to be A-MSDU encapsulated. 16944f655ef5SMatthew Dillon */ 16954f655ef5SMatthew Dillon if (is_amsdu) 16964f655ef5SMatthew Dillon kprintf("%s: XXX ERROR: is_amsdu set; not QoS!\n", 16974f655ef5SMatthew Dillon __func__); 169832176cfdSRui Paulo } 169932176cfdSRui Paulo 170032176cfdSRui Paulo 170132176cfdSRui Paulo /* check if xmit fragmentation is required */ 170232176cfdSRui Paulo txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold && 170332176cfdSRui Paulo !IEEE80211_IS_MULTICAST(wh->i_addr1) && 170432176cfdSRui Paulo (vap->iv_caps & IEEE80211_C_TXFRAG) && 170532176cfdSRui Paulo (m->m_flags & (M_FF | M_AMPDU_MPDU)) == 0); 1706841ab66cSSepherosa Ziehau if (key != NULL) { 1707841ab66cSSepherosa Ziehau /* 1708841ab66cSSepherosa Ziehau * IEEE 802.1X: send EAPOL frames always in the clear. 1709841ab66cSSepherosa Ziehau * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set. 1710841ab66cSSepherosa Ziehau */ 171132176cfdSRui Paulo if ((m->m_flags & M_EAPOL) == 0 || 171232176cfdSRui Paulo ((vap->iv_flags & IEEE80211_F_WPA) && 171332176cfdSRui Paulo (vap->iv_opmode == IEEE80211_M_STA ? 171432176cfdSRui Paulo !IEEE80211_KEY_UNDEFINED(key) : 171532176cfdSRui Paulo !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) { 1716085ff963SMatthew Dillon wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; 171732176cfdSRui Paulo if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) { 171832176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, 171932176cfdSRui Paulo eh.ether_dhost, 172032176cfdSRui Paulo "%s", "enmic failed, discard frame"); 172132176cfdSRui Paulo vap->iv_stats.is_crypto_enmicfail++; 1722841ab66cSSepherosa Ziehau goto bad; 1723841ab66cSSepherosa Ziehau } 1724841ab66cSSepherosa Ziehau } 1725841ab66cSSepherosa Ziehau } 172632176cfdSRui Paulo if (txfrag && !ieee80211_fragment(vap, m, hdrsize, 172732176cfdSRui Paulo key != NULL ? key->wk_cipher->ic_header : 0, vap->iv_fragthreshold)) 172832176cfdSRui Paulo goto bad; 172932176cfdSRui Paulo 173032176cfdSRui Paulo m->m_flags |= M_ENCAP; /* mark encapsulated */ 1731841ab66cSSepherosa Ziehau 1732841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_data); 173332176cfdSRui Paulo if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 1734fb134238SSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_mcast); 173532176cfdSRui Paulo m->m_flags |= M_MCAST; 173632176cfdSRui Paulo } else 1737fb134238SSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_ucast); 1738841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); 1739841ab66cSSepherosa Ziehau 1740f186073cSJoerg Sonnenberger return m; 1741f186073cSJoerg Sonnenberger bad: 1742f186073cSJoerg Sonnenberger if (m != NULL) 1743f186073cSJoerg Sonnenberger m_freem(m); 1744f186073cSJoerg Sonnenberger return NULL; 174532176cfdSRui Paulo #undef WH4 1746085ff963SMatthew Dillon #undef MC01 174732176cfdSRui Paulo } 174832176cfdSRui Paulo 17494f655ef5SMatthew Dillon void 17504f655ef5SMatthew Dillon ieee80211_free_mbuf(struct mbuf *m) 17514f655ef5SMatthew Dillon { 17524f655ef5SMatthew Dillon struct mbuf *next; 17534f655ef5SMatthew Dillon 17544f655ef5SMatthew Dillon if (m == NULL) 17554f655ef5SMatthew Dillon return; 17564f655ef5SMatthew Dillon 17574f655ef5SMatthew Dillon do { 17584f655ef5SMatthew Dillon next = m->m_nextpkt; 17594f655ef5SMatthew Dillon m->m_nextpkt = NULL; 17604f655ef5SMatthew Dillon m_freem(m); 17614f655ef5SMatthew Dillon } while ((m = next) != NULL); 17624f655ef5SMatthew Dillon } 17634f655ef5SMatthew Dillon 176432176cfdSRui Paulo /* 176532176cfdSRui Paulo * Fragment the frame according to the specified mtu. 176632176cfdSRui Paulo * The size of the 802.11 header (w/o padding) is provided 176732176cfdSRui Paulo * so we don't need to recalculate it. We create a new 176832176cfdSRui Paulo * mbuf for each fragment and chain it through m_nextpkt; 176932176cfdSRui Paulo * we might be able to optimize this by reusing the original 177032176cfdSRui Paulo * packet's mbufs but that is significantly more complicated. 177132176cfdSRui Paulo */ 177232176cfdSRui Paulo static int 177332176cfdSRui Paulo ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, 177432176cfdSRui Paulo u_int hdrsize, u_int ciphdrsize, u_int mtu) 177532176cfdSRui Paulo { 1776085ff963SMatthew Dillon struct ieee80211com *ic = vap->iv_ic; 177732176cfdSRui Paulo struct ieee80211_frame *wh, *whf; 17784f655ef5SMatthew Dillon struct mbuf *m, *prev; 177932176cfdSRui Paulo u_int totalhdrsize, fragno, fragsize, off, remainder, payload; 1780085ff963SMatthew Dillon u_int hdrspace; 178132176cfdSRui Paulo 178232176cfdSRui Paulo KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?")); 178332176cfdSRui Paulo KASSERT(m0->m_pkthdr.len > mtu, 178432176cfdSRui Paulo ("pktlen %u mtu %u", m0->m_pkthdr.len, mtu)); 178532176cfdSRui Paulo 1786085ff963SMatthew Dillon /* 1787085ff963SMatthew Dillon * Honor driver DATAPAD requirement. 1788085ff963SMatthew Dillon */ 1789085ff963SMatthew Dillon if (ic->ic_flags & IEEE80211_F_DATAPAD) 1790085ff963SMatthew Dillon hdrspace = roundup(hdrsize, sizeof(uint32_t)); 1791085ff963SMatthew Dillon else 1792085ff963SMatthew Dillon hdrspace = hdrsize; 1793085ff963SMatthew Dillon 179432176cfdSRui Paulo wh = mtod(m0, struct ieee80211_frame *); 179532176cfdSRui Paulo /* NB: mark the first frag; it will be propagated below */ 179632176cfdSRui Paulo wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG; 1797085ff963SMatthew Dillon totalhdrsize = hdrspace + ciphdrsize; 179832176cfdSRui Paulo fragno = 1; 179932176cfdSRui Paulo off = mtu - ciphdrsize; 180032176cfdSRui Paulo remainder = m0->m_pkthdr.len - off; 180132176cfdSRui Paulo prev = m0; 180232176cfdSRui Paulo do { 180332176cfdSRui Paulo fragsize = totalhdrsize + remainder; 180432176cfdSRui Paulo if (fragsize > mtu) 180532176cfdSRui Paulo fragsize = mtu; 180632176cfdSRui Paulo /* XXX fragsize can be >2048! */ 180732176cfdSRui Paulo KASSERT(fragsize < MCLBYTES, 180832176cfdSRui Paulo ("fragment size %u too big!", fragsize)); 180932176cfdSRui Paulo if (fragsize > MHLEN) 1810b5523eacSSascha Wildner m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 181132176cfdSRui Paulo else 1812b5523eacSSascha Wildner m = m_gethdr(M_NOWAIT, MT_DATA); 181332176cfdSRui Paulo if (m == NULL) 181432176cfdSRui Paulo goto bad; 181532176cfdSRui Paulo /* leave room to prepend any cipher header */ 181632176cfdSRui Paulo m_align(m, fragsize - ciphdrsize); 181732176cfdSRui Paulo 181832176cfdSRui Paulo /* 181932176cfdSRui Paulo * Form the header in the fragment. Note that since 182032176cfdSRui Paulo * we mark the first fragment with the MORE_FRAG bit 182132176cfdSRui Paulo * it automatically is propagated to each fragment; we 182232176cfdSRui Paulo * need only clear it on the last fragment (done below). 1823085ff963SMatthew Dillon * NB: frag 1+ dont have Mesh Control field present. 182432176cfdSRui Paulo */ 182532176cfdSRui Paulo whf = mtod(m, struct ieee80211_frame *); 182632176cfdSRui Paulo memcpy(whf, wh, hdrsize); 1827085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_MESH 1828085ff963SMatthew Dillon if (vap->iv_opmode == IEEE80211_M_MBSS) { 1829085ff963SMatthew Dillon if (IEEE80211_IS_DSTODS(wh)) 1830085ff963SMatthew Dillon ((struct ieee80211_qosframe_addr4 *) 1831085ff963SMatthew Dillon whf)->i_qos[1] &= ~IEEE80211_QOS_MC; 1832085ff963SMatthew Dillon else 1833085ff963SMatthew Dillon ((struct ieee80211_qosframe *) 1834085ff963SMatthew Dillon whf)->i_qos[1] &= ~IEEE80211_QOS_MC; 1835085ff963SMatthew Dillon } 1836085ff963SMatthew Dillon #endif 183732176cfdSRui Paulo *(uint16_t *)&whf->i_seq[0] |= htole16( 183832176cfdSRui Paulo (fragno & IEEE80211_SEQ_FRAG_MASK) << 183932176cfdSRui Paulo IEEE80211_SEQ_FRAG_SHIFT); 184032176cfdSRui Paulo fragno++; 184132176cfdSRui Paulo 184232176cfdSRui Paulo payload = fragsize - totalhdrsize; 184332176cfdSRui Paulo /* NB: destination is known to be contiguous */ 1844085ff963SMatthew Dillon 1845085ff963SMatthew Dillon m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrspace); 1846085ff963SMatthew Dillon m->m_len = hdrspace + payload; 1847085ff963SMatthew Dillon m->m_pkthdr.len = hdrspace + payload; 184832176cfdSRui Paulo m->m_flags |= M_FRAG; 184932176cfdSRui Paulo 185032176cfdSRui Paulo /* chain up the fragment */ 185132176cfdSRui Paulo prev->m_nextpkt = m; 185232176cfdSRui Paulo prev = m; 185332176cfdSRui Paulo 185432176cfdSRui Paulo /* deduct fragment just formed */ 185532176cfdSRui Paulo remainder -= payload; 185632176cfdSRui Paulo off += payload; 185732176cfdSRui Paulo } while (remainder != 0); 185832176cfdSRui Paulo 185932176cfdSRui Paulo /* set the last fragment */ 186032176cfdSRui Paulo m->m_flags |= M_LASTFRAG; 186132176cfdSRui Paulo whf->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG; 186232176cfdSRui Paulo 186332176cfdSRui Paulo /* strip first mbuf now that everything has been copied */ 186432176cfdSRui Paulo m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize))); 186532176cfdSRui Paulo m0->m_flags |= M_FIRSTFRAG | M_FRAG; 186632176cfdSRui Paulo 186732176cfdSRui Paulo vap->iv_stats.is_tx_fragframes++; 186832176cfdSRui Paulo vap->iv_stats.is_tx_frags += fragno-1; 186932176cfdSRui Paulo 187032176cfdSRui Paulo return 1; 187132176cfdSRui Paulo bad: 187232176cfdSRui Paulo /* reclaim fragments but leave original frame for caller to free */ 18734f655ef5SMatthew Dillon ieee80211_free_mbuf(m0->m_nextpkt); 187432176cfdSRui Paulo m0->m_nextpkt = NULL; 187532176cfdSRui Paulo return 0; 1876f186073cSJoerg Sonnenberger } 1877f186073cSJoerg Sonnenberger 1878f186073cSJoerg Sonnenberger /* 1879f186073cSJoerg Sonnenberger * Add a supported rates element id to a frame. 1880f186073cSJoerg Sonnenberger */ 1881f186073cSJoerg Sonnenberger uint8_t * 1882f186073cSJoerg Sonnenberger ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs) 1883f186073cSJoerg Sonnenberger { 1884f186073cSJoerg Sonnenberger int nrates; 1885f186073cSJoerg Sonnenberger 1886f186073cSJoerg Sonnenberger *frm++ = IEEE80211_ELEMID_RATES; 1887f186073cSJoerg Sonnenberger nrates = rs->rs_nrates; 1888f186073cSJoerg Sonnenberger if (nrates > IEEE80211_RATE_SIZE) 1889f186073cSJoerg Sonnenberger nrates = IEEE80211_RATE_SIZE; 1890f186073cSJoerg Sonnenberger *frm++ = nrates; 1891f186073cSJoerg Sonnenberger memcpy(frm, rs->rs_rates, nrates); 1892f186073cSJoerg Sonnenberger return frm + nrates; 1893f186073cSJoerg Sonnenberger } 1894f186073cSJoerg Sonnenberger 1895f186073cSJoerg Sonnenberger /* 1896f186073cSJoerg Sonnenberger * Add an extended supported rates element id to a frame. 1897f186073cSJoerg Sonnenberger */ 1898f186073cSJoerg Sonnenberger uint8_t * 1899f186073cSJoerg Sonnenberger ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) 1900f186073cSJoerg Sonnenberger { 1901f186073cSJoerg Sonnenberger /* 1902f186073cSJoerg Sonnenberger * Add an extended supported rates element if operating in 11g mode. 1903f186073cSJoerg Sonnenberger */ 1904f186073cSJoerg Sonnenberger if (rs->rs_nrates > IEEE80211_RATE_SIZE) { 1905f186073cSJoerg Sonnenberger int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; 1906f186073cSJoerg Sonnenberger *frm++ = IEEE80211_ELEMID_XRATES; 1907f186073cSJoerg Sonnenberger *frm++ = nrates; 1908f186073cSJoerg Sonnenberger memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); 1909f186073cSJoerg Sonnenberger frm += nrates; 1910f186073cSJoerg Sonnenberger } 1911f186073cSJoerg Sonnenberger return frm; 1912f186073cSJoerg Sonnenberger } 1913f186073cSJoerg Sonnenberger 1914f186073cSJoerg Sonnenberger /* 191532176cfdSRui Paulo * Add an ssid element to a frame. 1916f186073cSJoerg Sonnenberger */ 1917e218318cSMatthew Dillon uint8_t * 1918f186073cSJoerg Sonnenberger ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) 1919f186073cSJoerg Sonnenberger { 1920f186073cSJoerg Sonnenberger *frm++ = IEEE80211_ELEMID_SSID; 1921f186073cSJoerg Sonnenberger *frm++ = len; 1922f186073cSJoerg Sonnenberger memcpy(frm, ssid, len); 1923f186073cSJoerg Sonnenberger return frm + len; 1924f186073cSJoerg Sonnenberger } 1925f186073cSJoerg Sonnenberger 1926841ab66cSSepherosa Ziehau /* 1927841ab66cSSepherosa Ziehau * Add an erp element to a frame. 1928841ab66cSSepherosa Ziehau */ 1929841ab66cSSepherosa Ziehau static uint8_t * 1930841ab66cSSepherosa Ziehau ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic) 1931f186073cSJoerg Sonnenberger { 1932841ab66cSSepherosa Ziehau uint8_t erp; 1933f186073cSJoerg Sonnenberger 1934841ab66cSSepherosa Ziehau *frm++ = IEEE80211_ELEMID_ERP; 1935841ab66cSSepherosa Ziehau *frm++ = 1; 1936841ab66cSSepherosa Ziehau erp = 0; 1937841ab66cSSepherosa Ziehau if (ic->ic_nonerpsta != 0) 1938841ab66cSSepherosa Ziehau erp |= IEEE80211_ERP_NON_ERP_PRESENT; 1939841ab66cSSepherosa Ziehau if (ic->ic_flags & IEEE80211_F_USEPROT) 1940841ab66cSSepherosa Ziehau erp |= IEEE80211_ERP_USE_PROTECTION; 1941841ab66cSSepherosa Ziehau if (ic->ic_flags & IEEE80211_F_USEBARKER) 1942841ab66cSSepherosa Ziehau erp |= IEEE80211_ERP_LONG_PREAMBLE; 1943841ab66cSSepherosa Ziehau *frm++ = erp; 1944841ab66cSSepherosa Ziehau return frm; 1945841ab66cSSepherosa Ziehau } 1946841ab66cSSepherosa Ziehau 1947841ab66cSSepherosa Ziehau /* 194832176cfdSRui Paulo * Add a CFParams element to a frame. 1949841ab66cSSepherosa Ziehau */ 1950841ab66cSSepherosa Ziehau static uint8_t * 195132176cfdSRui Paulo ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic) 1952841ab66cSSepherosa Ziehau { 195332176cfdSRui Paulo #define ADDSHORT(frm, v) do { \ 19544f655ef5SMatthew Dillon le16enc(frm, v); \ 195532176cfdSRui Paulo frm += 2; \ 195632176cfdSRui Paulo } while (0) 195732176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_CFPARMS; 195832176cfdSRui Paulo *frm++ = 6; 195932176cfdSRui Paulo *frm++ = 0; /* CFP count */ 196032176cfdSRui Paulo *frm++ = 2; /* CFP period */ 196132176cfdSRui Paulo ADDSHORT(frm, 0); /* CFP MaxDuration (TU) */ 196232176cfdSRui Paulo ADDSHORT(frm, 0); /* CFP CurRemaining (TU) */ 1963841ab66cSSepherosa Ziehau return frm; 196432176cfdSRui Paulo #undef ADDSHORT 196532176cfdSRui Paulo } 196632176cfdSRui Paulo 196732176cfdSRui Paulo static __inline uint8_t * 196832176cfdSRui Paulo add_appie(uint8_t *frm, const struct ieee80211_appie *ie) 196932176cfdSRui Paulo { 197032176cfdSRui Paulo memcpy(frm, ie->ie_data, ie->ie_len); 197132176cfdSRui Paulo return frm + ie->ie_len; 197232176cfdSRui Paulo } 197332176cfdSRui Paulo 197432176cfdSRui Paulo static __inline uint8_t * 197532176cfdSRui Paulo add_ie(uint8_t *frm, const uint8_t *ie) 197632176cfdSRui Paulo { 197732176cfdSRui Paulo memcpy(frm, ie, 2 + ie[1]); 197832176cfdSRui Paulo return frm + 2 + ie[1]; 1979841ab66cSSepherosa Ziehau } 1980841ab66cSSepherosa Ziehau 1981841ab66cSSepherosa Ziehau #define WME_OUI_BYTES 0x00, 0x50, 0xf2 1982841ab66cSSepherosa Ziehau /* 1983841ab66cSSepherosa Ziehau * Add a WME information element to a frame. 1984841ab66cSSepherosa Ziehau */ 19854f655ef5SMatthew Dillon uint8_t * 1986841ab66cSSepherosa Ziehau ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme) 1987841ab66cSSepherosa Ziehau { 1988841ab66cSSepherosa Ziehau static const struct ieee80211_wme_info info = { 1989841ab66cSSepherosa Ziehau .wme_id = IEEE80211_ELEMID_VENDOR, 1990841ab66cSSepherosa Ziehau .wme_len = sizeof(struct ieee80211_wme_info) - 2, 1991841ab66cSSepherosa Ziehau .wme_oui = { WME_OUI_BYTES }, 1992841ab66cSSepherosa Ziehau .wme_type = WME_OUI_TYPE, 1993841ab66cSSepherosa Ziehau .wme_subtype = WME_INFO_OUI_SUBTYPE, 1994841ab66cSSepherosa Ziehau .wme_version = WME_VERSION, 1995841ab66cSSepherosa Ziehau .wme_info = 0, 1996841ab66cSSepherosa Ziehau }; 1997841ab66cSSepherosa Ziehau memcpy(frm, &info, sizeof(info)); 1998841ab66cSSepherosa Ziehau return frm + sizeof(info); 1999841ab66cSSepherosa Ziehau } 2000841ab66cSSepherosa Ziehau 2001841ab66cSSepherosa Ziehau /* 2002841ab66cSSepherosa Ziehau * Add a WME parameters element to a frame. 2003841ab66cSSepherosa Ziehau */ 2004841ab66cSSepherosa Ziehau static uint8_t * 2005841ab66cSSepherosa Ziehau ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme) 2006841ab66cSSepherosa Ziehau { 2007841ab66cSSepherosa Ziehau #define SM(_v, _f) (((_v) << _f##_S) & _f) 2008841ab66cSSepherosa Ziehau #define ADDSHORT(frm, v) do { \ 20094f655ef5SMatthew Dillon le16enc(frm, v); \ 2010841ab66cSSepherosa Ziehau frm += 2; \ 2011841ab66cSSepherosa Ziehau } while (0) 2012841ab66cSSepherosa Ziehau /* NB: this works 'cuz a param has an info at the front */ 2013841ab66cSSepherosa Ziehau static const struct ieee80211_wme_info param = { 2014841ab66cSSepherosa Ziehau .wme_id = IEEE80211_ELEMID_VENDOR, 2015841ab66cSSepherosa Ziehau .wme_len = sizeof(struct ieee80211_wme_param) - 2, 2016841ab66cSSepherosa Ziehau .wme_oui = { WME_OUI_BYTES }, 2017841ab66cSSepherosa Ziehau .wme_type = WME_OUI_TYPE, 2018841ab66cSSepherosa Ziehau .wme_subtype = WME_PARAM_OUI_SUBTYPE, 2019841ab66cSSepherosa Ziehau .wme_version = WME_VERSION, 2020841ab66cSSepherosa Ziehau }; 2021841ab66cSSepherosa Ziehau int i; 2022841ab66cSSepherosa Ziehau 2023841ab66cSSepherosa Ziehau memcpy(frm, ¶m, sizeof(param)); 2024841ab66cSSepherosa Ziehau frm += __offsetof(struct ieee80211_wme_info, wme_info); 2025841ab66cSSepherosa Ziehau *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */ 2026841ab66cSSepherosa Ziehau *frm++ = 0; /* reserved field */ 2027841ab66cSSepherosa Ziehau for (i = 0; i < WME_NUM_AC; i++) { 2028841ab66cSSepherosa Ziehau const struct wmeParams *ac = 2029841ab66cSSepherosa Ziehau &wme->wme_bssChanParams.cap_wmeParams[i]; 2030841ab66cSSepherosa Ziehau *frm++ = SM(i, WME_PARAM_ACI) 2031841ab66cSSepherosa Ziehau | SM(ac->wmep_acm, WME_PARAM_ACM) 2032841ab66cSSepherosa Ziehau | SM(ac->wmep_aifsn, WME_PARAM_AIFSN) 2033841ab66cSSepherosa Ziehau ; 2034841ab66cSSepherosa Ziehau *frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX) 2035841ab66cSSepherosa Ziehau | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN) 2036841ab66cSSepherosa Ziehau ; 2037841ab66cSSepherosa Ziehau ADDSHORT(frm, ac->wmep_txopLimit); 2038841ab66cSSepherosa Ziehau } 2039841ab66cSSepherosa Ziehau return frm; 2040841ab66cSSepherosa Ziehau #undef SM 2041841ab66cSSepherosa Ziehau #undef ADDSHORT 2042841ab66cSSepherosa Ziehau } 2043841ab66cSSepherosa Ziehau #undef WME_OUI_BYTES 2044841ab66cSSepherosa Ziehau 2045841ab66cSSepherosa Ziehau /* 204632176cfdSRui Paulo * Add an 11h Power Constraint element to a frame. 204732176cfdSRui Paulo */ 204832176cfdSRui Paulo static uint8_t * 204932176cfdSRui Paulo ieee80211_add_powerconstraint(uint8_t *frm, struct ieee80211vap *vap) 205032176cfdSRui Paulo { 205132176cfdSRui Paulo const struct ieee80211_channel *c = vap->iv_bss->ni_chan; 205232176cfdSRui Paulo /* XXX per-vap tx power limit? */ 205332176cfdSRui Paulo int8_t limit = vap->iv_ic->ic_txpowlimit / 2; 205432176cfdSRui Paulo 205532176cfdSRui Paulo frm[0] = IEEE80211_ELEMID_PWRCNSTR; 205632176cfdSRui Paulo frm[1] = 1; 205732176cfdSRui Paulo frm[2] = c->ic_maxregpower > limit ? c->ic_maxregpower - limit : 0; 205832176cfdSRui Paulo return frm + 3; 205932176cfdSRui Paulo } 206032176cfdSRui Paulo 206132176cfdSRui Paulo /* 206232176cfdSRui Paulo * Add an 11h Power Capability element to a frame. 206332176cfdSRui Paulo */ 206432176cfdSRui Paulo static uint8_t * 206532176cfdSRui Paulo ieee80211_add_powercapability(uint8_t *frm, const struct ieee80211_channel *c) 206632176cfdSRui Paulo { 206732176cfdSRui Paulo frm[0] = IEEE80211_ELEMID_PWRCAP; 206832176cfdSRui Paulo frm[1] = 2; 206932176cfdSRui Paulo frm[2] = c->ic_minpower; 207032176cfdSRui Paulo frm[3] = c->ic_maxpower; 207132176cfdSRui Paulo return frm + 4; 207232176cfdSRui Paulo } 207332176cfdSRui Paulo 207432176cfdSRui Paulo /* 207532176cfdSRui Paulo * Add an 11h Supported Channels element to a frame. 207632176cfdSRui Paulo */ 207732176cfdSRui Paulo static uint8_t * 207832176cfdSRui Paulo ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic) 207932176cfdSRui Paulo { 208032176cfdSRui Paulo static const int ielen = 26; 208132176cfdSRui Paulo 208232176cfdSRui Paulo frm[0] = IEEE80211_ELEMID_SUPPCHAN; 208332176cfdSRui Paulo frm[1] = ielen; 208432176cfdSRui Paulo /* XXX not correct */ 208532176cfdSRui Paulo memcpy(frm+2, ic->ic_chan_avail, ielen); 208632176cfdSRui Paulo return frm + 2 + ielen; 208732176cfdSRui Paulo } 208832176cfdSRui Paulo 208932176cfdSRui Paulo /* 2090085ff963SMatthew Dillon * Add an 11h Quiet time element to a frame. 2091085ff963SMatthew Dillon */ 2092085ff963SMatthew Dillon static uint8_t * 2093085ff963SMatthew Dillon ieee80211_add_quiet(uint8_t *frm, struct ieee80211vap *vap) 2094085ff963SMatthew Dillon { 2095085ff963SMatthew Dillon struct ieee80211_quiet_ie *quiet = (struct ieee80211_quiet_ie *) frm; 2096085ff963SMatthew Dillon 2097085ff963SMatthew Dillon quiet->quiet_ie = IEEE80211_ELEMID_QUIET; 2098085ff963SMatthew Dillon quiet->len = 6; 2099085ff963SMatthew Dillon if (vap->iv_quiet_count_value == 1) 2100085ff963SMatthew Dillon vap->iv_quiet_count_value = vap->iv_quiet_count; 2101085ff963SMatthew Dillon else if (vap->iv_quiet_count_value > 1) 2102085ff963SMatthew Dillon vap->iv_quiet_count_value--; 2103085ff963SMatthew Dillon 2104085ff963SMatthew Dillon if (vap->iv_quiet_count_value == 0) { 2105085ff963SMatthew Dillon /* value 0 is reserved as per 802.11h standerd */ 2106085ff963SMatthew Dillon vap->iv_quiet_count_value = 1; 2107085ff963SMatthew Dillon } 2108085ff963SMatthew Dillon 2109085ff963SMatthew Dillon quiet->tbttcount = vap->iv_quiet_count_value; 2110085ff963SMatthew Dillon quiet->period = vap->iv_quiet_period; 2111085ff963SMatthew Dillon quiet->duration = htole16(vap->iv_quiet_duration); 2112085ff963SMatthew Dillon quiet->offset = htole16(vap->iv_quiet_offset); 2113085ff963SMatthew Dillon return frm + sizeof(*quiet); 2114085ff963SMatthew Dillon } 2115085ff963SMatthew Dillon 2116085ff963SMatthew Dillon /* 211732176cfdSRui Paulo * Add an 11h Channel Switch Announcement element to a frame. 211832176cfdSRui Paulo * Note that we use the per-vap CSA count to adjust the global 211932176cfdSRui Paulo * counter so we can use this routine to form probe response 212032176cfdSRui Paulo * frames and get the current count. 212132176cfdSRui Paulo */ 212232176cfdSRui Paulo static uint8_t * 212332176cfdSRui Paulo ieee80211_add_csa(uint8_t *frm, struct ieee80211vap *vap) 212432176cfdSRui Paulo { 212532176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 212632176cfdSRui Paulo struct ieee80211_csa_ie *csa = (struct ieee80211_csa_ie *) frm; 212732176cfdSRui Paulo 212832176cfdSRui Paulo csa->csa_ie = IEEE80211_ELEMID_CSA; 212932176cfdSRui Paulo csa->csa_len = 3; 213032176cfdSRui Paulo csa->csa_mode = 1; /* XXX force quiet on channel */ 213132176cfdSRui Paulo csa->csa_newchan = ieee80211_chan2ieee(ic, ic->ic_csa_newchan); 213232176cfdSRui Paulo csa->csa_count = ic->ic_csa_count - vap->iv_csa_count; 213332176cfdSRui Paulo return frm + sizeof(*csa); 213432176cfdSRui Paulo } 213532176cfdSRui Paulo 213632176cfdSRui Paulo /* 213732176cfdSRui Paulo * Add an 11h country information element to a frame. 213832176cfdSRui Paulo */ 213932176cfdSRui Paulo static uint8_t * 214032176cfdSRui Paulo ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic) 214132176cfdSRui Paulo { 214232176cfdSRui Paulo 214332176cfdSRui Paulo if (ic->ic_countryie == NULL || 214432176cfdSRui Paulo ic->ic_countryie_chan != ic->ic_bsschan) { 214532176cfdSRui Paulo /* 214632176cfdSRui Paulo * Handle lazy construction of ie. This is done on 214732176cfdSRui Paulo * first use and after a channel change that requires 214832176cfdSRui Paulo * re-calculation. 214932176cfdSRui Paulo */ 215032176cfdSRui Paulo if (ic->ic_countryie != NULL) 21514f655ef5SMatthew Dillon IEEE80211_FREE(ic->ic_countryie, M_80211_NODE_IE); 215232176cfdSRui Paulo ic->ic_countryie = ieee80211_alloc_countryie(ic); 215332176cfdSRui Paulo if (ic->ic_countryie == NULL) 215432176cfdSRui Paulo return frm; 215532176cfdSRui Paulo ic->ic_countryie_chan = ic->ic_bsschan; 215632176cfdSRui Paulo } 215732176cfdSRui Paulo return add_appie(frm, ic->ic_countryie); 215832176cfdSRui Paulo } 215932176cfdSRui Paulo 2160085ff963SMatthew Dillon uint8_t * 2161085ff963SMatthew Dillon ieee80211_add_wpa(uint8_t *frm, const struct ieee80211vap *vap) 2162085ff963SMatthew Dillon { 2163085ff963SMatthew Dillon if (vap->iv_flags & IEEE80211_F_WPA1 && vap->iv_wpa_ie != NULL) 2164085ff963SMatthew Dillon return (add_ie(frm, vap->iv_wpa_ie)); 2165085ff963SMatthew Dillon else { 2166085ff963SMatthew Dillon /* XXX else complain? */ 2167085ff963SMatthew Dillon return (frm); 2168085ff963SMatthew Dillon } 2169085ff963SMatthew Dillon } 2170085ff963SMatthew Dillon 2171085ff963SMatthew Dillon uint8_t * 2172085ff963SMatthew Dillon ieee80211_add_rsn(uint8_t *frm, const struct ieee80211vap *vap) 2173085ff963SMatthew Dillon { 2174085ff963SMatthew Dillon if (vap->iv_flags & IEEE80211_F_WPA2 && vap->iv_rsn_ie != NULL) 2175085ff963SMatthew Dillon return (add_ie(frm, vap->iv_rsn_ie)); 2176085ff963SMatthew Dillon else { 2177085ff963SMatthew Dillon /* XXX else complain? */ 2178085ff963SMatthew Dillon return (frm); 2179085ff963SMatthew Dillon } 2180085ff963SMatthew Dillon } 2181085ff963SMatthew Dillon 2182085ff963SMatthew Dillon uint8_t * 2183085ff963SMatthew Dillon ieee80211_add_qos(uint8_t *frm, const struct ieee80211_node *ni) 2184085ff963SMatthew Dillon { 2185085ff963SMatthew Dillon if (ni->ni_flags & IEEE80211_NODE_QOS) { 2186085ff963SMatthew Dillon *frm++ = IEEE80211_ELEMID_QOS; 2187085ff963SMatthew Dillon *frm++ = 1; 2188085ff963SMatthew Dillon *frm++ = 0; 2189085ff963SMatthew Dillon } 2190085ff963SMatthew Dillon 2191085ff963SMatthew Dillon return (frm); 2192085ff963SMatthew Dillon } 2193085ff963SMatthew Dillon 219432176cfdSRui Paulo /* 2195841ab66cSSepherosa Ziehau * Send a probe request frame with the specified ssid 2196841ab66cSSepherosa Ziehau * and any optional information element data. 2197841ab66cSSepherosa Ziehau */ 2198841ab66cSSepherosa Ziehau int 2199841ab66cSSepherosa Ziehau ieee80211_send_probereq(struct ieee80211_node *ni, 2200841ab66cSSepherosa Ziehau const uint8_t sa[IEEE80211_ADDR_LEN], 2201841ab66cSSepherosa Ziehau const uint8_t da[IEEE80211_ADDR_LEN], 2202841ab66cSSepherosa Ziehau const uint8_t bssid[IEEE80211_ADDR_LEN], 220332176cfdSRui Paulo const uint8_t *ssid, size_t ssidlen) 2204841ab66cSSepherosa Ziehau { 220532176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 2206841ab66cSSepherosa Ziehau struct ieee80211com *ic = ni->ni_ic; 220732176cfdSRui Paulo const struct ieee80211_txparam *tp; 220832176cfdSRui Paulo struct ieee80211_bpf_params params; 2209085ff963SMatthew Dillon struct ieee80211_frame *wh; 221032176cfdSRui Paulo const struct ieee80211_rateset *rs; 2211841ab66cSSepherosa Ziehau struct mbuf *m; 2212841ab66cSSepherosa Ziehau uint8_t *frm; 2213085ff963SMatthew Dillon int ret; 2214841ab66cSSepherosa Ziehau 221532176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_CAC) { 221632176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, 221732176cfdSRui Paulo "block %s frame in CAC state", "probe request"); 221832176cfdSRui Paulo vap->iv_stats.is_tx_badstate++; 221932176cfdSRui Paulo return EIO; /* XXX */ 222032176cfdSRui Paulo } 222132176cfdSRui Paulo 2222841ab66cSSepherosa Ziehau /* 2223841ab66cSSepherosa Ziehau * Hold a reference on the node so it doesn't go away until after 2224841ab66cSSepherosa Ziehau * the xmit is complete all the way in the driver. On error we 2225841ab66cSSepherosa Ziehau * will remove our reference. 2226841ab66cSSepherosa Ziehau */ 222732176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 22281e290df3SAntonio Huete Jimenez "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 2229841ab66cSSepherosa Ziehau __func__, __LINE__, 2230085ff963SMatthew Dillon ni, ether_sprintf(ni->ni_macaddr), 2231841ab66cSSepherosa Ziehau ieee80211_node_refcnt(ni)+1); 2232841ab66cSSepherosa Ziehau ieee80211_ref_node(ni); 2233841ab66cSSepherosa Ziehau 2234841ab66cSSepherosa Ziehau /* 2235841ab66cSSepherosa Ziehau * prreq frame format 2236841ab66cSSepherosa Ziehau * [tlv] ssid 2237841ab66cSSepherosa Ziehau * [tlv] supported rates 223832176cfdSRui Paulo * [tlv] RSN (optional) 2239841ab66cSSepherosa Ziehau * [tlv] extended supported rates 224032176cfdSRui Paulo * [tlv] WPA (optional) 2241841ab66cSSepherosa Ziehau * [tlv] user-specified ie's 2242841ab66cSSepherosa Ziehau */ 2243841ab66cSSepherosa Ziehau m = ieee80211_getmgtframe(&frm, 22444ac84526SSepherosa Ziehau ic->ic_headroom + sizeof(struct ieee80211_frame), 2245841ab66cSSepherosa Ziehau 2 + IEEE80211_NWID_LEN 2246841ab66cSSepherosa Ziehau + 2 + IEEE80211_RATE_SIZE 224732176cfdSRui Paulo + sizeof(struct ieee80211_ie_wpa) 2248841ab66cSSepherosa Ziehau + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 224932176cfdSRui Paulo + sizeof(struct ieee80211_ie_wpa) 225032176cfdSRui Paulo + (vap->iv_appie_probereq != NULL ? 225132176cfdSRui Paulo vap->iv_appie_probereq->ie_len : 0) 2252841ab66cSSepherosa Ziehau ); 2253841ab66cSSepherosa Ziehau if (m == NULL) { 225432176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++; 2255841ab66cSSepherosa Ziehau ieee80211_free_node(ni); 2256841ab66cSSepherosa Ziehau return ENOMEM; 2257841ab66cSSepherosa Ziehau } 2258841ab66cSSepherosa Ziehau 2259841ab66cSSepherosa Ziehau frm = ieee80211_add_ssid(frm, ssid, ssidlen); 226032176cfdSRui Paulo rs = ieee80211_get_suprates(ic, ic->ic_curchan); 226132176cfdSRui Paulo frm = ieee80211_add_rates(frm, rs); 2262085ff963SMatthew Dillon frm = ieee80211_add_rsn(frm, vap); 226332176cfdSRui Paulo frm = ieee80211_add_xrates(frm, rs); 2264085ff963SMatthew Dillon frm = ieee80211_add_wpa(frm, vap); 226532176cfdSRui Paulo if (vap->iv_appie_probereq != NULL) 226632176cfdSRui Paulo frm = add_appie(frm, vap->iv_appie_probereq); 2267841ab66cSSepherosa Ziehau m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2268841ab66cSSepherosa Ziehau 226932176cfdSRui Paulo KASSERT(M_LEADINGSPACE(m) >= sizeof(struct ieee80211_frame), 227032176cfdSRui Paulo ("leading space %zd", M_LEADINGSPACE(m))); 2271b5523eacSSascha Wildner M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); 227232176cfdSRui Paulo if (m == NULL) { 227332176cfdSRui Paulo /* NB: cannot happen */ 227432176cfdSRui Paulo ieee80211_free_node(ni); 2275841ab66cSSepherosa Ziehau return ENOMEM; 227632176cfdSRui Paulo } 2277841ab66cSSepherosa Ziehau 2278085ff963SMatthew Dillon IEEE80211_TX_LOCK(ic); 2279085ff963SMatthew Dillon wh = mtod(m, struct ieee80211_frame *); 228032176cfdSRui Paulo ieee80211_send_setup(ni, m, 2281841ab66cSSepherosa Ziehau IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, 228232176cfdSRui Paulo IEEE80211_NONQOS_TID, sa, da, bssid); 2283841ab66cSSepherosa Ziehau /* XXX power management? */ 228432176cfdSRui Paulo m->m_flags |= M_ENCAP; /* mark encapsulated */ 228532176cfdSRui Paulo 228632176cfdSRui Paulo M_WME_SETAC(m, WME_AC_BE); 2287841ab66cSSepherosa Ziehau 2288841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_probereq); 2289841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_mgmt); 2290841ab66cSSepherosa Ziehau 229132176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, 22921e290df3SAntonio Huete Jimenez "send probe req on channel %u bssid %s ssid \"%.*s\"\n", 2293085ff963SMatthew Dillon ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid), 2294f92fae3fSSascha Wildner (int)ssidlen, ssid); 2295841ab66cSSepherosa Ziehau 229632176cfdSRui Paulo memset(¶ms, 0, sizeof(params)); 229732176cfdSRui Paulo params.ibp_pri = M_WME_GETAC(m); 229832176cfdSRui Paulo tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; 229932176cfdSRui Paulo params.ibp_rate0 = tp->mgmtrate; 230032176cfdSRui Paulo if (IEEE80211_IS_MULTICAST(da)) { 230132176cfdSRui Paulo params.ibp_flags |= IEEE80211_BPF_NOACK; 230232176cfdSRui Paulo params.ibp_try0 = 1; 230332176cfdSRui Paulo } else 230432176cfdSRui Paulo params.ibp_try0 = tp->maxretry; 230532176cfdSRui Paulo params.ibp_power = ni->ni_txpower; 2306085ff963SMatthew Dillon ret = ieee80211_raw_output(vap, ni, m, ¶ms); 2307085ff963SMatthew Dillon IEEE80211_TX_UNLOCK(ic); 2308085ff963SMatthew Dillon return (ret); 2309841ab66cSSepherosa Ziehau } 2310841ab66cSSepherosa Ziehau 2311841ab66cSSepherosa Ziehau /* 2312841ab66cSSepherosa Ziehau * Calculate capability information for mgt frames. 2313841ab66cSSepherosa Ziehau */ 231432176cfdSRui Paulo uint16_t 231532176cfdSRui Paulo ieee80211_getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan) 2316841ab66cSSepherosa Ziehau { 231732176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 2318841ab66cSSepherosa Ziehau uint16_t capinfo; 2319841ab66cSSepherosa Ziehau 232032176cfdSRui Paulo KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode")); 2321841ab66cSSepherosa Ziehau 232232176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_HOSTAP) 2323841ab66cSSepherosa Ziehau capinfo = IEEE80211_CAPINFO_ESS; 232432176cfdSRui Paulo else if (vap->iv_opmode == IEEE80211_M_IBSS) 2325841ab66cSSepherosa Ziehau capinfo = IEEE80211_CAPINFO_IBSS; 2326841ab66cSSepherosa Ziehau else 2327841ab66cSSepherosa Ziehau capinfo = 0; 232832176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_PRIVACY) 2329841ab66cSSepherosa Ziehau capinfo |= IEEE80211_CAPINFO_PRIVACY; 233032176cfdSRui Paulo if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 233132176cfdSRui Paulo IEEE80211_IS_CHAN_2GHZ(chan)) 2332841ab66cSSepherosa Ziehau capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 2333841ab66cSSepherosa Ziehau if (ic->ic_flags & IEEE80211_F_SHSLOT) 2334841ab66cSSepherosa Ziehau capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 233532176cfdSRui Paulo if (IEEE80211_IS_CHAN_5GHZ(chan) && (vap->iv_flags & IEEE80211_F_DOTH)) 233632176cfdSRui Paulo capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; 2337841ab66cSSepherosa Ziehau return capinfo; 2338f186073cSJoerg Sonnenberger } 2339f186073cSJoerg Sonnenberger 2340f186073cSJoerg Sonnenberger /* 2341f186073cSJoerg Sonnenberger * Send a management frame. The node is for the destination (or ic_bss 2342f186073cSJoerg Sonnenberger * when in station mode). Nodes other than ic_bss have their reference 2343f186073cSJoerg Sonnenberger * count bumped to reflect our use for an indeterminant time. 2344f186073cSJoerg Sonnenberger */ 2345f186073cSJoerg Sonnenberger int 234632176cfdSRui Paulo ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) 2347f186073cSJoerg Sonnenberger { 234832176cfdSRui Paulo #define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT) 234932176cfdSRui Paulo #define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) 235032176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 235132176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic; 235232176cfdSRui Paulo struct ieee80211_node *bss = vap->iv_bss; 235332176cfdSRui Paulo struct ieee80211_bpf_params params; 2354f186073cSJoerg Sonnenberger struct mbuf *m; 2355f186073cSJoerg Sonnenberger uint8_t *frm; 2356f186073cSJoerg Sonnenberger uint16_t capinfo; 235732176cfdSRui Paulo int has_challenge, is_shared_key, ret, status; 2358f186073cSJoerg Sonnenberger 2359f186073cSJoerg Sonnenberger KASSERT(ni != NULL, ("null node")); 2360f186073cSJoerg Sonnenberger 2361f186073cSJoerg Sonnenberger /* 2362f186073cSJoerg Sonnenberger * Hold a reference on the node so it doesn't go away until after 2363f186073cSJoerg Sonnenberger * the xmit is complete all the way in the driver. On error we 2364f186073cSJoerg Sonnenberger * will remove our reference. 2365f186073cSJoerg Sonnenberger */ 236632176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 23671e290df3SAntonio Huete Jimenez "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 2368841ab66cSSepherosa Ziehau __func__, __LINE__, 2369085ff963SMatthew Dillon ni, ether_sprintf(ni->ni_macaddr), 2370841ab66cSSepherosa Ziehau ieee80211_node_refcnt(ni)+1); 2371f186073cSJoerg Sonnenberger ieee80211_ref_node(ni); 2372841ab66cSSepherosa Ziehau 237332176cfdSRui Paulo memset(¶ms, 0, sizeof(params)); 2374f186073cSJoerg Sonnenberger switch (type) { 2375f186073cSJoerg Sonnenberger 2376f186073cSJoerg Sonnenberger case IEEE80211_FC0_SUBTYPE_AUTH: 2377841ab66cSSepherosa Ziehau status = arg >> 16; 2378841ab66cSSepherosa Ziehau arg &= 0xffff; 2379841ab66cSSepherosa Ziehau has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || 2380841ab66cSSepherosa Ziehau arg == IEEE80211_AUTH_SHARED_RESPONSE) && 2381841ab66cSSepherosa Ziehau ni->ni_challenge != NULL); 2382841ab66cSSepherosa Ziehau 2383841ab66cSSepherosa Ziehau /* 2384841ab66cSSepherosa Ziehau * Deduce whether we're doing open authentication or 2385841ab66cSSepherosa Ziehau * shared key authentication. We do the latter if 2386841ab66cSSepherosa Ziehau * we're in the middle of a shared key authentication 2387841ab66cSSepherosa Ziehau * handshake or if we're initiating an authentication 2388841ab66cSSepherosa Ziehau * request and configured to use shared key. 2389841ab66cSSepherosa Ziehau */ 2390841ab66cSSepherosa Ziehau is_shared_key = has_challenge || 2391841ab66cSSepherosa Ziehau arg >= IEEE80211_AUTH_SHARED_RESPONSE || 2392841ab66cSSepherosa Ziehau (arg == IEEE80211_AUTH_SHARED_REQUEST && 239332176cfdSRui Paulo bss->ni_authmode == IEEE80211_AUTH_SHARED); 2394841ab66cSSepherosa Ziehau 2395841ab66cSSepherosa Ziehau m = ieee80211_getmgtframe(&frm, 23964ac84526SSepherosa Ziehau ic->ic_headroom + sizeof(struct ieee80211_frame), 2397841ab66cSSepherosa Ziehau 3 * sizeof(uint16_t) 2398841ab66cSSepherosa Ziehau + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? 2399841ab66cSSepherosa Ziehau sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0) 2400841ab66cSSepherosa Ziehau ); 2401f186073cSJoerg Sonnenberger if (m == NULL) 2402841ab66cSSepherosa Ziehau senderr(ENOMEM, is_tx_nobuf); 2403841ab66cSSepherosa Ziehau 2404841ab66cSSepherosa Ziehau ((uint16_t *)frm)[0] = 2405841ab66cSSepherosa Ziehau (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) 2406841ab66cSSepherosa Ziehau : htole16(IEEE80211_AUTH_ALG_OPEN); 2407f186073cSJoerg Sonnenberger ((uint16_t *)frm)[1] = htole16(arg); /* sequence number */ 2408841ab66cSSepherosa Ziehau ((uint16_t *)frm)[2] = htole16(status);/* status */ 2409841ab66cSSepherosa Ziehau 2410841ab66cSSepherosa Ziehau if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { 2411841ab66cSSepherosa Ziehau ((uint16_t *)frm)[3] = 2412841ab66cSSepherosa Ziehau htole16((IEEE80211_CHALLENGE_LEN << 8) | 2413841ab66cSSepherosa Ziehau IEEE80211_ELEMID_CHALLENGE); 2414841ab66cSSepherosa Ziehau memcpy(&((uint16_t *)frm)[4], ni->ni_challenge, 2415841ab66cSSepherosa Ziehau IEEE80211_CHALLENGE_LEN); 2416841ab66cSSepherosa Ziehau m->m_pkthdr.len = m->m_len = 2417841ab66cSSepherosa Ziehau 4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN; 2418841ab66cSSepherosa Ziehau if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { 241932176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, 242032176cfdSRui Paulo "request encrypt frame (%s)", __func__); 242132176cfdSRui Paulo /* mark frame for encryption */ 242232176cfdSRui Paulo params.ibp_flags |= IEEE80211_BPF_CRYPTO; 2423841ab66cSSepherosa Ziehau } 2424841ab66cSSepherosa Ziehau } else 2425841ab66cSSepherosa Ziehau m->m_pkthdr.len = m->m_len = 3 * sizeof(uint16_t); 2426841ab66cSSepherosa Ziehau 2427841ab66cSSepherosa Ziehau /* XXX not right for shared key */ 2428841ab66cSSepherosa Ziehau if (status == IEEE80211_STATUS_SUCCESS) 2429841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_auth); 2430841ab66cSSepherosa Ziehau else 2431841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_auth_fail); 2432841ab66cSSepherosa Ziehau 243332176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA) 243432176cfdSRui Paulo ieee80211_add_callback(m, ieee80211_tx_mgt_cb, 243532176cfdSRui Paulo (void *) vap->iv_state); 2436f186073cSJoerg Sonnenberger break; 2437f186073cSJoerg Sonnenberger 2438f186073cSJoerg Sonnenberger case IEEE80211_FC0_SUBTYPE_DEAUTH: 243932176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, 24404f655ef5SMatthew Dillon "send station deauthenticate (reason: %d (%s))", arg, 24414f655ef5SMatthew Dillon ieee80211_reason_to_string(arg)); 24424ac84526SSepherosa Ziehau m = ieee80211_getmgtframe(&frm, 24434ac84526SSepherosa Ziehau ic->ic_headroom + sizeof(struct ieee80211_frame), 24444ac84526SSepherosa Ziehau sizeof(uint16_t)); 2445f186073cSJoerg Sonnenberger if (m == NULL) 2446841ab66cSSepherosa Ziehau senderr(ENOMEM, is_tx_nobuf); 2447841ab66cSSepherosa Ziehau *(uint16_t *)frm = htole16(arg); /* reason */ 2448841ab66cSSepherosa Ziehau m->m_pkthdr.len = m->m_len = sizeof(uint16_t); 2449841ab66cSSepherosa Ziehau 2450841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_deauth); 2451841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg); 2452841ab66cSSepherosa Ziehau 2453841ab66cSSepherosa Ziehau ieee80211_node_unauthorize(ni); /* port closed */ 2454f186073cSJoerg Sonnenberger break; 2455f186073cSJoerg Sonnenberger 2456f186073cSJoerg Sonnenberger case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 2457208a1285SSepherosa Ziehau case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 2458f186073cSJoerg Sonnenberger /* 2459f186073cSJoerg Sonnenberger * asreq frame format 2460f186073cSJoerg Sonnenberger * [2] capability information 2461f186073cSJoerg Sonnenberger * [2] listen interval 2462f186073cSJoerg Sonnenberger * [6*] current AP address (reassoc only) 2463f186073cSJoerg Sonnenberger * [tlv] ssid 2464f186073cSJoerg Sonnenberger * [tlv] supported rates 2465f186073cSJoerg Sonnenberger * [tlv] extended supported rates 246632176cfdSRui Paulo * [4] power capability (optional) 246732176cfdSRui Paulo * [28] supported channels (optional) 246832176cfdSRui Paulo * [tlv] HT capabilities 246932176cfdSRui Paulo * [tlv] WME (optional) 247032176cfdSRui Paulo * [tlv] Vendor OUI HT capabilities (optional) 247132176cfdSRui Paulo * [tlv] Atheros capabilities (if negotiated) 247232176cfdSRui Paulo * [tlv] AppIE's (optional) 2473f186073cSJoerg Sonnenberger */ 2474841ab66cSSepherosa Ziehau m = ieee80211_getmgtframe(&frm, 24754ac84526SSepherosa Ziehau ic->ic_headroom + sizeof(struct ieee80211_frame), 2476841ab66cSSepherosa Ziehau sizeof(uint16_t) 2477f186073cSJoerg Sonnenberger + sizeof(uint16_t) 2478f186073cSJoerg Sonnenberger + IEEE80211_ADDR_LEN 2479841ab66cSSepherosa Ziehau + 2 + IEEE80211_NWID_LEN 2480f186073cSJoerg Sonnenberger + 2 + IEEE80211_RATE_SIZE 2481841ab66cSSepherosa Ziehau + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 248232176cfdSRui Paulo + 4 248332176cfdSRui Paulo + 2 + 26 2484841ab66cSSepherosa Ziehau + sizeof(struct ieee80211_wme_info) 248532176cfdSRui Paulo + sizeof(struct ieee80211_ie_htcap) 248632176cfdSRui Paulo + 4 + sizeof(struct ieee80211_ie_htcap) 248732176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 248832176cfdSRui Paulo + sizeof(struct ieee80211_ath_ie) 248932176cfdSRui Paulo #endif 249032176cfdSRui Paulo + (vap->iv_appie_wpa != NULL ? 249132176cfdSRui Paulo vap->iv_appie_wpa->ie_len : 0) 249232176cfdSRui Paulo + (vap->iv_appie_assocreq != NULL ? 249332176cfdSRui Paulo vap->iv_appie_assocreq->ie_len : 0) 2494841ab66cSSepherosa Ziehau ); 2495f186073cSJoerg Sonnenberger if (m == NULL) 2496841ab66cSSepherosa Ziehau senderr(ENOMEM, is_tx_nobuf); 2497f186073cSJoerg Sonnenberger 249832176cfdSRui Paulo KASSERT(vap->iv_opmode == IEEE80211_M_STA, 249932176cfdSRui Paulo ("wrong mode %u", vap->iv_opmode)); 2500841ab66cSSepherosa Ziehau capinfo = IEEE80211_CAPINFO_ESS; 250132176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_PRIVACY) 2502f186073cSJoerg Sonnenberger capinfo |= IEEE80211_CAPINFO_PRIVACY; 2503f186073cSJoerg Sonnenberger /* 2504f186073cSJoerg Sonnenberger * NB: Some 11a AP's reject the request when 250532176cfdSRui Paulo * short premable is set. 2506f186073cSJoerg Sonnenberger */ 250732176cfdSRui Paulo if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 250832176cfdSRui Paulo IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) 2509f186073cSJoerg Sonnenberger capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 251032176cfdSRui Paulo if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 2511841ab66cSSepherosa Ziehau (ic->ic_caps & IEEE80211_C_SHSLOT)) 2512f186073cSJoerg Sonnenberger capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 251332176cfdSRui Paulo if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) && 251432176cfdSRui Paulo (vap->iv_flags & IEEE80211_F_DOTH)) 251532176cfdSRui Paulo capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; 2516f186073cSJoerg Sonnenberger *(uint16_t *)frm = htole16(capinfo); 2517f186073cSJoerg Sonnenberger frm += 2; 2518f186073cSJoerg Sonnenberger 251932176cfdSRui Paulo KASSERT(bss->ni_intval != 0, ("beacon interval is zero!")); 252022b21df8SSepherosa Ziehau *(uint16_t *)frm = htole16(howmany(ic->ic_lintval, 252132176cfdSRui Paulo bss->ni_intval)); 2522f186073cSJoerg Sonnenberger frm += 2; 2523f186073cSJoerg Sonnenberger 2524f186073cSJoerg Sonnenberger if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 252532176cfdSRui Paulo IEEE80211_ADDR_COPY(frm, bss->ni_bssid); 2526f186073cSJoerg Sonnenberger frm += IEEE80211_ADDR_LEN; 2527f186073cSJoerg Sonnenberger } 2528f186073cSJoerg Sonnenberger 2529f186073cSJoerg Sonnenberger frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); 253032176cfdSRui Paulo frm = ieee80211_add_rates(frm, &ni->ni_rates); 2531085ff963SMatthew Dillon frm = ieee80211_add_rsn(frm, vap); 253232176cfdSRui Paulo frm = ieee80211_add_xrates(frm, &ni->ni_rates); 253332176cfdSRui Paulo if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) { 253432176cfdSRui Paulo frm = ieee80211_add_powercapability(frm, 253532176cfdSRui Paulo ic->ic_curchan); 253632176cfdSRui Paulo frm = ieee80211_add_supportedchannels(frm, ic); 253732176cfdSRui Paulo } 25384f898719SImre Vadász 25394f898719SImre Vadász /* 25404f898719SImre Vadász * Check the channel - we may be using an 11n NIC with an 25414f898719SImre Vadász * 11n capable station, but we're configured to be an 11b 25424f898719SImre Vadász * channel. 25434f898719SImre Vadász */ 254432176cfdSRui Paulo if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && 25454f898719SImre Vadász IEEE80211_IS_CHAN_HT(ni->ni_chan) && 254632176cfdSRui Paulo ni->ni_ies.htcap_ie != NULL && 25474f898719SImre Vadász ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) { 254832176cfdSRui Paulo frm = ieee80211_add_htcap(frm, ni); 25494f898719SImre Vadász } 2550085ff963SMatthew Dillon frm = ieee80211_add_wpa(frm, vap); 255132176cfdSRui Paulo if ((ic->ic_flags & IEEE80211_F_WME) && 255232176cfdSRui Paulo ni->ni_ies.wme_ie != NULL) 255332176cfdSRui Paulo frm = ieee80211_add_wme_info(frm, &ic->ic_wme); 25544f898719SImre Vadász 25554f898719SImre Vadász /* 25564f898719SImre Vadász * Same deal - only send HT info if we're on an 11n 25574f898719SImre Vadász * capable channel. 25584f898719SImre Vadász */ 255932176cfdSRui Paulo if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && 25604f898719SImre Vadász IEEE80211_IS_CHAN_HT(ni->ni_chan) && 256132176cfdSRui Paulo ni->ni_ies.htcap_ie != NULL && 25624f898719SImre Vadász ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR) { 256332176cfdSRui Paulo frm = ieee80211_add_htcap_vendor(frm, ni); 25644f898719SImre Vadász } 256532176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 256632176cfdSRui Paulo if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) { 256732176cfdSRui Paulo frm = ieee80211_add_ath(frm, 256832176cfdSRui Paulo IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), 256932176cfdSRui Paulo ((vap->iv_flags & IEEE80211_F_WPA) == 0 && 257032176cfdSRui Paulo ni->ni_authmode != IEEE80211_AUTH_8021X) ? 257132176cfdSRui Paulo vap->iv_def_txkey : IEEE80211_KEYIX_NONE); 257232176cfdSRui Paulo } 257332176cfdSRui Paulo #endif /* IEEE80211_SUPPORT_SUPERG */ 257432176cfdSRui Paulo if (vap->iv_appie_assocreq != NULL) 257532176cfdSRui Paulo frm = add_appie(frm, vap->iv_appie_assocreq); 2576f186073cSJoerg Sonnenberger m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2577f186073cSJoerg Sonnenberger 257832176cfdSRui Paulo ieee80211_add_callback(m, ieee80211_tx_mgt_cb, 257932176cfdSRui Paulo (void *) vap->iv_state); 2580f186073cSJoerg Sonnenberger break; 2581f186073cSJoerg Sonnenberger 2582f186073cSJoerg Sonnenberger case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 2583f186073cSJoerg Sonnenberger case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 2584f186073cSJoerg Sonnenberger /* 258532176cfdSRui Paulo * asresp frame format 2586f186073cSJoerg Sonnenberger * [2] capability information 2587f186073cSJoerg Sonnenberger * [2] status 2588f186073cSJoerg Sonnenberger * [2] association ID 2589f186073cSJoerg Sonnenberger * [tlv] supported rates 2590f186073cSJoerg Sonnenberger * [tlv] extended supported rates 259132176cfdSRui Paulo * [tlv] HT capabilities (standard, if STA enabled) 259232176cfdSRui Paulo * [tlv] HT information (standard, if STA enabled) 259332176cfdSRui Paulo * [tlv] WME (if configured and STA enabled) 259432176cfdSRui Paulo * [tlv] HT capabilities (vendor OUI, if STA enabled) 259532176cfdSRui Paulo * [tlv] HT information (vendor OUI, if STA enabled) 259632176cfdSRui Paulo * [tlv] Atheros capabilities (if STA enabled) 259732176cfdSRui Paulo * [tlv] AppIE's (optional) 2598f186073cSJoerg Sonnenberger */ 2599841ab66cSSepherosa Ziehau m = ieee80211_getmgtframe(&frm, 26004ac84526SSepherosa Ziehau ic->ic_headroom + sizeof(struct ieee80211_frame), 2601841ab66cSSepherosa Ziehau sizeof(uint16_t) 2602f186073cSJoerg Sonnenberger + sizeof(uint16_t) 2603f186073cSJoerg Sonnenberger + sizeof(uint16_t) 2604f186073cSJoerg Sonnenberger + 2 + IEEE80211_RATE_SIZE 2605841ab66cSSepherosa Ziehau + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 260632176cfdSRui Paulo + sizeof(struct ieee80211_ie_htcap) + 4 260732176cfdSRui Paulo + sizeof(struct ieee80211_ie_htinfo) + 4 2608841ab66cSSepherosa Ziehau + sizeof(struct ieee80211_wme_param) 260932176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 261032176cfdSRui Paulo + sizeof(struct ieee80211_ath_ie) 261132176cfdSRui Paulo #endif 261232176cfdSRui Paulo + (vap->iv_appie_assocresp != NULL ? 261332176cfdSRui Paulo vap->iv_appie_assocresp->ie_len : 0) 2614841ab66cSSepherosa Ziehau ); 2615f186073cSJoerg Sonnenberger if (m == NULL) 2616841ab66cSSepherosa Ziehau senderr(ENOMEM, is_tx_nobuf); 2617f186073cSJoerg Sonnenberger 261832176cfdSRui Paulo capinfo = ieee80211_getcapinfo(vap, bss->ni_chan); 2619f186073cSJoerg Sonnenberger *(uint16_t *)frm = htole16(capinfo); 2620f186073cSJoerg Sonnenberger frm += 2; 2621f186073cSJoerg Sonnenberger 2622f186073cSJoerg Sonnenberger *(uint16_t *)frm = htole16(arg); /* status */ 2623f186073cSJoerg Sonnenberger frm += 2; 2624f186073cSJoerg Sonnenberger 2625841ab66cSSepherosa Ziehau if (arg == IEEE80211_STATUS_SUCCESS) { 2626f186073cSJoerg Sonnenberger *(uint16_t *)frm = htole16(ni->ni_associd); 2627841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_assoc); 2628841ab66cSSepherosa Ziehau } else 2629841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_assoc_fail); 2630f186073cSJoerg Sonnenberger frm += 2; 2631f186073cSJoerg Sonnenberger 263232176cfdSRui Paulo frm = ieee80211_add_rates(frm, &ni->ni_rates); 263332176cfdSRui Paulo frm = ieee80211_add_xrates(frm, &ni->ni_rates); 263432176cfdSRui Paulo /* NB: respond according to what we received */ 263532176cfdSRui Paulo if ((ni->ni_flags & HTFLAGS) == IEEE80211_NODE_HT) { 263632176cfdSRui Paulo frm = ieee80211_add_htcap(frm, ni); 263732176cfdSRui Paulo frm = ieee80211_add_htinfo(frm, ni); 263832176cfdSRui Paulo } 263932176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_WME) && 264032176cfdSRui Paulo ni->ni_ies.wme_ie != NULL) 2641841ab66cSSepherosa Ziehau frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 264232176cfdSRui Paulo if ((ni->ni_flags & HTFLAGS) == HTFLAGS) { 264332176cfdSRui Paulo frm = ieee80211_add_htcap_vendor(frm, ni); 264432176cfdSRui Paulo frm = ieee80211_add_htinfo_vendor(frm, ni); 264532176cfdSRui Paulo } 264632176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 264732176cfdSRui Paulo if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) 264832176cfdSRui Paulo frm = ieee80211_add_ath(frm, 264932176cfdSRui Paulo IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), 265032176cfdSRui Paulo ((vap->iv_flags & IEEE80211_F_WPA) == 0 && 265132176cfdSRui Paulo ni->ni_authmode != IEEE80211_AUTH_8021X) ? 265232176cfdSRui Paulo vap->iv_def_txkey : IEEE80211_KEYIX_NONE); 265332176cfdSRui Paulo #endif /* IEEE80211_SUPPORT_SUPERG */ 265432176cfdSRui Paulo if (vap->iv_appie_assocresp != NULL) 265532176cfdSRui Paulo frm = add_appie(frm, vap->iv_appie_assocresp); 2656f186073cSJoerg Sonnenberger m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 2657f186073cSJoerg Sonnenberger break; 2658f186073cSJoerg Sonnenberger 2659f186073cSJoerg Sonnenberger case IEEE80211_FC0_SUBTYPE_DISASSOC: 266032176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, 26614f655ef5SMatthew Dillon "send station disassociate (reason: %d (%s))", arg, 26624f655ef5SMatthew Dillon ieee80211_reason_to_string(arg)); 26634ac84526SSepherosa Ziehau m = ieee80211_getmgtframe(&frm, 26644ac84526SSepherosa Ziehau ic->ic_headroom + sizeof(struct ieee80211_frame), 26654ac84526SSepherosa Ziehau sizeof(uint16_t)); 2666f186073cSJoerg Sonnenberger if (m == NULL) 2667841ab66cSSepherosa Ziehau senderr(ENOMEM, is_tx_nobuf); 2668841ab66cSSepherosa Ziehau *(uint16_t *)frm = htole16(arg); /* reason */ 2669841ab66cSSepherosa Ziehau m->m_pkthdr.len = m->m_len = sizeof(uint16_t); 2670841ab66cSSepherosa Ziehau 2671841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT(ni, tx_disassoc); 2672841ab66cSSepherosa Ziehau IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg); 2673f186073cSJoerg Sonnenberger break; 2674f186073cSJoerg Sonnenberger 2675f186073cSJoerg Sonnenberger default: 267632176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, 267732176cfdSRui Paulo "invalid mgmt frame type %u", type); 2678f186073cSJoerg Sonnenberger senderr(EINVAL, is_tx_unknownmgt); 2679f186073cSJoerg Sonnenberger /* NOTREACHED */ 2680f186073cSJoerg Sonnenberger } 268132176cfdSRui Paulo 268232176cfdSRui Paulo /* NB: force non-ProbeResp frames to the highest queue */ 268332176cfdSRui Paulo params.ibp_pri = WME_AC_VO; 268432176cfdSRui Paulo params.ibp_rate0 = bss->ni_txparms->mgmtrate; 268532176cfdSRui Paulo /* NB: we know all frames are unicast */ 268632176cfdSRui Paulo params.ibp_try0 = bss->ni_txparms->maxretry; 268732176cfdSRui Paulo params.ibp_power = bss->ni_txpower; 268832176cfdSRui Paulo return ieee80211_mgmt_output(ni, m, type, ¶ms); 2689f186073cSJoerg Sonnenberger bad: 2690841ab66cSSepherosa Ziehau ieee80211_free_node(ni); 2691f186073cSJoerg Sonnenberger return ret; 2692f186073cSJoerg Sonnenberger #undef senderr 269332176cfdSRui Paulo #undef HTFLAGS 2694f186073cSJoerg Sonnenberger } 2695841ab66cSSepherosa Ziehau 2696841ab66cSSepherosa Ziehau /* 269732176cfdSRui Paulo * Return an mbuf with a probe response frame in it. 269832176cfdSRui Paulo * Space is left to prepend and 802.11 header at the 269932176cfdSRui Paulo * front but it's left to the caller to fill in. 27003da93495SSepherosa Ziehau */ 27013da93495SSepherosa Ziehau struct mbuf * 270232176cfdSRui Paulo ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) 27033da93495SSepherosa Ziehau { 270432176cfdSRui Paulo struct ieee80211vap *vap = bss->ni_vap; 270532176cfdSRui Paulo struct ieee80211com *ic = bss->ni_ic; 270632176cfdSRui Paulo const struct ieee80211_rateset *rs; 27073da93495SSepherosa Ziehau struct mbuf *m; 270832176cfdSRui Paulo uint16_t capinfo; 270932176cfdSRui Paulo uint8_t *frm; 27103da93495SSepherosa Ziehau 271132176cfdSRui Paulo /* 271232176cfdSRui Paulo * probe response frame format 271332176cfdSRui Paulo * [8] time stamp 271432176cfdSRui Paulo * [2] beacon interval 271532176cfdSRui Paulo * [2] cabability information 271632176cfdSRui Paulo * [tlv] ssid 271732176cfdSRui Paulo * [tlv] supported rates 271832176cfdSRui Paulo * [tlv] parameter set (FH/DS) 271932176cfdSRui Paulo * [tlv] parameter set (IBSS) 272032176cfdSRui Paulo * [tlv] country (optional) 272132176cfdSRui Paulo * [3] power control (optional) 272232176cfdSRui Paulo * [5] channel switch announcement (CSA) (optional) 272332176cfdSRui Paulo * [tlv] extended rate phy (ERP) 272432176cfdSRui Paulo * [tlv] extended supported rates 272532176cfdSRui Paulo * [tlv] RSN (optional) 272632176cfdSRui Paulo * [tlv] HT capabilities 272732176cfdSRui Paulo * [tlv] HT information 272832176cfdSRui Paulo * [tlv] WPA (optional) 272932176cfdSRui Paulo * [tlv] WME (optional) 273032176cfdSRui Paulo * [tlv] Vendor OUI HT capabilities (optional) 273132176cfdSRui Paulo * [tlv] Vendor OUI HT information (optional) 273232176cfdSRui Paulo * [tlv] Atheros capabilities 273332176cfdSRui Paulo * [tlv] AppIE's (optional) 273432176cfdSRui Paulo * [tlv] Mesh ID (MBSS) 273532176cfdSRui Paulo * [tlv] Mesh Conf (MBSS) 273632176cfdSRui Paulo */ 273732176cfdSRui Paulo m = ieee80211_getmgtframe(&frm, 273832176cfdSRui Paulo ic->ic_headroom + sizeof(struct ieee80211_frame), 273932176cfdSRui Paulo 8 274032176cfdSRui Paulo + sizeof(uint16_t) 274132176cfdSRui Paulo + sizeof(uint16_t) 274232176cfdSRui Paulo + 2 + IEEE80211_NWID_LEN 274332176cfdSRui Paulo + 2 + IEEE80211_RATE_SIZE 274432176cfdSRui Paulo + 7 /* max(7,3) */ 274532176cfdSRui Paulo + IEEE80211_COUNTRY_MAX_SIZE 274632176cfdSRui Paulo + 3 274732176cfdSRui Paulo + sizeof(struct ieee80211_csa_ie) 2748085ff963SMatthew Dillon + sizeof(struct ieee80211_quiet_ie) 274932176cfdSRui Paulo + 3 275032176cfdSRui Paulo + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 275132176cfdSRui Paulo + sizeof(struct ieee80211_ie_wpa) 275232176cfdSRui Paulo + sizeof(struct ieee80211_ie_htcap) 275332176cfdSRui Paulo + sizeof(struct ieee80211_ie_htinfo) 275432176cfdSRui Paulo + sizeof(struct ieee80211_ie_wpa) 275532176cfdSRui Paulo + sizeof(struct ieee80211_wme_param) 275632176cfdSRui Paulo + 4 + sizeof(struct ieee80211_ie_htcap) 275732176cfdSRui Paulo + 4 + sizeof(struct ieee80211_ie_htinfo) 275832176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 275932176cfdSRui Paulo + sizeof(struct ieee80211_ath_ie) 276032176cfdSRui Paulo #endif 276132176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 276232176cfdSRui Paulo + 2 + IEEE80211_MESHID_LEN 276332176cfdSRui Paulo + sizeof(struct ieee80211_meshconf_ie) 276432176cfdSRui Paulo #endif 276532176cfdSRui Paulo + (vap->iv_appie_proberesp != NULL ? 276632176cfdSRui Paulo vap->iv_appie_proberesp->ie_len : 0) 276732176cfdSRui Paulo ); 276832176cfdSRui Paulo if (m == NULL) { 276932176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++; 27703da93495SSepherosa Ziehau return NULL; 277132176cfdSRui Paulo } 27723da93495SSepherosa Ziehau 277332176cfdSRui Paulo memset(frm, 0, 8); /* timestamp should be filled later */ 277432176cfdSRui Paulo frm += 8; 277532176cfdSRui Paulo *(uint16_t *)frm = htole16(bss->ni_intval); 277632176cfdSRui Paulo frm += 2; 277732176cfdSRui Paulo capinfo = ieee80211_getcapinfo(vap, bss->ni_chan); 277832176cfdSRui Paulo *(uint16_t *)frm = htole16(capinfo); 277932176cfdSRui Paulo frm += 2; 27803da93495SSepherosa Ziehau 278132176cfdSRui Paulo frm = ieee80211_add_ssid(frm, bss->ni_essid, bss->ni_esslen); 278232176cfdSRui Paulo rs = ieee80211_get_suprates(ic, bss->ni_chan); 278332176cfdSRui Paulo frm = ieee80211_add_rates(frm, rs); 278432176cfdSRui Paulo 278532176cfdSRui Paulo if (IEEE80211_IS_CHAN_FHSS(bss->ni_chan)) { 278632176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_FHPARMS; 278732176cfdSRui Paulo *frm++ = 5; 278832176cfdSRui Paulo *frm++ = bss->ni_fhdwell & 0x00ff; 278932176cfdSRui Paulo *frm++ = (bss->ni_fhdwell >> 8) & 0x00ff; 279032176cfdSRui Paulo *frm++ = IEEE80211_FH_CHANSET( 279132176cfdSRui Paulo ieee80211_chan2ieee(ic, bss->ni_chan)); 279232176cfdSRui Paulo *frm++ = IEEE80211_FH_CHANPAT( 279332176cfdSRui Paulo ieee80211_chan2ieee(ic, bss->ni_chan)); 279432176cfdSRui Paulo *frm++ = bss->ni_fhindex; 279532176cfdSRui Paulo } else { 279632176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_DSPARMS; 279732176cfdSRui Paulo *frm++ = 1; 279832176cfdSRui Paulo *frm++ = ieee80211_chan2ieee(ic, bss->ni_chan); 279932176cfdSRui Paulo } 280032176cfdSRui Paulo 280132176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_IBSS) { 280232176cfdSRui Paulo *frm++ = IEEE80211_ELEMID_IBSSPARMS; 280332176cfdSRui Paulo *frm++ = 2; 280432176cfdSRui Paulo *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ 280532176cfdSRui Paulo } 280632176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_DOTH) || 280732176cfdSRui Paulo (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) 280832176cfdSRui Paulo frm = ieee80211_add_countryie(frm, ic); 280932176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_DOTH) { 281032176cfdSRui Paulo if (IEEE80211_IS_CHAN_5GHZ(bss->ni_chan)) 281132176cfdSRui Paulo frm = ieee80211_add_powerconstraint(frm, vap); 281232176cfdSRui Paulo if (ic->ic_flags & IEEE80211_F_CSAPENDING) 281332176cfdSRui Paulo frm = ieee80211_add_csa(frm, vap); 281432176cfdSRui Paulo } 2815085ff963SMatthew Dillon if (vap->iv_flags & IEEE80211_F_DOTH) { 2816085ff963SMatthew Dillon if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && 2817085ff963SMatthew Dillon (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) { 2818085ff963SMatthew Dillon if (vap->iv_quiet) 2819085ff963SMatthew Dillon frm = ieee80211_add_quiet(frm, vap); 2820085ff963SMatthew Dillon } 2821085ff963SMatthew Dillon } 282232176cfdSRui Paulo if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan)) 282332176cfdSRui Paulo frm = ieee80211_add_erp(frm, ic); 282432176cfdSRui Paulo frm = ieee80211_add_xrates(frm, rs); 2825085ff963SMatthew Dillon frm = ieee80211_add_rsn(frm, vap); 282632176cfdSRui Paulo /* 282732176cfdSRui Paulo * NB: legacy 11b clients do not get certain ie's. 282832176cfdSRui Paulo * The caller identifies such clients by passing 282932176cfdSRui Paulo * a token in legacy to us. Could expand this to be 283032176cfdSRui Paulo * any legacy client for stuff like HT ie's. 283132176cfdSRui Paulo */ 283232176cfdSRui Paulo if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && 283332176cfdSRui Paulo legacy != IEEE80211_SEND_LEGACY_11B) { 283432176cfdSRui Paulo frm = ieee80211_add_htcap(frm, bss); 283532176cfdSRui Paulo frm = ieee80211_add_htinfo(frm, bss); 283632176cfdSRui Paulo } 2837085ff963SMatthew Dillon frm = ieee80211_add_wpa(frm, vap); 283832176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_WME) 283932176cfdSRui Paulo frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 284032176cfdSRui Paulo if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && 284132176cfdSRui Paulo (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) && 284232176cfdSRui Paulo legacy != IEEE80211_SEND_LEGACY_11B) { 284332176cfdSRui Paulo frm = ieee80211_add_htcap_vendor(frm, bss); 284432176cfdSRui Paulo frm = ieee80211_add_htinfo_vendor(frm, bss); 284532176cfdSRui Paulo } 284632176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 284732176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_ATHEROS) && 284832176cfdSRui Paulo legacy != IEEE80211_SEND_LEGACY_11B) 284932176cfdSRui Paulo frm = ieee80211_add_athcaps(frm, bss); 285032176cfdSRui Paulo #endif 285132176cfdSRui Paulo if (vap->iv_appie_proberesp != NULL) 285232176cfdSRui Paulo frm = add_appie(frm, vap->iv_appie_proberesp); 285332176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 285432176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_MBSS) { 285532176cfdSRui Paulo frm = ieee80211_add_meshid(frm, vap); 285632176cfdSRui Paulo frm = ieee80211_add_meshconf(frm, vap); 285732176cfdSRui Paulo } 285832176cfdSRui Paulo #endif 285932176cfdSRui Paulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 28603da93495SSepherosa Ziehau 28613da93495SSepherosa Ziehau return m; 28623da93495SSepherosa Ziehau } 28633da93495SSepherosa Ziehau 28643da93495SSepherosa Ziehau /* 286532176cfdSRui Paulo * Send a probe response frame to the specified mac address. 286632176cfdSRui Paulo * This does not go through the normal mgt frame api so we 286732176cfdSRui Paulo * can specify the destination address and re-use the bss node 286832176cfdSRui Paulo * for the sta reference. 2869841ab66cSSepherosa Ziehau */ 287032176cfdSRui Paulo int 287132176cfdSRui Paulo ieee80211_send_proberesp(struct ieee80211vap *vap, 287232176cfdSRui Paulo const uint8_t da[IEEE80211_ADDR_LEN], int legacy) 2873841ab66cSSepherosa Ziehau { 287432176cfdSRui Paulo struct ieee80211_node *bss = vap->iv_bss; 287532176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic; 2876085ff963SMatthew Dillon struct ieee80211_frame *wh; 2877841ab66cSSepherosa Ziehau struct mbuf *m; 2878085ff963SMatthew Dillon int ret; 287932176cfdSRui Paulo 288032176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_CAC) { 288132176cfdSRui Paulo IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss, 288232176cfdSRui Paulo "block %s frame in CAC state", "probe response"); 288332176cfdSRui Paulo vap->iv_stats.is_tx_badstate++; 288432176cfdSRui Paulo return EIO; /* XXX */ 288532176cfdSRui Paulo } 288632176cfdSRui Paulo 288732176cfdSRui Paulo /* 288832176cfdSRui Paulo * Hold a reference on the node so it doesn't go away until after 288932176cfdSRui Paulo * the xmit is complete all the way in the driver. On error we 289032176cfdSRui Paulo * will remove our reference. 289132176cfdSRui Paulo */ 289232176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, 28931e290df3SAntonio Huete Jimenez "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", 2894085ff963SMatthew Dillon __func__, __LINE__, bss, ether_sprintf(bss->ni_macaddr), 289532176cfdSRui Paulo ieee80211_node_refcnt(bss)+1); 289632176cfdSRui Paulo ieee80211_ref_node(bss); 289732176cfdSRui Paulo 289832176cfdSRui Paulo m = ieee80211_alloc_proberesp(bss, legacy); 289932176cfdSRui Paulo if (m == NULL) { 290032176cfdSRui Paulo ieee80211_free_node(bss); 290132176cfdSRui Paulo return ENOMEM; 290232176cfdSRui Paulo } 290332176cfdSRui Paulo 2904b5523eacSSascha Wildner M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); 290532176cfdSRui Paulo KASSERT(m != NULL, ("no room for header")); 290632176cfdSRui Paulo 2907085ff963SMatthew Dillon IEEE80211_TX_LOCK(ic); 2908085ff963SMatthew Dillon wh = mtod(m, struct ieee80211_frame *); 290932176cfdSRui Paulo ieee80211_send_setup(bss, m, 291032176cfdSRui Paulo IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP, 291132176cfdSRui Paulo IEEE80211_NONQOS_TID, vap->iv_myaddr, da, bss->ni_bssid); 291232176cfdSRui Paulo /* XXX power management? */ 291332176cfdSRui Paulo m->m_flags |= M_ENCAP; /* mark encapsulated */ 291432176cfdSRui Paulo 291532176cfdSRui Paulo M_WME_SETAC(m, WME_AC_BE); 291632176cfdSRui Paulo 291732176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, 29181e290df3SAntonio Huete Jimenez "send probe resp on channel %u to %s%s\n", 2919085ff963SMatthew Dillon ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(da), 292032176cfdSRui Paulo legacy ? " <legacy>" : ""); 292132176cfdSRui Paulo IEEE80211_NODE_STAT(bss, tx_mgmt); 292232176cfdSRui Paulo 2923085ff963SMatthew Dillon ret = ieee80211_raw_output(vap, bss, m, NULL); 2924085ff963SMatthew Dillon IEEE80211_TX_UNLOCK(ic); 2925085ff963SMatthew Dillon return (ret); 292632176cfdSRui Paulo } 292732176cfdSRui Paulo 292832176cfdSRui Paulo /* 292932176cfdSRui Paulo * Allocate and build a RTS (Request To Send) control frame. 293032176cfdSRui Paulo */ 293132176cfdSRui Paulo struct mbuf * 293232176cfdSRui Paulo ieee80211_alloc_rts(struct ieee80211com *ic, 293332176cfdSRui Paulo const uint8_t ra[IEEE80211_ADDR_LEN], 293432176cfdSRui Paulo const uint8_t ta[IEEE80211_ADDR_LEN], 293532176cfdSRui Paulo uint16_t dur) 293632176cfdSRui Paulo { 293732176cfdSRui Paulo struct ieee80211_frame_rts *rts; 293832176cfdSRui Paulo struct mbuf *m; 293932176cfdSRui Paulo 294032176cfdSRui Paulo /* XXX honor ic_headroom */ 2941b5523eacSSascha Wildner m = m_gethdr(M_NOWAIT, MT_DATA); 294232176cfdSRui Paulo if (m != NULL) { 294332176cfdSRui Paulo rts = mtod(m, struct ieee80211_frame_rts *); 294432176cfdSRui Paulo rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | 294532176cfdSRui Paulo IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_RTS; 294632176cfdSRui Paulo rts->i_fc[1] = IEEE80211_FC1_DIR_NODS; 294732176cfdSRui Paulo *(u_int16_t *)rts->i_dur = htole16(dur); 294832176cfdSRui Paulo IEEE80211_ADDR_COPY(rts->i_ra, ra); 294932176cfdSRui Paulo IEEE80211_ADDR_COPY(rts->i_ta, ta); 295032176cfdSRui Paulo 295132176cfdSRui Paulo m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts); 295232176cfdSRui Paulo } 295332176cfdSRui Paulo return m; 295432176cfdSRui Paulo } 295532176cfdSRui Paulo 295632176cfdSRui Paulo /* 295732176cfdSRui Paulo * Allocate and build a CTS (Clear To Send) control frame. 295832176cfdSRui Paulo */ 295932176cfdSRui Paulo struct mbuf * 296032176cfdSRui Paulo ieee80211_alloc_cts(struct ieee80211com *ic, 296132176cfdSRui Paulo const uint8_t ra[IEEE80211_ADDR_LEN], uint16_t dur) 296232176cfdSRui Paulo { 296332176cfdSRui Paulo struct ieee80211_frame_cts *cts; 296432176cfdSRui Paulo struct mbuf *m; 296532176cfdSRui Paulo 296632176cfdSRui Paulo /* XXX honor ic_headroom */ 2967b5523eacSSascha Wildner m = m_gethdr(M_NOWAIT, MT_DATA); 296832176cfdSRui Paulo if (m != NULL) { 296932176cfdSRui Paulo cts = mtod(m, struct ieee80211_frame_cts *); 297032176cfdSRui Paulo cts->i_fc[0] = IEEE80211_FC0_VERSION_0 | 297132176cfdSRui Paulo IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_CTS; 297232176cfdSRui Paulo cts->i_fc[1] = IEEE80211_FC1_DIR_NODS; 297332176cfdSRui Paulo *(u_int16_t *)cts->i_dur = htole16(dur); 297432176cfdSRui Paulo IEEE80211_ADDR_COPY(cts->i_ra, ra); 297532176cfdSRui Paulo 297632176cfdSRui Paulo m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts); 297732176cfdSRui Paulo } 297832176cfdSRui Paulo return m; 297932176cfdSRui Paulo } 298032176cfdSRui Paulo 298132176cfdSRui Paulo static void 2982085ff963SMatthew Dillon ieee80211_tx_mgt_timeout(void *arg) 298332176cfdSRui Paulo { 2984085ff963SMatthew Dillon struct ieee80211vap *vap = arg; 298532176cfdSRui Paulo 2986085ff963SMatthew Dillon IEEE80211_LOCK(vap->iv_ic); 298732176cfdSRui Paulo if (vap->iv_state != IEEE80211_S_INIT && 298832176cfdSRui Paulo (vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0) { 298932176cfdSRui Paulo /* 299032176cfdSRui Paulo * NB: it's safe to specify a timeout as the reason here; 299132176cfdSRui Paulo * it'll only be used in the right state. 299232176cfdSRui Paulo */ 2993085ff963SMatthew Dillon ieee80211_new_state_locked(vap, IEEE80211_S_SCAN, 299432176cfdSRui Paulo IEEE80211_SCAN_FAIL_TIMEOUT); 299532176cfdSRui Paulo } 2996085ff963SMatthew Dillon IEEE80211_UNLOCK(vap->iv_ic); 299732176cfdSRui Paulo } 299832176cfdSRui Paulo 2999085ff963SMatthew Dillon /* 3000085ff963SMatthew Dillon * This is the callback set on net80211-sourced transmitted 3001085ff963SMatthew Dillon * authentication request frames. 3002085ff963SMatthew Dillon * 3003085ff963SMatthew Dillon * This does a couple of things: 3004085ff963SMatthew Dillon * 3005085ff963SMatthew Dillon * + If the frame transmitted was a success, it schedules a future 3006085ff963SMatthew Dillon * event which will transition the interface to scan. 3007085ff963SMatthew Dillon * If a state transition _then_ occurs before that event occurs, 3008085ff963SMatthew Dillon * said state transition will cancel this callout. 3009085ff963SMatthew Dillon * 3010085ff963SMatthew Dillon * + If the frame transmit was a failure, it immediately schedules 3011085ff963SMatthew Dillon * the transition back to scan. 3012085ff963SMatthew Dillon */ 301332176cfdSRui Paulo static void 301432176cfdSRui Paulo ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) 301532176cfdSRui Paulo { 301632176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 301732176cfdSRui Paulo enum ieee80211_state ostate = (enum ieee80211_state) arg; 301832176cfdSRui Paulo 301932176cfdSRui Paulo /* 302032176cfdSRui Paulo * Frame transmit completed; arrange timer callback. If 30214f655ef5SMatthew Dillon * transmit was successfully we wait for response. Otherwise 302232176cfdSRui Paulo * we arrange an immediate callback instead of doing the 302332176cfdSRui Paulo * callback directly since we don't know what state the driver 302432176cfdSRui Paulo * is in (e.g. what locks it is holding). This work should 302532176cfdSRui Paulo * not be too time-critical and not happen too often so the 302632176cfdSRui Paulo * added overhead is acceptable. 302732176cfdSRui Paulo * 302832176cfdSRui Paulo * XXX what happens if !acked but response shows up before callback? 302932176cfdSRui Paulo */ 3030085ff963SMatthew Dillon if (vap->iv_state == ostate) { 303132176cfdSRui Paulo callout_reset(&vap->iv_mgtsend, 303232176cfdSRui Paulo status == 0 ? IEEE80211_TRANS_WAIT*hz : 0, 3033085ff963SMatthew Dillon ieee80211_tx_mgt_timeout, vap); 3034085ff963SMatthew Dillon } 303532176cfdSRui Paulo } 303632176cfdSRui Paulo 303732176cfdSRui Paulo static void 303832176cfdSRui Paulo ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, 30394f655ef5SMatthew Dillon struct ieee80211_node *ni) 304032176cfdSRui Paulo { 304132176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 30424f655ef5SMatthew Dillon struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; 304332176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic; 304432176cfdSRui Paulo struct ieee80211_rateset *rs = &ni->ni_rates; 3045841ab66cSSepherosa Ziehau uint16_t capinfo; 3046841ab66cSSepherosa Ziehau 3047841ab66cSSepherosa Ziehau /* 3048841ab66cSSepherosa Ziehau * beacon frame format 3049841ab66cSSepherosa Ziehau * [8] time stamp 3050841ab66cSSepherosa Ziehau * [2] beacon interval 3051841ab66cSSepherosa Ziehau * [2] cabability information 3052841ab66cSSepherosa Ziehau * [tlv] ssid 3053841ab66cSSepherosa Ziehau * [tlv] supported rates 3054841ab66cSSepherosa Ziehau * [3] parameter set (DS) 305532176cfdSRui Paulo * [8] CF parameter set (optional) 3056841ab66cSSepherosa Ziehau * [tlv] parameter set (IBSS/TIM) 305732176cfdSRui Paulo * [tlv] country (optional) 305832176cfdSRui Paulo * [3] power control (optional) 305932176cfdSRui Paulo * [5] channel switch announcement (CSA) (optional) 3060841ab66cSSepherosa Ziehau * [tlv] extended rate phy (ERP) 3061841ab66cSSepherosa Ziehau * [tlv] extended supported rates 306232176cfdSRui Paulo * [tlv] RSN parameters 306332176cfdSRui Paulo * [tlv] HT capabilities 306432176cfdSRui Paulo * [tlv] HT information 3065841ab66cSSepherosa Ziehau * XXX Vendor-specific OIDs (e.g. Atheros) 306632176cfdSRui Paulo * [tlv] WPA parameters 306732176cfdSRui Paulo * [tlv] WME parameters 306832176cfdSRui Paulo * [tlv] Vendor OUI HT capabilities (optional) 306932176cfdSRui Paulo * [tlv] Vendor OUI HT information (optional) 307032176cfdSRui Paulo * [tlv] Atheros capabilities (optional) 307132176cfdSRui Paulo * [tlv] TDMA parameters (optional) 307232176cfdSRui Paulo * [tlv] Mesh ID (MBSS) 307332176cfdSRui Paulo * [tlv] Mesh Conf (MBSS) 307432176cfdSRui Paulo * [tlv] application data (optional) 3075841ab66cSSepherosa Ziehau */ 307632176cfdSRui Paulo 307732176cfdSRui Paulo memset(bo, 0, sizeof(*bo)); 3078841ab66cSSepherosa Ziehau 3079841ab66cSSepherosa Ziehau memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */ 3080841ab66cSSepherosa Ziehau frm += 8; 3081841ab66cSSepherosa Ziehau *(uint16_t *)frm = htole16(ni->ni_intval); 3082841ab66cSSepherosa Ziehau frm += 2; 308332176cfdSRui Paulo capinfo = ieee80211_getcapinfo(vap, ni->ni_chan); 3084841ab66cSSepherosa Ziehau bo->bo_caps = (uint16_t *)frm; 3085841ab66cSSepherosa Ziehau *(uint16_t *)frm = htole16(capinfo); 3086841ab66cSSepherosa Ziehau frm += 2; 3087841ab66cSSepherosa Ziehau *frm++ = IEEE80211_ELEMID_SSID; 308832176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_HIDESSID) == 0) { 3089841ab66cSSepherosa Ziehau *frm++ = ni->ni_esslen; 3090841ab66cSSepherosa Ziehau memcpy(frm, ni->ni_essid, ni->ni_esslen); 3091841ab66cSSepherosa Ziehau frm += ni->ni_esslen; 3092841ab66cSSepherosa Ziehau } else 3093841ab66cSSepherosa Ziehau *frm++ = 0; 3094841ab66cSSepherosa Ziehau frm = ieee80211_add_rates(frm, rs); 309532176cfdSRui Paulo if (!IEEE80211_IS_CHAN_FHSS(ni->ni_chan)) { 3096841ab66cSSepherosa Ziehau *frm++ = IEEE80211_ELEMID_DSPARMS; 3097841ab66cSSepherosa Ziehau *frm++ = 1; 3098841ab66cSSepherosa Ziehau *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); 3099841ab66cSSepherosa Ziehau } 310032176cfdSRui Paulo if (ic->ic_flags & IEEE80211_F_PCF) { 310132176cfdSRui Paulo bo->bo_cfp = frm; 310232176cfdSRui Paulo frm = ieee80211_add_cfparms(frm, ic); 310332176cfdSRui Paulo } 3104841ab66cSSepherosa Ziehau bo->bo_tim = frm; 310532176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_IBSS) { 3106841ab66cSSepherosa Ziehau *frm++ = IEEE80211_ELEMID_IBSSPARMS; 3107841ab66cSSepherosa Ziehau *frm++ = 2; 3108841ab66cSSepherosa Ziehau *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ 3109841ab66cSSepherosa Ziehau bo->bo_tim_len = 0; 311032176cfdSRui Paulo } else if (vap->iv_opmode == IEEE80211_M_HOSTAP || 311132176cfdSRui Paulo vap->iv_opmode == IEEE80211_M_MBSS) { 311232176cfdSRui Paulo /* TIM IE is the same for Mesh and Hostap */ 3113841ab66cSSepherosa Ziehau struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm; 3114841ab66cSSepherosa Ziehau 3115841ab66cSSepherosa Ziehau tie->tim_ie = IEEE80211_ELEMID_TIM; 3116841ab66cSSepherosa Ziehau tie->tim_len = 4; /* length */ 3117841ab66cSSepherosa Ziehau tie->tim_count = 0; /* DTIM count */ 311832176cfdSRui Paulo tie->tim_period = vap->iv_dtim_period; /* DTIM period */ 3119841ab66cSSepherosa Ziehau tie->tim_bitctl = 0; /* bitmap control */ 3120841ab66cSSepherosa Ziehau tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */ 3121841ab66cSSepherosa Ziehau frm += sizeof(struct ieee80211_tim_ie); 3122841ab66cSSepherosa Ziehau bo->bo_tim_len = 1; 3123841ab66cSSepherosa Ziehau } 312432176cfdSRui Paulo bo->bo_tim_trailer = frm; 312532176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_DOTH) || 312632176cfdSRui Paulo (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) 312732176cfdSRui Paulo frm = ieee80211_add_countryie(frm, ic); 312832176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_DOTH) { 312932176cfdSRui Paulo if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) 313032176cfdSRui Paulo frm = ieee80211_add_powerconstraint(frm, vap); 313132176cfdSRui Paulo bo->bo_csa = frm; 313232176cfdSRui Paulo if (ic->ic_flags & IEEE80211_F_CSAPENDING) 313332176cfdSRui Paulo frm = ieee80211_add_csa(frm, vap); 313432176cfdSRui Paulo } else 313532176cfdSRui Paulo bo->bo_csa = frm; 3136085ff963SMatthew Dillon 3137085ff963SMatthew Dillon if (vap->iv_flags & IEEE80211_F_DOTH) { 3138085ff963SMatthew Dillon bo->bo_quiet = frm; 3139085ff963SMatthew Dillon if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && 3140085ff963SMatthew Dillon (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) { 3141085ff963SMatthew Dillon if (vap->iv_quiet) 3142085ff963SMatthew Dillon frm = ieee80211_add_quiet(frm,vap); 3143085ff963SMatthew Dillon } 3144085ff963SMatthew Dillon } else 3145085ff963SMatthew Dillon bo->bo_quiet = frm; 3146085ff963SMatthew Dillon 314732176cfdSRui Paulo if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) { 3148841ab66cSSepherosa Ziehau bo->bo_erp = frm; 3149841ab66cSSepherosa Ziehau frm = ieee80211_add_erp(frm, ic); 3150841ab66cSSepherosa Ziehau } 315132176cfdSRui Paulo frm = ieee80211_add_xrates(frm, rs); 3152085ff963SMatthew Dillon frm = ieee80211_add_rsn(frm, vap); 315332176cfdSRui Paulo if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { 315432176cfdSRui Paulo frm = ieee80211_add_htcap(frm, ni); 315532176cfdSRui Paulo bo->bo_htinfo = frm; 315632176cfdSRui Paulo frm = ieee80211_add_htinfo(frm, ni); 315732176cfdSRui Paulo } 3158085ff963SMatthew Dillon frm = ieee80211_add_wpa(frm, vap); 315932176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_WME) { 316032176cfdSRui Paulo bo->bo_wme = frm; 316132176cfdSRui Paulo frm = ieee80211_add_wme_param(frm, &ic->ic_wme); 316232176cfdSRui Paulo } 316332176cfdSRui Paulo if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && 316432176cfdSRui Paulo (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT)) { 316532176cfdSRui Paulo frm = ieee80211_add_htcap_vendor(frm, ni); 316632176cfdSRui Paulo frm = ieee80211_add_htinfo_vendor(frm, ni); 316732176cfdSRui Paulo } 316832176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 316932176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_ATHEROS) { 317032176cfdSRui Paulo bo->bo_ath = frm; 317132176cfdSRui Paulo frm = ieee80211_add_athcaps(frm, ni); 317232176cfdSRui Paulo } 317332176cfdSRui Paulo #endif 317432176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_TDMA 317532176cfdSRui Paulo if (vap->iv_caps & IEEE80211_C_TDMA) { 317632176cfdSRui Paulo bo->bo_tdma = frm; 317732176cfdSRui Paulo frm = ieee80211_add_tdma(frm, vap); 317832176cfdSRui Paulo } 317932176cfdSRui Paulo #endif 318032176cfdSRui Paulo if (vap->iv_appie_beacon != NULL) { 318132176cfdSRui Paulo bo->bo_appie = frm; 318232176cfdSRui Paulo bo->bo_appie_len = vap->iv_appie_beacon->ie_len; 318332176cfdSRui Paulo frm = add_appie(frm, vap->iv_appie_beacon); 318432176cfdSRui Paulo } 318532176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 318632176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_MBSS) { 318732176cfdSRui Paulo frm = ieee80211_add_meshid(frm, vap); 318832176cfdSRui Paulo bo->bo_meshconf = frm; 318932176cfdSRui Paulo frm = ieee80211_add_meshconf(frm, vap); 319032176cfdSRui Paulo } 319132176cfdSRui Paulo #endif 319232176cfdSRui Paulo bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer; 319332176cfdSRui Paulo bo->bo_csa_trailer_len = frm - bo->bo_csa; 319432176cfdSRui Paulo m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); 319532176cfdSRui Paulo } 319632176cfdSRui Paulo 319732176cfdSRui Paulo /* 319832176cfdSRui Paulo * Allocate a beacon frame and fillin the appropriate bits. 319932176cfdSRui Paulo */ 320032176cfdSRui Paulo struct mbuf * 32014f655ef5SMatthew Dillon ieee80211_beacon_alloc(struct ieee80211_node *ni) 320232176cfdSRui Paulo { 320332176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 320432176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic; 320532176cfdSRui Paulo struct ifnet *ifp = vap->iv_ifp; 320632176cfdSRui Paulo struct ieee80211_frame *wh; 320732176cfdSRui Paulo struct mbuf *m; 320832176cfdSRui Paulo int pktlen; 320932176cfdSRui Paulo uint8_t *frm; 321032176cfdSRui Paulo 321132176cfdSRui Paulo /* 321232176cfdSRui Paulo * beacon frame format 321332176cfdSRui Paulo * [8] time stamp 321432176cfdSRui Paulo * [2] beacon interval 321532176cfdSRui Paulo * [2] cabability information 321632176cfdSRui Paulo * [tlv] ssid 321732176cfdSRui Paulo * [tlv] supported rates 321832176cfdSRui Paulo * [3] parameter set (DS) 321932176cfdSRui Paulo * [8] CF parameter set (optional) 322032176cfdSRui Paulo * [tlv] parameter set (IBSS/TIM) 322132176cfdSRui Paulo * [tlv] country (optional) 322232176cfdSRui Paulo * [3] power control (optional) 322332176cfdSRui Paulo * [5] channel switch announcement (CSA) (optional) 322432176cfdSRui Paulo * [tlv] extended rate phy (ERP) 322532176cfdSRui Paulo * [tlv] extended supported rates 322632176cfdSRui Paulo * [tlv] RSN parameters 322732176cfdSRui Paulo * [tlv] HT capabilities 322832176cfdSRui Paulo * [tlv] HT information 322932176cfdSRui Paulo * [tlv] Vendor OUI HT capabilities (optional) 323032176cfdSRui Paulo * [tlv] Vendor OUI HT information (optional) 323132176cfdSRui Paulo * XXX Vendor-specific OIDs (e.g. Atheros) 323232176cfdSRui Paulo * [tlv] WPA parameters 323332176cfdSRui Paulo * [tlv] WME parameters 323432176cfdSRui Paulo * [tlv] TDMA parameters (optional) 323532176cfdSRui Paulo * [tlv] Mesh ID (MBSS) 323632176cfdSRui Paulo * [tlv] Mesh Conf (MBSS) 323732176cfdSRui Paulo * [tlv] application data (optional) 323832176cfdSRui Paulo * NB: we allocate the max space required for the TIM bitmap. 323932176cfdSRui Paulo * XXX how big is this? 324032176cfdSRui Paulo */ 324132176cfdSRui Paulo pktlen = 8 /* time stamp */ 324232176cfdSRui Paulo + sizeof(uint16_t) /* beacon interval */ 324332176cfdSRui Paulo + sizeof(uint16_t) /* capabilities */ 324432176cfdSRui Paulo + 2 + ni->ni_esslen /* ssid */ 324532176cfdSRui Paulo + 2 + IEEE80211_RATE_SIZE /* supported rates */ 324632176cfdSRui Paulo + 2 + 1 /* DS parameters */ 324732176cfdSRui Paulo + 2 + 6 /* CF parameters */ 324832176cfdSRui Paulo + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */ 324932176cfdSRui Paulo + IEEE80211_COUNTRY_MAX_SIZE /* country */ 325032176cfdSRui Paulo + 2 + 1 /* power control */ 325132176cfdSRui Paulo + sizeof(struct ieee80211_csa_ie) /* CSA */ 3252085ff963SMatthew Dillon + sizeof(struct ieee80211_quiet_ie) /* Quiet */ 325332176cfdSRui Paulo + 2 + 1 /* ERP */ 325432176cfdSRui Paulo + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) 325532176cfdSRui Paulo + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ 325632176cfdSRui Paulo 2*sizeof(struct ieee80211_ie_wpa) : 0) 325732176cfdSRui Paulo /* XXX conditional? */ 325832176cfdSRui Paulo + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ 325932176cfdSRui Paulo + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ 326032176cfdSRui Paulo + (vap->iv_caps & IEEE80211_C_WME ? /* WME */ 326132176cfdSRui Paulo sizeof(struct ieee80211_wme_param) : 0) 326232176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 326332176cfdSRui Paulo + sizeof(struct ieee80211_ath_ie) /* ATH */ 326432176cfdSRui Paulo #endif 326532176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_TDMA 326632176cfdSRui Paulo + (vap->iv_caps & IEEE80211_C_TDMA ? /* TDMA */ 326732176cfdSRui Paulo sizeof(struct ieee80211_tdma_param) : 0) 326832176cfdSRui Paulo #endif 326932176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 327032176cfdSRui Paulo + 2 + ni->ni_meshidlen 327132176cfdSRui Paulo + sizeof(struct ieee80211_meshconf_ie) 327232176cfdSRui Paulo #endif 327332176cfdSRui Paulo + IEEE80211_MAX_APPIE 327432176cfdSRui Paulo ; 327532176cfdSRui Paulo m = ieee80211_getmgtframe(&frm, 327632176cfdSRui Paulo ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen); 327732176cfdSRui Paulo if (m == NULL) { 327832176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, 327932176cfdSRui Paulo "%s: cannot get buf; size %u\n", __func__, pktlen); 328032176cfdSRui Paulo vap->iv_stats.is_tx_nobuf++; 328132176cfdSRui Paulo return NULL; 328232176cfdSRui Paulo } 32834f655ef5SMatthew Dillon ieee80211_beacon_construct(m, frm, ni); 3284841ab66cSSepherosa Ziehau 3285b5523eacSSascha Wildner M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); 3286841ab66cSSepherosa Ziehau KASSERT(m != NULL, ("no space for 802.11 header?")); 3287841ab66cSSepherosa Ziehau wh = mtod(m, struct ieee80211_frame *); 3288841ab66cSSepherosa Ziehau wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 3289841ab66cSSepherosa Ziehau IEEE80211_FC0_SUBTYPE_BEACON; 3290841ab66cSSepherosa Ziehau wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 3291841ab66cSSepherosa Ziehau *(uint16_t *)wh->i_dur = 0; 3292841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); 329332176cfdSRui Paulo IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 3294841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); 3295841ab66cSSepherosa Ziehau *(uint16_t *)wh->i_seq = 0; 3296841ab66cSSepherosa Ziehau 3297841ab66cSSepherosa Ziehau return m; 3298841ab66cSSepherosa Ziehau } 3299841ab66cSSepherosa Ziehau 3300841ab66cSSepherosa Ziehau /* 3301841ab66cSSepherosa Ziehau * Update the dynamic parts of a beacon frame based on the current state. 3302841ab66cSSepherosa Ziehau */ 3303841ab66cSSepherosa Ziehau int 33044f655ef5SMatthew Dillon ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast) 3305841ab66cSSepherosa Ziehau { 330632176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap; 33074f655ef5SMatthew Dillon struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; 330832176cfdSRui Paulo struct ieee80211com *ic = ni->ni_ic; 3309841ab66cSSepherosa Ziehau int len_changed = 0; 3310841ab66cSSepherosa Ziehau uint16_t capinfo; 3311085ff963SMatthew Dillon struct ieee80211_frame *wh; 3312085ff963SMatthew Dillon ieee80211_seq seqno; 3313841ab66cSSepherosa Ziehau 3314085ff963SMatthew Dillon IEEE80211_LOCK(ic); 331532176cfdSRui Paulo /* 331632176cfdSRui Paulo * Handle 11h channel change when we've reached the count. 331732176cfdSRui Paulo * We must recalculate the beacon frame contents to account 331832176cfdSRui Paulo * for the new channel. Note we do this only for the first 331932176cfdSRui Paulo * vap that reaches this point; subsequent vaps just update 332032176cfdSRui Paulo * their beacon state to reflect the recalculated channel. 332132176cfdSRui Paulo */ 332232176cfdSRui Paulo if (isset(bo->bo_flags, IEEE80211_BEACON_CSA) && 332332176cfdSRui Paulo vap->iv_csa_count == ic->ic_csa_count) { 332432176cfdSRui Paulo vap->iv_csa_count = 0; 332532176cfdSRui Paulo /* 332632176cfdSRui Paulo * Effect channel change before reconstructing the beacon 332732176cfdSRui Paulo * frame contents as many places reference ni_chan. 332832176cfdSRui Paulo */ 332932176cfdSRui Paulo if (ic->ic_csa_newchan != NULL) 333032176cfdSRui Paulo ieee80211_csa_completeswitch(ic); 333132176cfdSRui Paulo /* 333232176cfdSRui Paulo * NB: ieee80211_beacon_construct clears all pending 333332176cfdSRui Paulo * updates in bo_flags so we don't need to explicitly 333432176cfdSRui Paulo * clear IEEE80211_BEACON_CSA. 333532176cfdSRui Paulo */ 333632176cfdSRui Paulo ieee80211_beacon_construct(m, 33374f655ef5SMatthew Dillon mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), ni); 333832176cfdSRui Paulo 333932176cfdSRui Paulo /* XXX do WME aggressive mode processing? */ 3340085ff963SMatthew Dillon IEEE80211_UNLOCK(ic); 334132176cfdSRui Paulo return 1; /* just assume length changed */ 334232176cfdSRui Paulo } 3343841ab66cSSepherosa Ziehau 3344085ff963SMatthew Dillon wh = mtod(m, struct ieee80211_frame *); 3345085ff963SMatthew Dillon seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; 3346085ff963SMatthew Dillon *(uint16_t *)&wh->i_seq[0] = 3347085ff963SMatthew Dillon htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); 3348085ff963SMatthew Dillon M_SEQNO_SET(m, seqno); 3349085ff963SMatthew Dillon 3350841ab66cSSepherosa Ziehau /* XXX faster to recalculate entirely or just changes? */ 335132176cfdSRui Paulo capinfo = ieee80211_getcapinfo(vap, ni->ni_chan); 3352841ab66cSSepherosa Ziehau *bo->bo_caps = htole16(capinfo); 3353841ab66cSSepherosa Ziehau 335432176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_WME) { 3355841ab66cSSepherosa Ziehau struct ieee80211_wme_state *wme = &ic->ic_wme; 3356841ab66cSSepherosa Ziehau 3357841ab66cSSepherosa Ziehau /* 33584f655ef5SMatthew Dillon * Check for aggressive mode change. When there is 3359841ab66cSSepherosa Ziehau * significant high priority traffic in the BSS 3360841ab66cSSepherosa Ziehau * throttle back BE traffic by using conservative 33614f655ef5SMatthew Dillon * parameters. Otherwise BE uses aggressive params 3362841ab66cSSepherosa Ziehau * to optimize performance of legacy/non-QoS traffic. 3363841ab66cSSepherosa Ziehau */ 3364841ab66cSSepherosa Ziehau if (wme->wme_flags & WME_F_AGGRMODE) { 3365841ab66cSSepherosa Ziehau if (wme->wme_hipri_traffic > 3366841ab66cSSepherosa Ziehau wme->wme_hipri_switch_thresh) { 336732176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 3368841ab66cSSepherosa Ziehau "%s: traffic %u, disable aggressive mode\n", 3369841ab66cSSepherosa Ziehau __func__, wme->wme_hipri_traffic); 3370841ab66cSSepherosa Ziehau wme->wme_flags &= ~WME_F_AGGRMODE; 337132176cfdSRui Paulo ieee80211_wme_updateparams_locked(vap); 3372841ab66cSSepherosa Ziehau wme->wme_hipri_traffic = 3373841ab66cSSepherosa Ziehau wme->wme_hipri_switch_hysteresis; 3374841ab66cSSepherosa Ziehau } else 3375841ab66cSSepherosa Ziehau wme->wme_hipri_traffic = 0; 3376841ab66cSSepherosa Ziehau } else { 3377841ab66cSSepherosa Ziehau if (wme->wme_hipri_traffic <= 3378841ab66cSSepherosa Ziehau wme->wme_hipri_switch_thresh) { 337932176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 3380841ab66cSSepherosa Ziehau "%s: traffic %u, enable aggressive mode\n", 3381841ab66cSSepherosa Ziehau __func__, wme->wme_hipri_traffic); 3382841ab66cSSepherosa Ziehau wme->wme_flags |= WME_F_AGGRMODE; 338332176cfdSRui Paulo ieee80211_wme_updateparams_locked(vap); 3384841ab66cSSepherosa Ziehau wme->wme_hipri_traffic = 0; 3385841ab66cSSepherosa Ziehau } else 3386841ab66cSSepherosa Ziehau wme->wme_hipri_traffic = 3387841ab66cSSepherosa Ziehau wme->wme_hipri_switch_hysteresis; 3388841ab66cSSepherosa Ziehau } 338932176cfdSRui Paulo if (isset(bo->bo_flags, IEEE80211_BEACON_WME)) { 3390841ab66cSSepherosa Ziehau (void) ieee80211_add_wme_param(bo->bo_wme, wme); 339132176cfdSRui Paulo clrbit(bo->bo_flags, IEEE80211_BEACON_WME); 3392841ab66cSSepherosa Ziehau } 3393841ab66cSSepherosa Ziehau } 3394841ab66cSSepherosa Ziehau 339532176cfdSRui Paulo if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) { 339632176cfdSRui Paulo ieee80211_ht_update_beacon(vap, bo); 339732176cfdSRui Paulo clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO); 339832176cfdSRui Paulo } 339932176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_TDMA 340032176cfdSRui Paulo if (vap->iv_caps & IEEE80211_C_TDMA) { 340132176cfdSRui Paulo /* 340232176cfdSRui Paulo * NB: the beacon is potentially updated every TBTT. 340332176cfdSRui Paulo */ 340432176cfdSRui Paulo ieee80211_tdma_update_beacon(vap, bo); 340532176cfdSRui Paulo } 340632176cfdSRui Paulo #endif 340732176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_MESH 340832176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_MBSS) 340932176cfdSRui Paulo ieee80211_mesh_update_beacon(vap, bo); 341032176cfdSRui Paulo #endif 341132176cfdSRui Paulo 341232176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_HOSTAP || 341332176cfdSRui Paulo vap->iv_opmode == IEEE80211_M_MBSS) { /* NB: no IBSS support*/ 3414841ab66cSSepherosa Ziehau struct ieee80211_tim_ie *tie = 3415841ab66cSSepherosa Ziehau (struct ieee80211_tim_ie *) bo->bo_tim; 341632176cfdSRui Paulo if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) { 3417841ab66cSSepherosa Ziehau u_int timlen, timoff, i; 3418841ab66cSSepherosa Ziehau /* 3419841ab66cSSepherosa Ziehau * ATIM/DTIM needs updating. If it fits in the 3420841ab66cSSepherosa Ziehau * current space allocated then just copy in the 3421841ab66cSSepherosa Ziehau * new bits. Otherwise we need to move any trailing 3422841ab66cSSepherosa Ziehau * data to make room. Note that we know there is 3423841ab66cSSepherosa Ziehau * contiguous space because ieee80211_beacon_allocate 3424841ab66cSSepherosa Ziehau * insures there is space in the mbuf to write a 342532176cfdSRui Paulo * maximal-size virtual bitmap (based on iv_max_aid). 3426841ab66cSSepherosa Ziehau */ 3427841ab66cSSepherosa Ziehau /* 3428841ab66cSSepherosa Ziehau * Calculate the bitmap size and offset, copy any 3429841ab66cSSepherosa Ziehau * trailer out of the way, and then copy in the 3430841ab66cSSepherosa Ziehau * new bitmap and update the information element. 3431841ab66cSSepherosa Ziehau * Note that the tim bitmap must contain at least 3432841ab66cSSepherosa Ziehau * one byte and any offset must be even. 3433841ab66cSSepherosa Ziehau */ 343432176cfdSRui Paulo if (vap->iv_ps_pending != 0) { 3435841ab66cSSepherosa Ziehau timoff = 128; /* impossibly large */ 343632176cfdSRui Paulo for (i = 0; i < vap->iv_tim_len; i++) 343732176cfdSRui Paulo if (vap->iv_tim_bitmap[i]) { 3438841ab66cSSepherosa Ziehau timoff = i &~ 1; 3439841ab66cSSepherosa Ziehau break; 3440841ab66cSSepherosa Ziehau } 3441841ab66cSSepherosa Ziehau KASSERT(timoff != 128, ("tim bitmap empty!")); 344232176cfdSRui Paulo for (i = vap->iv_tim_len-1; i >= timoff; i--) 344332176cfdSRui Paulo if (vap->iv_tim_bitmap[i]) 3444841ab66cSSepherosa Ziehau break; 3445841ab66cSSepherosa Ziehau timlen = 1 + (i - timoff); 3446841ab66cSSepherosa Ziehau } else { 3447841ab66cSSepherosa Ziehau timoff = 0; 3448841ab66cSSepherosa Ziehau timlen = 1; 3449841ab66cSSepherosa Ziehau } 3450841ab66cSSepherosa Ziehau if (timlen != bo->bo_tim_len) { 3451841ab66cSSepherosa Ziehau /* copy up/down trailer */ 3452841ab66cSSepherosa Ziehau int adjust = tie->tim_bitmap+timlen 345332176cfdSRui Paulo - bo->bo_tim_trailer; 3454*afd2da4dSMatthew Dillon bcopy(bo->bo_tim_trailer, 345532176cfdSRui Paulo bo->bo_tim_trailer+adjust, 345632176cfdSRui Paulo bo->bo_tim_trailer_len); 345732176cfdSRui Paulo bo->bo_tim_trailer += adjust; 3458841ab66cSSepherosa Ziehau bo->bo_erp += adjust; 345932176cfdSRui Paulo bo->bo_htinfo += adjust; 3460085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_SUPERG 346132176cfdSRui Paulo bo->bo_ath += adjust; 346232176cfdSRui Paulo #endif 3463085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_TDMA 346432176cfdSRui Paulo bo->bo_tdma += adjust; 346532176cfdSRui Paulo #endif 3466085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_MESH 346732176cfdSRui Paulo bo->bo_meshconf += adjust; 346832176cfdSRui Paulo #endif 346932176cfdSRui Paulo bo->bo_appie += adjust; 347032176cfdSRui Paulo bo->bo_wme += adjust; 347132176cfdSRui Paulo bo->bo_csa += adjust; 3472085ff963SMatthew Dillon bo->bo_quiet += adjust; 3473841ab66cSSepherosa Ziehau bo->bo_tim_len = timlen; 3474841ab66cSSepherosa Ziehau 3475841ab66cSSepherosa Ziehau /* update information element */ 3476841ab66cSSepherosa Ziehau tie->tim_len = 3 + timlen; 3477841ab66cSSepherosa Ziehau tie->tim_bitctl = timoff; 3478841ab66cSSepherosa Ziehau len_changed = 1; 3479841ab66cSSepherosa Ziehau } 348032176cfdSRui Paulo memcpy(tie->tim_bitmap, vap->iv_tim_bitmap + timoff, 3481841ab66cSSepherosa Ziehau bo->bo_tim_len); 3482841ab66cSSepherosa Ziehau 348332176cfdSRui Paulo clrbit(bo->bo_flags, IEEE80211_BEACON_TIM); 3484841ab66cSSepherosa Ziehau 348532176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, 3486841ab66cSSepherosa Ziehau "%s: TIM updated, pending %u, off %u, len %u\n", 348732176cfdSRui Paulo __func__, vap->iv_ps_pending, timoff, timlen); 3488841ab66cSSepherosa Ziehau } 3489841ab66cSSepherosa Ziehau /* count down DTIM period */ 3490841ab66cSSepherosa Ziehau if (tie->tim_count == 0) 3491841ab66cSSepherosa Ziehau tie->tim_count = tie->tim_period - 1; 3492841ab66cSSepherosa Ziehau else 3493841ab66cSSepherosa Ziehau tie->tim_count--; 3494841ab66cSSepherosa Ziehau /* update state for buffered multicast frames on DTIM */ 3495841ab66cSSepherosa Ziehau if (mcast && tie->tim_count == 0) 3496841ab66cSSepherosa Ziehau tie->tim_bitctl |= 1; 3497841ab66cSSepherosa Ziehau else 3498841ab66cSSepherosa Ziehau tie->tim_bitctl &= ~1; 349932176cfdSRui Paulo if (isset(bo->bo_flags, IEEE80211_BEACON_CSA)) { 350032176cfdSRui Paulo struct ieee80211_csa_ie *csa = 350132176cfdSRui Paulo (struct ieee80211_csa_ie *) bo->bo_csa; 350232176cfdSRui Paulo 350332176cfdSRui Paulo /* 350432176cfdSRui Paulo * Insert or update CSA ie. If we're just starting 350532176cfdSRui Paulo * to count down to the channel switch then we need 350632176cfdSRui Paulo * to insert the CSA ie. Otherwise we just need to 350732176cfdSRui Paulo * drop the count. The actual change happens above 350832176cfdSRui Paulo * when the vap's count reaches the target count. 350932176cfdSRui Paulo */ 351032176cfdSRui Paulo if (vap->iv_csa_count == 0) { 351132176cfdSRui Paulo memmove(&csa[1], csa, bo->bo_csa_trailer_len); 351232176cfdSRui Paulo bo->bo_erp += sizeof(*csa); 351332176cfdSRui Paulo bo->bo_htinfo += sizeof(*csa); 351432176cfdSRui Paulo bo->bo_wme += sizeof(*csa); 3515085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_SUPERG 351632176cfdSRui Paulo bo->bo_ath += sizeof(*csa); 351732176cfdSRui Paulo #endif 3518085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_TDMA 351932176cfdSRui Paulo bo->bo_tdma += sizeof(*csa); 352032176cfdSRui Paulo #endif 3521085ff963SMatthew Dillon #ifdef IEEE80211_SUPPORT_MESH 352232176cfdSRui Paulo bo->bo_meshconf += sizeof(*csa); 352332176cfdSRui Paulo #endif 352432176cfdSRui Paulo bo->bo_appie += sizeof(*csa); 352532176cfdSRui Paulo bo->bo_csa_trailer_len += sizeof(*csa); 3526085ff963SMatthew Dillon bo->bo_quiet += sizeof(*csa); 352732176cfdSRui Paulo bo->bo_tim_trailer_len += sizeof(*csa); 352832176cfdSRui Paulo m->m_len += sizeof(*csa); 352932176cfdSRui Paulo m->m_pkthdr.len += sizeof(*csa); 353032176cfdSRui Paulo 353132176cfdSRui Paulo ieee80211_add_csa(bo->bo_csa, vap); 353232176cfdSRui Paulo } else 353332176cfdSRui Paulo csa->csa_count--; 353432176cfdSRui Paulo vap->iv_csa_count++; 353532176cfdSRui Paulo /* NB: don't clear IEEE80211_BEACON_CSA */ 353632176cfdSRui Paulo } 3537085ff963SMatthew Dillon if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && 3538085ff963SMatthew Dillon (vap->iv_flags_ext & IEEE80211_FEXT_DFS) ){ 3539085ff963SMatthew Dillon if (vap->iv_quiet) 3540085ff963SMatthew Dillon ieee80211_add_quiet(bo->bo_quiet, vap); 3541085ff963SMatthew Dillon } 354232176cfdSRui Paulo if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) { 3543841ab66cSSepherosa Ziehau /* 3544841ab66cSSepherosa Ziehau * ERP element needs updating. 3545841ab66cSSepherosa Ziehau */ 3546841ab66cSSepherosa Ziehau (void) ieee80211_add_erp(bo->bo_erp, ic); 354732176cfdSRui Paulo clrbit(bo->bo_flags, IEEE80211_BEACON_ERP); 3548841ab66cSSepherosa Ziehau } 354932176cfdSRui Paulo #ifdef IEEE80211_SUPPORT_SUPERG 355032176cfdSRui Paulo if (isset(bo->bo_flags, IEEE80211_BEACON_ATH)) { 355132176cfdSRui Paulo ieee80211_add_athcaps(bo->bo_ath, ni); 355232176cfdSRui Paulo clrbit(bo->bo_flags, IEEE80211_BEACON_ATH); 35536dd1c373SSepherosa Ziehau } 3554841ab66cSSepherosa Ziehau #endif 3555841ab66cSSepherosa Ziehau } 355632176cfdSRui Paulo if (isset(bo->bo_flags, IEEE80211_BEACON_APPIE)) { 355732176cfdSRui Paulo const struct ieee80211_appie *aie = vap->iv_appie_beacon; 355832176cfdSRui Paulo int aielen; 355932176cfdSRui Paulo uint8_t *frm; 3560841ab66cSSepherosa Ziehau 356132176cfdSRui Paulo aielen = 0; 356232176cfdSRui Paulo if (aie != NULL) 356332176cfdSRui Paulo aielen += aie->ie_len; 356432176cfdSRui Paulo if (aielen != bo->bo_appie_len) { 356532176cfdSRui Paulo /* copy up/down trailer */ 356632176cfdSRui Paulo int adjust = aielen - bo->bo_appie_len; 3567*afd2da4dSMatthew Dillon bcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust, 356832176cfdSRui Paulo bo->bo_tim_trailer_len); 356932176cfdSRui Paulo bo->bo_tim_trailer += adjust; 357032176cfdSRui Paulo bo->bo_appie += adjust; 357132176cfdSRui Paulo bo->bo_appie_len = aielen; 3572841ab66cSSepherosa Ziehau 357332176cfdSRui Paulo len_changed = 1; 3574841ab66cSSepherosa Ziehau } 357532176cfdSRui Paulo frm = bo->bo_appie; 357632176cfdSRui Paulo if (aie != NULL) 357732176cfdSRui Paulo frm = add_appie(frm, aie); 357832176cfdSRui Paulo clrbit(bo->bo_flags, IEEE80211_BEACON_APPIE); 35792d7dda79SSepherosa Ziehau } 3580085ff963SMatthew Dillon IEEE80211_UNLOCK(ic); 35812d7dda79SSepherosa Ziehau 358232176cfdSRui Paulo return len_changed; 3583322b19a8SSepherosa Ziehau } 3584085ff963SMatthew Dillon 3585085ff963SMatthew Dillon /* 3586085ff963SMatthew Dillon * Do Ethernet-LLC encapsulation for each payload in a fast frame 3587085ff963SMatthew Dillon * tunnel encapsulation. The frame is assumed to have an Ethernet 3588085ff963SMatthew Dillon * header at the front that must be stripped before prepending the 3589085ff963SMatthew Dillon * LLC followed by the Ethernet header passed in (with an Ethernet 3590085ff963SMatthew Dillon * type that specifies the payload size). 3591085ff963SMatthew Dillon */ 3592085ff963SMatthew Dillon struct mbuf * 3593085ff963SMatthew Dillon ieee80211_ff_encap1(struct ieee80211vap *vap, struct mbuf *m, 3594085ff963SMatthew Dillon const struct ether_header *eh) 3595085ff963SMatthew Dillon { 3596085ff963SMatthew Dillon struct llc *llc; 3597085ff963SMatthew Dillon uint16_t payload; 3598085ff963SMatthew Dillon 3599085ff963SMatthew Dillon /* XXX optimize by combining m_adj+M_PREPEND */ 3600085ff963SMatthew Dillon m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); 3601085ff963SMatthew Dillon llc = mtod(m, struct llc *); 3602085ff963SMatthew Dillon llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; 3603085ff963SMatthew Dillon llc->llc_control = LLC_UI; 3604085ff963SMatthew Dillon llc->llc_snap.org_code[0] = 0; 3605085ff963SMatthew Dillon llc->llc_snap.org_code[1] = 0; 3606085ff963SMatthew Dillon llc->llc_snap.org_code[2] = 0; 3607085ff963SMatthew Dillon llc->llc_snap.ether_type = eh->ether_type; 3608085ff963SMatthew Dillon payload = m->m_pkthdr.len; /* NB: w/o Ethernet header */ 3609085ff963SMatthew Dillon 3610b5523eacSSascha Wildner M_PREPEND(m, sizeof(struct ether_header), M_NOWAIT); 3611085ff963SMatthew Dillon if (m == NULL) { /* XXX cannot happen */ 3612085ff963SMatthew Dillon IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, 3613085ff963SMatthew Dillon "%s: no space for ether_header\n", __func__); 3614085ff963SMatthew Dillon vap->iv_stats.is_tx_nobuf++; 3615085ff963SMatthew Dillon return NULL; 3616085ff963SMatthew Dillon } 3617085ff963SMatthew Dillon ETHER_HEADER_COPY(mtod(m, void *), eh); 3618085ff963SMatthew Dillon mtod(m, struct ether_header *)->ether_type = htons(payload); 3619085ff963SMatthew Dillon return m; 3620085ff963SMatthew Dillon } 3621085ff963SMatthew Dillon 3622085ff963SMatthew Dillon /* 3623085ff963SMatthew Dillon * Complete an mbuf transmission. 3624085ff963SMatthew Dillon * 3625085ff963SMatthew Dillon * For now, this simply processes a completed frame after the 3626085ff963SMatthew Dillon * driver has completed it's transmission and/or retransmission. 3627085ff963SMatthew Dillon * It assumes the frame is an 802.11 encapsulated frame. 3628085ff963SMatthew Dillon * 3629085ff963SMatthew Dillon * Later on it will grow to become the exit path for a given frame 3630085ff963SMatthew Dillon * from the driver and, depending upon how it's been encapsulated 3631085ff963SMatthew Dillon * and already transmitted, it may end up doing A-MPDU retransmission, 3632085ff963SMatthew Dillon * power save requeuing, etc. 3633085ff963SMatthew Dillon * 3634085ff963SMatthew Dillon * In order for the above to work, the driver entry point to this 3635085ff963SMatthew Dillon * must not hold any driver locks. Thus, the driver needs to delay 3636085ff963SMatthew Dillon * any actual mbuf completion until it can release said locks. 3637085ff963SMatthew Dillon * 3638085ff963SMatthew Dillon * This frees the mbuf and if the mbuf has a node reference, 3639085ff963SMatthew Dillon * the node reference will be freed. 3640085ff963SMatthew Dillon */ 3641085ff963SMatthew Dillon void 3642085ff963SMatthew Dillon ieee80211_tx_complete(struct ieee80211_node *ni, struct mbuf *m, int status) 3643085ff963SMatthew Dillon { 3644085ff963SMatthew Dillon 3645085ff963SMatthew Dillon if (ni != NULL) { 36464f655ef5SMatthew Dillon struct ifnet *ifp = ni->ni_vap->iv_ifp; 36474f655ef5SMatthew Dillon 36484f655ef5SMatthew Dillon if (status == 0) { 36494f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 3650664776f3SImre Vadász #if defined(__DragonFly__) 3651664776f3SImre Vadász /* 3652664776f3SImre Vadász * On DragonFly, IFCOUNTER_OBYTES and 3653664776f3SImre Vadász * IFCOUNTER_OMCASTS increments are currently done 3654664776f3SImre Vadász * by ifq_dispatch() already. 3655664776f3SImre Vadász */ 3656664776f3SImre Vadász #else 3657664776f3SImre Vadász if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len); 36584f655ef5SMatthew Dillon if (m->m_flags & M_MCAST) 36594f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); 3660664776f3SImre Vadász #endif 36614f655ef5SMatthew Dillon } else 36624f655ef5SMatthew Dillon if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 3663085ff963SMatthew Dillon if (m->m_flags & M_TXCB) 3664085ff963SMatthew Dillon ieee80211_process_callback(ni, m, status); 3665085ff963SMatthew Dillon ieee80211_free_node(ni); 3666085ff963SMatthew Dillon } 3667085ff963SMatthew Dillon m_freem(m); 3668085ff963SMatthew Dillon } 3669