1*54fbbda3Sjsg /* $OpenBSD: ieee80211_output.c,v 1.140 2024/09/01 03:09:00 jsg Exp $ */ 291b2158bSmillert /* $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $ */ 391b2158bSmillert 491b2158bSmillert /*- 591b2158bSmillert * Copyright (c) 2001 Atsushi Onoe 691b2158bSmillert * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting 745eec175Sdamien * Copyright (c) 2007-2009 Damien Bergamini 891b2158bSmillert * All rights reserved. 991b2158bSmillert * 1091b2158bSmillert * Redistribution and use in source and binary forms, with or without 1191b2158bSmillert * modification, are permitted provided that the following conditions 1291b2158bSmillert * are met: 1391b2158bSmillert * 1. Redistributions of source code must retain the above copyright 1491b2158bSmillert * notice, this list of conditions and the following disclaimer. 1591b2158bSmillert * 2. Redistributions in binary form must reproduce the above copyright 1691b2158bSmillert * notice, this list of conditions and the following disclaimer in the 1791b2158bSmillert * documentation and/or other materials provided with the distribution. 1891b2158bSmillert * 3. The name of the author may not be used to endorse or promote products 1991b2158bSmillert * derived from this software without specific prior written permission. 2091b2158bSmillert * 2191b2158bSmillert * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2291b2158bSmillert * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2391b2158bSmillert * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2491b2158bSmillert * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2591b2158bSmillert * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2691b2158bSmillert * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2791b2158bSmillert * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2891b2158bSmillert * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2991b2158bSmillert * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3091b2158bSmillert * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3191b2158bSmillert */ 3291b2158bSmillert 3391b2158bSmillert #include "bpfilter.h" 34ef822fe8Sdamien #include "vlan.h" 3591b2158bSmillert 3691b2158bSmillert #include <sys/param.h> 3791b2158bSmillert #include <sys/systm.h> 3891b2158bSmillert #include <sys/mbuf.h> 3991b2158bSmillert #include <sys/kernel.h> 4091b2158bSmillert #include <sys/socket.h> 4191b2158bSmillert #include <sys/sockio.h> 4291b2158bSmillert #include <sys/endian.h> 4391b2158bSmillert #include <sys/errno.h> 4491b2158bSmillert #include <sys/sysctl.h> 4591b2158bSmillert 4691b2158bSmillert #include <net/if.h> 4791b2158bSmillert #include <net/if_dl.h> 4891b2158bSmillert #include <net/if_media.h> 4991b2158bSmillert #include <net/if_llc.h> 5091b2158bSmillert #include <net/bpf.h> 5191b2158bSmillert 5291b2158bSmillert #include <netinet/in.h> 5391b2158bSmillert #include <netinet/if_ether.h> 54ef822fe8Sdamien #include <netinet/ip.h> 553c732269Sdamien #ifdef INET6 563c732269Sdamien #include <netinet/ip6.h> 573c732269Sdamien #endif 58ef822fe8Sdamien 59ef822fe8Sdamien #if NVLAN > 0 60ef822fe8Sdamien #include <net/if_vlan_var.h> 6191b2158bSmillert #endif 6291b2158bSmillert 6391b2158bSmillert #include <net80211/ieee80211_var.h> 646aaa29faSdamien #include <net80211/ieee80211_priv.h> 6591b2158bSmillert 66250085e6Sdamien int ieee80211_mgmt_output(struct ifnet *, struct ieee80211_node *, 67250085e6Sdamien struct mbuf *, int); 68aefc44daSstsp int ieee80211_can_use_ampdu(struct ieee80211com *, 69aefc44daSstsp struct ieee80211_node *); 701247f240Sdamien u_int8_t *ieee80211_add_rsn_body(u_int8_t *, struct ieee80211com *, 711247f240Sdamien const struct ieee80211_node *, int); 72e03e709cSdamien struct mbuf *ieee80211_getmgmt(int, int, u_int); 735d49b5a5Sdamien struct mbuf *ieee80211_get_probe_req(struct ieee80211com *, 745d49b5a5Sdamien struct ieee80211_node *); 75171ac09aSdamien #ifndef IEEE80211_STA_ONLY 76774cd68cSstsp struct mbuf *ieee80211_get_probe_resp(struct ieee80211com *); 77171ac09aSdamien #endif 785d49b5a5Sdamien struct mbuf *ieee80211_get_auth(struct ieee80211com *, 795d49b5a5Sdamien struct ieee80211_node *, u_int16_t, u_int16_t); 805d49b5a5Sdamien struct mbuf *ieee80211_get_deauth(struct ieee80211com *, 815d49b5a5Sdamien struct ieee80211_node *, u_int16_t); 825d49b5a5Sdamien struct mbuf *ieee80211_get_assoc_req(struct ieee80211com *, 835d49b5a5Sdamien struct ieee80211_node *, int); 84171ac09aSdamien #ifndef IEEE80211_STA_ONLY 855d49b5a5Sdamien struct mbuf *ieee80211_get_assoc_resp(struct ieee80211com *, 865d49b5a5Sdamien struct ieee80211_node *, u_int16_t); 87171ac09aSdamien #endif 885d49b5a5Sdamien struct mbuf *ieee80211_get_disassoc(struct ieee80211com *, 895d49b5a5Sdamien struct ieee80211_node *, u_int16_t); 9045eec175Sdamien struct mbuf *ieee80211_get_addba_req(struct ieee80211com *, 9145eec175Sdamien struct ieee80211_node *, u_int8_t); 9245eec175Sdamien struct mbuf *ieee80211_get_addba_resp(struct ieee80211com *, 93ec69e05aSdamien struct ieee80211_node *, u_int8_t, u_int8_t, u_int16_t); 9445eec175Sdamien struct mbuf *ieee80211_get_delba(struct ieee80211com *, 95ec69e05aSdamien struct ieee80211_node *, u_int8_t, u_int8_t, u_int16_t); 966999278eSstsp uint8_t *ieee80211_add_wme_info(uint8_t *, struct ieee80211com *); 97432d6c6bSstsp #ifndef IEEE80211_STA_ONLY 98432d6c6bSstsp uint8_t *ieee80211_add_wme_param(uint8_t *, struct ieee80211com *); 99432d6c6bSstsp #endif 10045eec175Sdamien struct mbuf *ieee80211_get_sa_query(struct ieee80211com *, 10145eec175Sdamien struct ieee80211_node *, u_int8_t); 10245eec175Sdamien struct mbuf *ieee80211_get_action(struct ieee80211com *, 10345eec175Sdamien struct ieee80211_node *, u_int8_t, u_int8_t, int); 104250085e6Sdamien 10591b2158bSmillert /* 106673b59bcSreyk * IEEE 802.11 output routine. Normally this will directly call the 107673b59bcSreyk * Ethernet output routine because 802.11 encapsulation is called 1081bb78573Sdamien * later by the driver. This function can be used to send raw frames 109673b59bcSreyk * if the mbuf has been tagged with a 802.11 data link type. 110673b59bcSreyk */ 111673b59bcSreyk int 112673b59bcSreyk ieee80211_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 113673b59bcSreyk struct rtentry *rt) 114673b59bcSreyk { 115131b80deSdamien struct ieee80211_frame *wh; 116673b59bcSreyk struct m_tag *mtag; 117e9188d0dSmpi int error = 0; 118673b59bcSreyk 119673b59bcSreyk /* Interface has to be up and running */ 120673b59bcSreyk if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != 121673b59bcSreyk (IFF_UP | IFF_RUNNING)) { 122673b59bcSreyk error = ENETDOWN; 123673b59bcSreyk goto bad; 124673b59bcSreyk } 125673b59bcSreyk 126673b59bcSreyk /* Try to get the DLT from a mbuf tag */ 127673b59bcSreyk if ((mtag = m_tag_find(m, PACKET_TAG_DLT, NULL)) != NULL) { 128131b80deSdamien struct ieee80211com *ic = (void *)ifp; 12902bc3a2fSdamien u_int dlt = *(u_int *)(mtag + 1); 130673b59bcSreyk 131673b59bcSreyk /* Fallback to ethernet for non-802.11 linktypes */ 132673b59bcSreyk if (!(dlt == DLT_IEEE802_11 || dlt == DLT_IEEE802_11_RADIO)) 133673b59bcSreyk goto fallback; 134673b59bcSreyk 135131b80deSdamien if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) 136131b80deSdamien return (EINVAL); 137131b80deSdamien wh = mtod(m, struct ieee80211_frame *); 138131b80deSdamien if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != 139131b80deSdamien IEEE80211_FC0_VERSION_0) 140131b80deSdamien return (EINVAL); 1419561b6baSdamien if (!(ic->ic_caps & IEEE80211_C_RAWCTL) && 142131b80deSdamien (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == 143131b80deSdamien IEEE80211_FC0_TYPE_CTL) 144131b80deSdamien return (EINVAL); 145131b80deSdamien 146c38eb4ffSmpi return (if_enqueue(ifp, m)); 147673b59bcSreyk } 148673b59bcSreyk 149673b59bcSreyk fallback: 150673b59bcSreyk return (ether_output(ifp, m, dst, rt)); 151673b59bcSreyk 152673b59bcSreyk bad: 153673b59bcSreyk m_freem(m); 154673b59bcSreyk return (error); 155673b59bcSreyk } 156673b59bcSreyk 1576998360eSstsp const char * 1586998360eSstsp ieee80211_action_name(struct ieee80211_frame *wh) 1596998360eSstsp { 1606998360eSstsp const u_int8_t *frm = (const uint8_t *)&wh[1]; 1616998360eSstsp const char *categ_ba_name[3] = { "addba_req", "addba_resp", "delba" }; 1626998360eSstsp 1636998360eSstsp if (frm[0] == IEEE80211_CATEG_BA && frm[1] < nitems(categ_ba_name)) 1646998360eSstsp return categ_ba_name[frm[1]]; 1656998360eSstsp 1666998360eSstsp return "action"; 1676998360eSstsp } 1686998360eSstsp 169673b59bcSreyk /* 17091b2158bSmillert * Send a management frame to the specified node. The node pointer 17191b2158bSmillert * must have a reference as the pointer will be passed to the driver 17291b2158bSmillert * and potentially held for a long time. If the frame is successfully 17391b2158bSmillert * dispatched to the driver, then it is responsible for freeing the 17491b2158bSmillert * reference (and potentially free'ing up any associated storage). 17591b2158bSmillert */ 176250085e6Sdamien int 17791b2158bSmillert ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni, 17891b2158bSmillert struct mbuf *m, int type) 17991b2158bSmillert { 18091b2158bSmillert struct ieee80211com *ic = (void *)ifp; 18191b2158bSmillert struct ieee80211_frame *wh; 18291b2158bSmillert 183fe6a7506Sjsg if (ni == NULL) 184fe6a7506Sjsg panic("null node"); 18591b2158bSmillert ni->ni_inact = 0; 18691b2158bSmillert 18791b2158bSmillert /* 1886da4b19dSmpi * We want to pass the node down to the driver's start 1896da4b19dSmpi * routine. We could stick this in an m_tag and tack that 1906da4b19dSmpi * on to the mbuf. However that's rather expensive to do 1916da4b19dSmpi * for every frame so instead we stuff it in a special pkthdr 1926da4b19dSmpi * field. 19391b2158bSmillert */ 19491b2158bSmillert M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 19591b2158bSmillert if (m == NULL) 19691b2158bSmillert return ENOMEM; 1976da4b19dSmpi m->m_pkthdr.ph_cookie = ni; 19891b2158bSmillert 19991b2158bSmillert wh = mtod(m, struct ieee80211_frame *); 20091b2158bSmillert wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type; 20191b2158bSmillert wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 20291b2158bSmillert *(u_int16_t *)&wh->i_dur[0] = 0; 20391b2158bSmillert *(u_int16_t *)&wh->i_seq[0] = 20491b2158bSmillert htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); 205797d79d7Sstsp ni->ni_txseq = (ni->ni_txseq + 1) & 0xfff; 20691b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); 20791b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); 20891b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); 20991b2158bSmillert 210e8163130Sdamien /* check if protection is required for this mgmt frame */ 211e8163130Sdamien if ((ic->ic_caps & IEEE80211_C_MFP) && 212e8163130Sdamien (type == IEEE80211_FC0_SUBTYPE_DISASSOC || 213e8163130Sdamien type == IEEE80211_FC0_SUBTYPE_DEAUTH || 214e8163130Sdamien type == IEEE80211_FC0_SUBTYPE_ACTION)) { 215e8163130Sdamien /* 216e8163130Sdamien * Hack: we should not set the Protected bit in outgoing 217e8163130Sdamien * group management frames, however it is used as an 218e8163130Sdamien * indication to the drivers that they must encrypt the 219e8163130Sdamien * frame. Drivers should clear this bit from group 220e8163130Sdamien * management frames (software crypto code will do it). 221e8163130Sdamien * XXX could use an mbuf flag.. 222e8163130Sdamien */ 223e8163130Sdamien if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 224e8163130Sdamien (ni->ni_flags & IEEE80211_NODE_TXMGMTPROT)) 225e8163130Sdamien wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; 226e8163130Sdamien } 227e8163130Sdamien 22891b2158bSmillert if (ifp->if_flags & IFF_DEBUG) { 22991b2158bSmillert /* avoid to print too many frames */ 230171ac09aSdamien if ( 231171ac09aSdamien #ifndef IEEE80211_STA_ONLY 232171ac09aSdamien ic->ic_opmode == IEEE80211_M_IBSS || 233171ac09aSdamien #endif 23491b2158bSmillert #ifdef IEEE80211_DEBUG 23591b2158bSmillert ieee80211_debug > 1 || 23691b2158bSmillert #endif 23791b2158bSmillert (type & IEEE80211_FC0_SUBTYPE_MASK) != 2386998360eSstsp IEEE80211_FC0_SUBTYPE_PROBE_RESP) { 2396998360eSstsp const char *subtype_name; 2406998360eSstsp if ((type & IEEE80211_FC0_SUBTYPE_MASK) == 2416998360eSstsp IEEE80211_FC0_SUBTYPE_ACTION) 2426998360eSstsp subtype_name = ieee80211_action_name(wh); 2436998360eSstsp else 2446998360eSstsp subtype_name = ieee80211_mgt_subtype_name[ 2456998360eSstsp (type & IEEE80211_FC0_SUBTYPE_MASK) >> 2466998360eSstsp IEEE80211_FC0_SUBTYPE_SHIFT]; 247aec29e1fSreyk printf("%s: sending %s to %s on channel %u mode %s\n", 2486998360eSstsp ifp->if_xname, subtype_name, 24991b2158bSmillert ether_sprintf(ni->ni_macaddr), 250aec29e1fSreyk ieee80211_chan2ieee(ic, ni->ni_chan), 2512206dc0fSstsp ieee80211_phymode_name[ic->ic_curmode]); 25291b2158bSmillert } 2536998360eSstsp } 25491b2158bSmillert 25593d01030Sdamien #ifndef IEEE80211_STA_ONLY 25693d01030Sdamien if (ic->ic_opmode == IEEE80211_M_HOSTAP && 25793d01030Sdamien ieee80211_pwrsave(ic, m, ni) != 0) 25893d01030Sdamien return 0; 25993d01030Sdamien #endif 260351e1934Sdlg mq_enqueue(&ic->ic_mgtq, m); 26191b2158bSmillert ifp->if_timer = 1; 26267de757dSmpi if_start(ifp); 26391b2158bSmillert return 0; 26491b2158bSmillert } 26591b2158bSmillert 2663b874289Sdamien /*- 2673b874289Sdamien * EDCA tables are computed using the following formulas: 2683b874289Sdamien * 2693b874289Sdamien * 1) EDCATable (non-AP QSTA) 2703b874289Sdamien * 2713b874289Sdamien * AC CWmin CWmax AIFSN TXOP limit(ms) 2723b874289Sdamien * ------------------------------------------------------------- 2733b874289Sdamien * AC_BK aCWmin aCWmax 7 0 2743b874289Sdamien * AC_BE aCWmin aCWmax 3 0 2753b874289Sdamien * AC_VI (aCWmin+1)/2-1 aCWmin 2 agn=3.008 b=6.016 others=0 2763b874289Sdamien * AC_VO (aCWmin+1)/4-1 (aCWmin+1)/2-1 2 agn=1.504 b=3.264 others=0 2773b874289Sdamien * 2783b874289Sdamien * 2) QAPEDCATable (QAP) 2793b874289Sdamien * 2803b874289Sdamien * AC CWmin CWmax AIFSN TXOP limit(ms) 2813b874289Sdamien * ------------------------------------------------------------- 2823b874289Sdamien * AC_BK aCWmin aCWmax 7 0 2833b874289Sdamien * AC_BE aCWmin 4*(aCWmin+1)-1 3 0 2843b874289Sdamien * AC_VI (aCWmin+1)/2-1 aCWmin 1 agn=3.008 b=6.016 others=0 2853b874289Sdamien * AC_VO (aCWmin+1)/4-1 (aCWmin+1)/2-1 1 agn=1.504 b=3.264 others=0 2863b874289Sdamien * 2873b874289Sdamien * and the following aCWmin/aCWmax values: 2883b874289Sdamien * 2893b874289Sdamien * PHY aCWmin aCWmax 2903b874289Sdamien * --------------------------- 2913b874289Sdamien * 11A 15 1023 2923b874289Sdamien * 11B 31 1023 2933b874289Sdamien * 11G 15* 1023 (*) aCWmin(1) 294432d6c6bSstsp * 11N 15 1023 2953b874289Sdamien */ 296fca238d4Sstsp const struct ieee80211_edca_ac_params 2973b874289Sdamien ieee80211_edca_table[IEEE80211_MODE_MAX][EDCA_NUM_AC] = { 2983b874289Sdamien [IEEE80211_MODE_11B] = { 2993b874289Sdamien [EDCA_AC_BK] = { 5, 10, 7, 0 }, 3003b874289Sdamien [EDCA_AC_BE] = { 5, 10, 3, 0 }, 3013b874289Sdamien [EDCA_AC_VI] = { 4, 5, 2, 188 }, 3023b874289Sdamien [EDCA_AC_VO] = { 3, 4, 2, 102 } 3033b874289Sdamien }, 3043b874289Sdamien [IEEE80211_MODE_11A] = { 3053b874289Sdamien [EDCA_AC_BK] = { 4, 10, 7, 0 }, 3063b874289Sdamien [EDCA_AC_BE] = { 4, 10, 3, 0 }, 3073b874289Sdamien [EDCA_AC_VI] = { 3, 4, 2, 94 }, 3083b874289Sdamien [EDCA_AC_VO] = { 2, 3, 2, 47 } 3093b874289Sdamien }, 3103b874289Sdamien [IEEE80211_MODE_11G] = { 3113b874289Sdamien [EDCA_AC_BK] = { 4, 10, 7, 0 }, 3123b874289Sdamien [EDCA_AC_BE] = { 4, 10, 3, 0 }, 3133b874289Sdamien [EDCA_AC_VI] = { 3, 4, 2, 94 }, 3143b874289Sdamien [EDCA_AC_VO] = { 2, 3, 2, 47 } 3153b874289Sdamien }, 316432d6c6bSstsp [IEEE80211_MODE_11N] = { 317432d6c6bSstsp [EDCA_AC_BK] = { 4, 10, 7, 0 }, 318432d6c6bSstsp [EDCA_AC_BE] = { 4, 10, 3, 0 }, 319432d6c6bSstsp [EDCA_AC_VI] = { 3, 4, 2, 94 }, 320432d6c6bSstsp [EDCA_AC_VO] = { 2, 3, 2, 47 } 321432d6c6bSstsp }, 32250e8fe1cSstsp [IEEE80211_MODE_11AC] = { 32350e8fe1cSstsp [EDCA_AC_BK] = { 4, 10, 7, 0 }, 32450e8fe1cSstsp [EDCA_AC_BE] = { 4, 10, 3, 0 }, 32550e8fe1cSstsp [EDCA_AC_VI] = { 3, 4, 2, 94 }, 32650e8fe1cSstsp [EDCA_AC_VO] = { 2, 3, 2, 47 } 32750e8fe1cSstsp }, 3283b874289Sdamien }; 3293b874289Sdamien 330171ac09aSdamien #ifndef IEEE80211_STA_ONLY 331e8f8f56cSstsp const struct ieee80211_edca_ac_params 3323b874289Sdamien ieee80211_qap_edca_table[IEEE80211_MODE_MAX][EDCA_NUM_AC] = { 3333b874289Sdamien [IEEE80211_MODE_11B] = { 3343b874289Sdamien [EDCA_AC_BK] = { 5, 10, 7, 0 }, 3353b874289Sdamien [EDCA_AC_BE] = { 5, 7, 3, 0 }, 3363b874289Sdamien [EDCA_AC_VI] = { 4, 5, 1, 188 }, 3373b874289Sdamien [EDCA_AC_VO] = { 3, 4, 1, 102 } 3383b874289Sdamien }, 3393b874289Sdamien [IEEE80211_MODE_11A] = { 3403b874289Sdamien [EDCA_AC_BK] = { 4, 10, 7, 0 }, 3413b874289Sdamien [EDCA_AC_BE] = { 4, 6, 3, 0 }, 3423b874289Sdamien [EDCA_AC_VI] = { 3, 4, 1, 94 }, 3433b874289Sdamien [EDCA_AC_VO] = { 2, 3, 1, 47 } 3443b874289Sdamien }, 3453b874289Sdamien [IEEE80211_MODE_11G] = { 3463b874289Sdamien [EDCA_AC_BK] = { 4, 10, 7, 0 }, 3473b874289Sdamien [EDCA_AC_BE] = { 4, 6, 3, 0 }, 3483b874289Sdamien [EDCA_AC_VI] = { 3, 4, 1, 94 }, 3493b874289Sdamien [EDCA_AC_VO] = { 2, 3, 1, 47 } 3503b874289Sdamien }, 351432d6c6bSstsp [IEEE80211_MODE_11N] = { 352432d6c6bSstsp [EDCA_AC_BK] = { 4, 10, 7, 0 }, 353432d6c6bSstsp [EDCA_AC_BE] = { 4, 6, 3, 0 }, 354432d6c6bSstsp [EDCA_AC_VI] = { 3, 4, 1, 94 }, 355432d6c6bSstsp [EDCA_AC_VO] = { 2, 3, 1, 47 } 356432d6c6bSstsp }, 35750e8fe1cSstsp [IEEE80211_MODE_11AC] = { 35850e8fe1cSstsp [EDCA_AC_BK] = { 4, 10, 7, 0 }, 35950e8fe1cSstsp [EDCA_AC_BE] = { 4, 6, 3, 0 }, 36050e8fe1cSstsp [EDCA_AC_VI] = { 3, 4, 1, 94 }, 36150e8fe1cSstsp [EDCA_AC_VO] = { 2, 3, 1, 47 } 36250e8fe1cSstsp }, 3633b874289Sdamien }; 364171ac09aSdamien #endif /* IEEE80211_STA_ONLY */ 3653b874289Sdamien 36691b2158bSmillert /* 367ef822fe8Sdamien * Return the EDCA Access Category to be used for transmitting a frame with 368ef822fe8Sdamien * user-priority `up'. 369ef822fe8Sdamien */ 370ef822fe8Sdamien enum ieee80211_edca_ac 371ef822fe8Sdamien ieee80211_up_to_ac(struct ieee80211com *ic, int up) 372ef822fe8Sdamien { 373ce7deb45Sdamien /* see Table 9-1 */ 374ef822fe8Sdamien static const enum ieee80211_edca_ac up_to_ac[] = { 375ef822fe8Sdamien EDCA_AC_BE, /* BE */ 376ef822fe8Sdamien EDCA_AC_BK, /* BK */ 377ef822fe8Sdamien EDCA_AC_BK, /* -- */ 378ef822fe8Sdamien EDCA_AC_BE, /* EE */ 379ef822fe8Sdamien EDCA_AC_VI, /* CL */ 380ef822fe8Sdamien EDCA_AC_VI, /* VI */ 381ef822fe8Sdamien EDCA_AC_VO, /* VO */ 382ef822fe8Sdamien EDCA_AC_VO /* NC */ 383ef822fe8Sdamien }; 384ef822fe8Sdamien enum ieee80211_edca_ac ac; 385ef822fe8Sdamien 386ef822fe8Sdamien ac = (up <= 7) ? up_to_ac[up] : EDCA_AC_BE; 387ef822fe8Sdamien 388171ac09aSdamien #ifndef IEEE80211_STA_ONLY 389ef822fe8Sdamien if (ic->ic_opmode == IEEE80211_M_HOSTAP) 390ef822fe8Sdamien return ac; 391171ac09aSdamien #endif 392ef822fe8Sdamien /* 393ef822fe8Sdamien * We do not support the admission control procedure defined in 394b5f5be9fSstsp * IEEE Std 802.11-2012 section 9.19.4.2.3. The spec says that 395ef822fe8Sdamien * non-AP QSTAs that don't support this procedure shall use EDCA 396ef822fe8Sdamien * parameters of a lower priority AC that does not require 397ef822fe8Sdamien * admission control. 398ef822fe8Sdamien */ 399ef822fe8Sdamien while (ac != EDCA_AC_BK && ic->ic_edca_ac[ac].ac_acm) { 400ef822fe8Sdamien switch (ac) { 401ef822fe8Sdamien case EDCA_AC_BK: 402ef822fe8Sdamien /* can't get there */ 403ef822fe8Sdamien break; 404ef822fe8Sdamien case EDCA_AC_BE: 405ef822fe8Sdamien /* BE shouldn't require admission control */ 406ef822fe8Sdamien ac = EDCA_AC_BK; 407ef822fe8Sdamien break; 408ef822fe8Sdamien case EDCA_AC_VI: 409ef822fe8Sdamien ac = EDCA_AC_BE; 410ef822fe8Sdamien break; 411ef822fe8Sdamien case EDCA_AC_VO: 412ef822fe8Sdamien ac = EDCA_AC_VI; 413ef822fe8Sdamien break; 414ef822fe8Sdamien } 415ef822fe8Sdamien } 416ef822fe8Sdamien return ac; 417ef822fe8Sdamien } 418ef822fe8Sdamien 419ef822fe8Sdamien /* 420ef822fe8Sdamien * Get mbuf's user-priority: if mbuf is not VLAN tagged, select user-priority 421fb4c23fcSdamien * based on the DSCP (Differentiated Services Codepoint) field. 422ef822fe8Sdamien */ 423fb4c23fcSdamien int 424fb4c23fcSdamien ieee80211_classify(struct ieee80211com *ic, struct mbuf *m) 425ef822fe8Sdamien { 4264e00b903Spatrick struct ether_header eh; 4273c732269Sdamien u_int8_t ds_field; 428ef822fe8Sdamien #if NVLAN > 0 4290cb2bd1bSdamien if (m->m_flags & M_VLANTAG) /* use VLAN 802.1D user-priority */ 4300cb2bd1bSdamien return EVL_PRIOFTAG(m->m_pkthdr.ether_vtag); 431ef822fe8Sdamien #endif 4324e00b903Spatrick m_copydata(m, 0, sizeof(eh), (caddr_t)&eh); 4334e00b903Spatrick if (eh.ether_type == htons(ETHERTYPE_IP)) { 4344e00b903Spatrick struct ip ip; 4354e00b903Spatrick m_copydata(m, sizeof(eh), sizeof(ip), (caddr_t)&ip); 4364e00b903Spatrick if (ip.ip_v != 4) 4373c732269Sdamien return 0; 4384e00b903Spatrick ds_field = ip.ip_tos; 4393c732269Sdamien } 4403c732269Sdamien #ifdef INET6 4414e00b903Spatrick else if (eh.ether_type == htons(ETHERTYPE_IPV6)) { 4424e00b903Spatrick struct ip6_hdr ip6; 4433c732269Sdamien u_int32_t flowlabel; 4444e00b903Spatrick m_copydata(m, sizeof(eh), sizeof(ip6), (caddr_t)&ip6); 4454e00b903Spatrick flowlabel = ntohl(ip6.ip6_flow); 4463c732269Sdamien if ((flowlabel >> 28) != 6) 4473c732269Sdamien return 0; 4483c732269Sdamien ds_field = (flowlabel >> 20) & 0xff; 4493c732269Sdamien } 4503c732269Sdamien #endif /* INET6 */ 4513c732269Sdamien else /* neither IPv4 nor IPv6 */ 4523c732269Sdamien return 0; 4533c732269Sdamien 454ef822fe8Sdamien /* 455fb4c23fcSdamien * Map Differentiated Services Codepoint field (see RFC2474). 456fb4c23fcSdamien * Preserves backward compatibility with IP Precedence field. 457ef822fe8Sdamien */ 4583c732269Sdamien switch (ds_field & 0xfc) { 459ef822fe8Sdamien case IPTOS_PREC_PRIORITY: 460aefc44daSstsp return EDCA_AC_VI; 461ef822fe8Sdamien case IPTOS_PREC_IMMEDIATE: 462aefc44daSstsp return EDCA_AC_BK; 463ef822fe8Sdamien case IPTOS_PREC_FLASH: 464ef822fe8Sdamien case IPTOS_PREC_FLASHOVERRIDE: 465ef822fe8Sdamien case IPTOS_PREC_CRITIC_ECP: 466ef822fe8Sdamien case IPTOS_PREC_INTERNETCONTROL: 467ef822fe8Sdamien case IPTOS_PREC_NETCONTROL: 468aefc44daSstsp return EDCA_AC_VO; 469aefc44daSstsp default: 470aefc44daSstsp return EDCA_AC_BE; 471ef822fe8Sdamien } 472aefc44daSstsp } 473aefc44daSstsp 474aefc44daSstsp int 475aefc44daSstsp ieee80211_can_use_ampdu(struct ieee80211com *ic, struct ieee80211_node *ni) 476aefc44daSstsp { 477aefc44daSstsp return (ni->ni_flags & IEEE80211_NODE_HT) && 478aefc44daSstsp (ic->ic_caps & IEEE80211_C_TX_AMPDU) && 479aefc44daSstsp !(ic->ic_opmode == IEEE80211_M_STA && ni != ic->ic_bss) && 480aefc44daSstsp /* 481aefc44daSstsp * Don't use A-MPDU on non-encrypted networks. There are devices 482aefc44daSstsp * with buggy firmware which allow an attacker to inject 802.11 483678831beSjsg * frames into a wifi network by embedding rogue A-MPDU subframes 484aefc44daSstsp * in an arbitrary data payload (e.g. PNG images) which may end 485aefc44daSstsp * up appearing as actual frames after de-aggregation by a buggy 486aefc44daSstsp * device; see https://github.com/rpp0/aggr-inject for details. 487aefc44daSstsp * WPA2 prevents this injection attack since the attacker would 488aefc44daSstsp * need to inject frames which get decrypted correctly. 489aefc44daSstsp */ 490aefc44daSstsp ((ic->ic_flags & IEEE80211_F_RSNON) && 491aefc44daSstsp (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)); 492aefc44daSstsp } 493aefc44daSstsp 494aefc44daSstsp void 495aefc44daSstsp ieee80211_tx_compressed_bar(struct ieee80211com *ic, struct ieee80211_node *ni, 496aefc44daSstsp int tid, uint16_t ssn) 497aefc44daSstsp { 498aefc44daSstsp struct ifnet *ifp = &ic->ic_if; 499aefc44daSstsp struct mbuf *m; 500aefc44daSstsp 501aefc44daSstsp m = ieee80211_get_compressed_bar(ic, ni, tid, ssn); 502aefc44daSstsp if (m == NULL) 503aefc44daSstsp return; 504aefc44daSstsp 505aefc44daSstsp ieee80211_ref_node(ni); 506aefc44daSstsp if (mq_enqueue(&ic->ic_mgtq, m) == 0) 507aefc44daSstsp if_start(ifp); 508aefc44daSstsp else 509aefc44daSstsp ieee80211_release_node(ic, ni); 510ef822fe8Sdamien } 511ef822fe8Sdamien 512ef822fe8Sdamien /* 51391b2158bSmillert * Encapsulate an outbound data frame. The mbuf chain is updated and 51491b2158bSmillert * a reference to the destination node is returned. If an error is 51591b2158bSmillert * encountered NULL is returned and the node reference will also be NULL. 51691b2158bSmillert * 51791b2158bSmillert * NB: The caller is responsible for free'ing a returned node reference. 51891b2158bSmillert * The convention is ic_bss is not reference counted; the caller must 51991b2158bSmillert * maintain that. 52091b2158bSmillert */ 52191b2158bSmillert struct mbuf * 52291b2158bSmillert ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) 52391b2158bSmillert { 52491b2158bSmillert struct ieee80211com *ic = (void *)ifp; 52591b2158bSmillert struct ether_header eh; 52691b2158bSmillert struct ieee80211_frame *wh; 52791b2158bSmillert struct ieee80211_node *ni = NULL; 52891b2158bSmillert struct llc *llc; 529673b59bcSreyk struct m_tag *mtag; 530673b59bcSreyk u_int8_t *addr; 531fb4c23fcSdamien u_int dlt, hdrlen; 532fb4c23fcSdamien int addqos, tid; 53391b2158bSmillert 534673b59bcSreyk /* Handle raw frames if mbuf is tagged as 802.11 */ 535673b59bcSreyk if ((mtag = m_tag_find(m, PACKET_TAG_DLT, NULL)) != NULL) { 536673b59bcSreyk dlt = *(u_int *)(mtag + 1); 537673b59bcSreyk 538673b59bcSreyk if (!(dlt == DLT_IEEE802_11 || dlt == DLT_IEEE802_11_RADIO)) 539673b59bcSreyk goto fallback; 540673b59bcSreyk 541673b59bcSreyk wh = mtod(m, struct ieee80211_frame *); 542673b59bcSreyk switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 543673b59bcSreyk case IEEE80211_FC1_DIR_NODS: 544673b59bcSreyk case IEEE80211_FC1_DIR_FROMDS: 545673b59bcSreyk addr = wh->i_addr1; 546673b59bcSreyk break; 547673b59bcSreyk case IEEE80211_FC1_DIR_DSTODS: 548673b59bcSreyk case IEEE80211_FC1_DIR_TODS: 549673b59bcSreyk addr = wh->i_addr3; 550673b59bcSreyk break; 551673b59bcSreyk default: 552673b59bcSreyk goto bad; 553673b59bcSreyk } 554673b59bcSreyk 555673b59bcSreyk ni = ieee80211_find_txnode(ic, addr); 556673b59bcSreyk if (ni == NULL) 557673b59bcSreyk ni = ieee80211_ref_node(ic->ic_bss); 558673b59bcSreyk if (ni == NULL) { 559a0068c42Sjsg printf("%s: no node for dst %s, " 560a0068c42Sjsg "discard raw tx frame\n", ifp->if_xname, 561a0068c42Sjsg ether_sprintf(addr)); 562673b59bcSreyk ic->ic_stats.is_tx_nonode++; 563673b59bcSreyk goto bad; 564673b59bcSreyk } 565673b59bcSreyk ni->ni_inact = 0; 566673b59bcSreyk 567673b59bcSreyk *pni = ni; 568673b59bcSreyk return (m); 569673b59bcSreyk } 570673b59bcSreyk 571673b59bcSreyk fallback: 572a12575e0Sstsp if (ic->ic_opmode == IEEE80211_M_MONITOR) 573a12575e0Sstsp goto bad; 574a12575e0Sstsp 57591b2158bSmillert if (m->m_len < sizeof(struct ether_header)) { 57691b2158bSmillert m = m_pullup(m, sizeof(struct ether_header)); 57791b2158bSmillert if (m == NULL) { 57891b2158bSmillert ic->ic_stats.is_tx_nombuf++; 57991b2158bSmillert goto bad; 58091b2158bSmillert } 58191b2158bSmillert } 58291b2158bSmillert memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header)); 58391b2158bSmillert 58491b2158bSmillert ni = ieee80211_find_txnode(ic, eh.ether_dhost); 58591b2158bSmillert if (ni == NULL) { 586932b9027Sdamien DPRINTF(("no node for dst %s, discard frame\n", 587932b9027Sdamien ether_sprintf(eh.ether_dhost))); 58891b2158bSmillert ic->ic_stats.is_tx_nonode++; 58991b2158bSmillert goto bad; 59091b2158bSmillert } 591e03e709cSdamien 592c7cb3652Sstsp #ifndef IEEE80211_STA_ONLY 593c7cb3652Sstsp if (ic->ic_opmode == IEEE80211_M_HOSTAP && ni != ic->ic_bss && 594c7cb3652Sstsp ni->ni_state != IEEE80211_STA_ASSOC) { 595c7cb3652Sstsp ic->ic_stats.is_tx_nonode++; 596c7cb3652Sstsp goto bad; 597c7cb3652Sstsp } 598c7cb3652Sstsp #endif 599c7cb3652Sstsp 60001ad6d9fSdamien if ((ic->ic_flags & IEEE80211_F_RSNON) && 60101ad6d9fSdamien !ni->ni_port_valid && 60283aa0ba6Sdlg eh.ether_type != htons(ETHERTYPE_EAPOL)) { 603932b9027Sdamien DPRINTF(("port not valid: %s\n", 604932b9027Sdamien ether_sprintf(eh.ether_dhost))); 605eba1a6b6Sdamien ic->ic_stats.is_tx_noauth++; 606eba1a6b6Sdamien goto bad; 607eba1a6b6Sdamien } 608e03e709cSdamien 609e03e709cSdamien if ((ic->ic_flags & IEEE80211_F_COUNTERM) && 610e03e709cSdamien ni->ni_rsncipher == IEEE80211_CIPHER_TKIP) 611e03e709cSdamien /* XXX TKIP countermeasures! */; 612e03e709cSdamien 61391b2158bSmillert ni->ni_inact = 0; 61491b2158bSmillert 615fb4c23fcSdamien if ((ic->ic_flags & IEEE80211_F_QOS) && 616eba1a6b6Sdamien (ni->ni_flags & IEEE80211_NODE_QOS) && 617eba1a6b6Sdamien /* do not QoS-encapsulate EAPOL frames */ 61883aa0ba6Sdlg eh.ether_type != htons(ETHERTYPE_EAPOL)) { 619aefc44daSstsp struct ieee80211_tx_ba *ba; 620fb4c23fcSdamien tid = ieee80211_classify(ic, m); 621aefc44daSstsp ba = &ni->ni_tx_ba[tid]; 62221eba542Sstsp /* We use QoS data frames for aggregation only. */ 62321eba542Sstsp if (ba->ba_state != IEEE80211_BA_AGREED) { 624aefc44daSstsp hdrlen = sizeof(struct ieee80211_frame); 625aefc44daSstsp addqos = 0; 62621eba542Sstsp if (ieee80211_can_use_ampdu(ic, ni)) 62721eba542Sstsp ieee80211_node_trigger_addba_req(ni, tid); 628aefc44daSstsp } else { 629fb4c23fcSdamien hdrlen = sizeof(struct ieee80211_qosframe); 630fb4c23fcSdamien addqos = 1; 631aefc44daSstsp } 632fb4c23fcSdamien } else { 633fb4c23fcSdamien hdrlen = sizeof(struct ieee80211_frame); 634fb4c23fcSdamien addqos = 0; 635fb4c23fcSdamien } 63649c1cac8Sdamien m_adj(m, sizeof(struct ether_header) - LLC_SNAPFRAMELEN); 63791b2158bSmillert llc = mtod(m, struct llc *); 63891b2158bSmillert llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; 63991b2158bSmillert llc->llc_control = LLC_UI; 64091b2158bSmillert llc->llc_snap.org_code[0] = 0; 64191b2158bSmillert llc->llc_snap.org_code[1] = 0; 64291b2158bSmillert llc->llc_snap.org_code[2] = 0; 64391b2158bSmillert llc->llc_snap.ether_type = eh.ether_type; 644fb4c23fcSdamien M_PREPEND(m, hdrlen, M_DONTWAIT); 64591b2158bSmillert if (m == NULL) { 64691b2158bSmillert ic->ic_stats.is_tx_nombuf++; 64791b2158bSmillert goto bad; 64891b2158bSmillert } 64991b2158bSmillert wh = mtod(m, struct ieee80211_frame *); 65091b2158bSmillert wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; 65191b2158bSmillert *(u_int16_t *)&wh->i_dur[0] = 0; 652fb4c23fcSdamien if (addqos) { 653fb4c23fcSdamien struct ieee80211_qosframe *qwh = 654fb4c23fcSdamien (struct ieee80211_qosframe *)wh; 65545eec175Sdamien u_int16_t qos = tid; 65645eec175Sdamien 65745eec175Sdamien if (ic->ic_tid_noack & (1 << tid)) 65845eec175Sdamien qos |= IEEE80211_QOS_ACK_POLICY_NOACK; 65921eba542Sstsp else { 660aefc44daSstsp /* Use HT immediate block-ack. */ 661aefc44daSstsp qos |= IEEE80211_QOS_ACK_POLICY_NORMAL; 66221eba542Sstsp } 663fb4c23fcSdamien qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; 66445eec175Sdamien *(u_int16_t *)qwh->i_qos = htole16(qos); 66545eec175Sdamien *(u_int16_t *)qwh->i_seq = 666fb4c23fcSdamien htole16(ni->ni_qos_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); 667797d79d7Sstsp ni->ni_qos_txseqs[tid] = (ni->ni_qos_txseqs[tid] + 1) & 0xfff; 668fb4c23fcSdamien } else { 66991b2158bSmillert *(u_int16_t *)&wh->i_seq[0] = 67091b2158bSmillert htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); 671797d79d7Sstsp ni->ni_txseq = (ni->ni_txseq + 1) & 0xfff; 672fb4c23fcSdamien } 67391b2158bSmillert switch (ic->ic_opmode) { 67491b2158bSmillert case IEEE80211_M_STA: 67591b2158bSmillert wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 67691b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); 67791b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 67891b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); 67991b2158bSmillert break; 680171ac09aSdamien #ifndef IEEE80211_STA_ONLY 68191b2158bSmillert case IEEE80211_M_IBSS: 68291b2158bSmillert case IEEE80211_M_AHDEMO: 68391b2158bSmillert wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 68491b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 68591b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); 6860fd4e251Sreyk IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid); 68791b2158bSmillert break; 68891b2158bSmillert case IEEE80211_M_HOSTAP: 68991b2158bSmillert wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; 69091b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); 69191b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); 69291b2158bSmillert IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); 69391b2158bSmillert break; 694171ac09aSdamien #endif 695171ac09aSdamien default: 696171ac09aSdamien /* should not get there */ 69791b2158bSmillert goto bad; 69891b2158bSmillert } 699e03e709cSdamien 700e03e709cSdamien if ((ic->ic_flags & IEEE80211_F_WEPON) || 70101ad6d9fSdamien ((ic->ic_flags & IEEE80211_F_RSNON) && 70201ad6d9fSdamien (ni->ni_flags & IEEE80211_NODE_TXPROT))) 703e03e709cSdamien wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; 704e03e709cSdamien 70593d01030Sdamien #ifndef IEEE80211_STA_ONLY 70693d01030Sdamien if (ic->ic_opmode == IEEE80211_M_HOSTAP && 70793d01030Sdamien ieee80211_pwrsave(ic, m, ni) != 0) { 70893d01030Sdamien *pni = NULL; 70993d01030Sdamien return NULL; 71093d01030Sdamien } 71193d01030Sdamien #endif 71291b2158bSmillert *pni = ni; 71391b2158bSmillert return m; 71491b2158bSmillert bad: 71591b2158bSmillert m_freem(m); 716901bffb8Sjsg if (ni != NULL) 7170fd4e251Sreyk ieee80211_release_node(ic, ni); 71891b2158bSmillert *pni = NULL; 71991b2158bSmillert return NULL; 72091b2158bSmillert } 72191b2158bSmillert 72291b2158bSmillert /* 7238d0ac44dSdamien * Add a Capability Information field to a frame (see 7.3.1.4). 7248d0ac44dSdamien */ 7258d0ac44dSdamien u_int8_t * 7268d0ac44dSdamien ieee80211_add_capinfo(u_int8_t *frm, struct ieee80211com *ic, 7278d0ac44dSdamien const struct ieee80211_node *ni) 7288d0ac44dSdamien { 7298d0ac44dSdamien u_int16_t capinfo; 7308d0ac44dSdamien 731171ac09aSdamien #ifndef IEEE80211_STA_ONLY 7328d0ac44dSdamien if (ic->ic_opmode == IEEE80211_M_IBSS) 7338d0ac44dSdamien capinfo = IEEE80211_CAPINFO_IBSS; 7348d0ac44dSdamien else if (ic->ic_opmode == IEEE80211_M_HOSTAP) 7358d0ac44dSdamien capinfo = IEEE80211_CAPINFO_ESS; 7368d0ac44dSdamien else 737171ac09aSdamien #endif 7388d0ac44dSdamien capinfo = 0; 739171ac09aSdamien #ifndef IEEE80211_STA_ONLY 740e03e709cSdamien if (ic->ic_opmode == IEEE80211_M_HOSTAP && 741e03e709cSdamien (ic->ic_flags & (IEEE80211_F_WEPON | IEEE80211_F_RSNON))) 7428d0ac44dSdamien capinfo |= IEEE80211_CAPINFO_PRIVACY; 743171ac09aSdamien #endif 7448d0ac44dSdamien /* NB: some 11a AP's reject the request when short preamble is set */ 7458d0ac44dSdamien if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 7468d0ac44dSdamien IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) 7478d0ac44dSdamien capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 7488d0ac44dSdamien if (ic->ic_flags & IEEE80211_F_SHSLOT) 7498d0ac44dSdamien capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 7508d0ac44dSdamien LE_WRITE_2(frm, capinfo); 7518d0ac44dSdamien return frm + 2; 7528d0ac44dSdamien } 7538d0ac44dSdamien 7548d0ac44dSdamien /* 7558d0ac44dSdamien * Add an SSID element to a frame (see 7.3.2.1). 7568d0ac44dSdamien */ 7578d0ac44dSdamien u_int8_t * 7588d0ac44dSdamien ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) 7598d0ac44dSdamien { 7608d0ac44dSdamien *frm++ = IEEE80211_ELEMID_SSID; 7618d0ac44dSdamien *frm++ = len; 7628d0ac44dSdamien memcpy(frm, ssid, len); 7638d0ac44dSdamien return frm + len; 7648d0ac44dSdamien } 7658d0ac44dSdamien 7668d0ac44dSdamien /* 7678d0ac44dSdamien * Add a supported rates element to a frame (see 7.3.2.2). 76891b2158bSmillert */ 76991b2158bSmillert u_int8_t * 77091b2158bSmillert ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) 77191b2158bSmillert { 77291b2158bSmillert int nrates; 77391b2158bSmillert 77491b2158bSmillert *frm++ = IEEE80211_ELEMID_RATES; 7755d49b5a5Sdamien nrates = min(rs->rs_nrates, IEEE80211_RATE_SIZE); 77691b2158bSmillert *frm++ = nrates; 77791b2158bSmillert memcpy(frm, rs->rs_rates, nrates); 77891b2158bSmillert return frm + nrates; 77991b2158bSmillert } 78091b2158bSmillert 781171ac09aSdamien #ifndef IEEE80211_STA_ONLY 78291b2158bSmillert /* 7838d0ac44dSdamien * Add a DS Parameter Set element to a frame (see 7.3.2.4). 78491b2158bSmillert */ 785c790398aSdamien u_int8_t * 7868d0ac44dSdamien ieee80211_add_ds_params(u_int8_t *frm, struct ieee80211com *ic, 7878d0ac44dSdamien const struct ieee80211_node *ni) 78891b2158bSmillert { 7898d0ac44dSdamien *frm++ = IEEE80211_ELEMID_DSPARMS; 7901bb78573Sdamien *frm++ = 1; 7918d0ac44dSdamien *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); 7921bb78573Sdamien return frm; 7931bb78573Sdamien } 7941bb78573Sdamien 7956003857fSdamien /* 7968d0ac44dSdamien * Add a TIM element to a frame (see 7.3.2.6 and Annex L). 797ece40bdcSdamien */ 798ece40bdcSdamien u_int8_t * 799ece40bdcSdamien ieee80211_add_tim(u_int8_t *frm, struct ieee80211com *ic) 800ece40bdcSdamien { 801ece40bdcSdamien u_int i, offset = 0, len; 802ece40bdcSdamien 803ece40bdcSdamien /* find first non-zero octet in the virtual bit map */ 80436dba039Sjsg for (i = 0; i < ic->ic_tim_len && ic->ic_tim_bitmap[i] == 0; i++) 80536dba039Sjsg ; 806ece40bdcSdamien 807ece40bdcSdamien /* clear the lsb as it is reserved for the broadcast indication bit */ 808ece40bdcSdamien if (i < ic->ic_tim_len) 809ece40bdcSdamien offset = i & ~1; 810ece40bdcSdamien 811ece40bdcSdamien /* find last non-zero octet in the virtual bit map */ 81236dba039Sjsg for (i = ic->ic_tim_len - 1; i > 0 && ic->ic_tim_bitmap[i] == 0; i--) 81336dba039Sjsg ; 814ece40bdcSdamien 815ece40bdcSdamien len = i - offset + 1; 816ece40bdcSdamien 817ece40bdcSdamien *frm++ = IEEE80211_ELEMID_TIM; 818ece40bdcSdamien *frm++ = len + 3; /* length */ 819ece40bdcSdamien *frm++ = ic->ic_dtim_count; /* DTIM count */ 820ece40bdcSdamien *frm++ = ic->ic_dtim_period; /* DTIM period */ 821ece40bdcSdamien 822ece40bdcSdamien /* Bitmap Control */ 823ece40bdcSdamien *frm = offset; 824ece40bdcSdamien /* set broadcast/multicast indication bit if necessary */ 82593d01030Sdamien if (ic->ic_dtim_count == 0 && ic->ic_tim_mcast_pending) 826ece40bdcSdamien *frm |= 0x01; 827ece40bdcSdamien frm++; 828ece40bdcSdamien 829ece40bdcSdamien /* Partial Virtual Bitmap */ 830ece40bdcSdamien memcpy(frm, &ic->ic_tim_bitmap[offset], len); 831ece40bdcSdamien return frm + len; 832ece40bdcSdamien } 833ece40bdcSdamien 834ece40bdcSdamien /* 8358d0ac44dSdamien * Add an IBSS Parameter Set element to a frame (see 7.3.2.7). 8366003857fSdamien */ 8376003857fSdamien u_int8_t * 8388d0ac44dSdamien ieee80211_add_ibss_params(u_int8_t *frm, const struct ieee80211_node *ni) 8396003857fSdamien { 8408d0ac44dSdamien *frm++ = IEEE80211_ELEMID_IBSSPARMS; 8418d0ac44dSdamien *frm++ = 2; 8428d0ac44dSdamien LE_WRITE_2(frm, 0); /* TODO: ATIM window */ 8438d0ac44dSdamien return frm + 2; 8446003857fSdamien } 8456003857fSdamien 8466003857fSdamien /* 8478d0ac44dSdamien * Add an EDCA Parameter Set element to a frame (see 7.3.2.29). 8486003857fSdamien */ 8496003857fSdamien u_int8_t * 8506003857fSdamien ieee80211_add_edca_params(u_int8_t *frm, struct ieee80211com *ic) 8516003857fSdamien { 8526003857fSdamien const struct ieee80211_edca_ac_params *edca; 8536003857fSdamien int aci; 8546003857fSdamien 8556003857fSdamien *frm++ = IEEE80211_ELEMID_EDCAPARMS; 8566003857fSdamien *frm++ = 18; /* length */ 8576003857fSdamien *frm++ = 0; /* QoS Info */ 8586003857fSdamien *frm++ = 0; /* reserved */ 8596003857fSdamien 8606003857fSdamien /* setup AC Parameter Records */ 861a1ea161cSstsp edca = ieee80211_edca_table[ic->ic_curmode]; 8626003857fSdamien for (aci = 0; aci < EDCA_NUM_AC; aci++) { 8636003857fSdamien const struct ieee80211_edca_ac_params *ac = &edca[aci]; 8646003857fSdamien 8656003857fSdamien *frm++ = (aci << 5) | ((ac->ac_acm & 0x1) << 4) | 8666003857fSdamien (ac->ac_aifsn & 0xf); 8676003857fSdamien *frm++ = (ac->ac_ecwmax << 4) | 8686003857fSdamien (ac->ac_ecwmin & 0xf); 8698d0ac44dSdamien LE_WRITE_2(frm, ac->ac_txoplimit); frm += 2; 8708d0ac44dSdamien } 8718d0ac44dSdamien return frm; 8728d0ac44dSdamien } 8738d0ac44dSdamien 8748d0ac44dSdamien /* 8758d0ac44dSdamien * Add an ERP element to a frame (see 7.3.2.13). 8768d0ac44dSdamien */ 8778d0ac44dSdamien u_int8_t * 8788d0ac44dSdamien ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic) 8798d0ac44dSdamien { 8808d0ac44dSdamien u_int8_t erp; 881ca7fda7eSstsp int nonerpsta = 0; 8828d0ac44dSdamien 8838d0ac44dSdamien *frm++ = IEEE80211_ELEMID_ERP; 8848d0ac44dSdamien *frm++ = 1; 8858d0ac44dSdamien erp = 0; 8868d0ac44dSdamien /* 8878d0ac44dSdamien * The NonERP_Present bit shall be set to 1 when a NonERP STA 8888d0ac44dSdamien * is associated with the BSS. 8898d0ac44dSdamien */ 890ca7fda7eSstsp ieee80211_iterate_nodes(ic, ieee80211_count_nonerpsta, &nonerpsta); 891ca7fda7eSstsp if (nonerpsta != 0) 8928d0ac44dSdamien erp |= IEEE80211_ERP_NON_ERP_PRESENT; 8938d0ac44dSdamien /* 8948d0ac44dSdamien * If one or more NonERP STAs are associated in the BSS, the 8958d0ac44dSdamien * Use_Protection bit shall be set to 1 in transmitted ERP 8968d0ac44dSdamien * Information Elements. 8978d0ac44dSdamien */ 8988d0ac44dSdamien if (ic->ic_flags & IEEE80211_F_USEPROT) 8998d0ac44dSdamien erp |= IEEE80211_ERP_USE_PROTECTION; 9008d0ac44dSdamien /* 9018d0ac44dSdamien * The Barker_Preamble_Mode bit shall be set to 1 by the ERP 9028d0ac44dSdamien * Information Element sender if one or more associated NonERP 9038d0ac44dSdamien * STAs are not short preamble capable. 9048d0ac44dSdamien */ 9058d0ac44dSdamien if (!(ic->ic_flags & IEEE80211_F_SHPREAMBLE)) 9068d0ac44dSdamien erp |= IEEE80211_ERP_BARKER_MODE; 9078d0ac44dSdamien *frm++ = erp; 9088d0ac44dSdamien return frm; 9098d0ac44dSdamien } 910171ac09aSdamien #endif /* IEEE80211_STA_ONLY */ 9118d0ac44dSdamien 9128d0ac44dSdamien /* 9138d0ac44dSdamien * Add a QoS Capability element to a frame (see 7.3.2.35). 9148d0ac44dSdamien */ 9158d0ac44dSdamien u_int8_t * 9168d0ac44dSdamien ieee80211_add_qos_capability(u_int8_t *frm, struct ieee80211com *ic) 9178d0ac44dSdamien { 9188d0ac44dSdamien *frm++ = IEEE80211_ELEMID_QOS_CAP; 9198d0ac44dSdamien *frm++ = 1; 9208d0ac44dSdamien *frm++ = 0; /* QoS Info */ 9218d0ac44dSdamien return frm; 9228d0ac44dSdamien } 9238d0ac44dSdamien 9246999278eSstsp /* 9256999278eSstsp * Add a Wifi-Alliance WME (aka WMM) info element to a frame. 9266999278eSstsp * WME is a requirement for Wifi-Alliance compliance and some 9276999278eSstsp * 11n APs will not negotiate HT if this element is missing. 9286999278eSstsp */ 9296999278eSstsp uint8_t * 9306999278eSstsp ieee80211_add_wme_info(uint8_t *frm, struct ieee80211com *ic) 9316999278eSstsp { 9326999278eSstsp *frm++ = IEEE80211_ELEMID_VENDOR; 9336999278eSstsp *frm++ = 7; 9346999278eSstsp memcpy(frm, MICROSOFT_OUI, 3); frm += 3; 9356999278eSstsp *frm++ = 2; /* OUI type */ 9366999278eSstsp *frm++ = 0; /* OUI subtype */ 9376999278eSstsp *frm++ = 1; /* version */ 9386999278eSstsp *frm++ = 0; /* info */ 9396999278eSstsp 9406999278eSstsp return frm; 9416999278eSstsp } 942432d6c6bSstsp 943432d6c6bSstsp #ifndef IEEE80211_STA_ONLY 944432d6c6bSstsp /* 945432d6c6bSstsp * Add a Wifi-Alliance WMM (aka WME) parameter element to a frame. 946432d6c6bSstsp */ 947432d6c6bSstsp uint8_t * 948432d6c6bSstsp ieee80211_add_wme_param(uint8_t *frm, struct ieee80211com *ic) 949432d6c6bSstsp { 950432d6c6bSstsp const struct ieee80211_edca_ac_params *edca; 951432d6c6bSstsp int aci; 952432d6c6bSstsp 953432d6c6bSstsp *frm++ = IEEE80211_ELEMID_VENDOR; 954432d6c6bSstsp *frm++ = 24; 955432d6c6bSstsp memcpy(frm, MICROSOFT_OUI, 3); frm += 3; 956432d6c6bSstsp *frm++ = 2; /* OUI type */ 957432d6c6bSstsp *frm++ = 1; /* OUI subtype */ 958432d6c6bSstsp *frm++ = 1; /* version */ 959432d6c6bSstsp *frm++ = 0; /* info */ 960432d6c6bSstsp *frm++ = 0; /* reserved */ 961432d6c6bSstsp 962432d6c6bSstsp /* setup AC Parameter Records */ 963a1ea161cSstsp edca = ieee80211_edca_table[ic->ic_curmode]; 964432d6c6bSstsp for (aci = 0; aci < EDCA_NUM_AC; aci++) { 965432d6c6bSstsp const struct ieee80211_edca_ac_params *ac = &edca[aci]; 966432d6c6bSstsp 967432d6c6bSstsp *frm++ = (aci << 5) | ((ac->ac_acm & 0x1) << 4) | 968432d6c6bSstsp (ac->ac_aifsn & 0xf); 969432d6c6bSstsp *frm++ = (ac->ac_ecwmax << 4) | 970432d6c6bSstsp (ac->ac_ecwmin & 0xf); 971432d6c6bSstsp LE_WRITE_2(frm, ac->ac_txoplimit); frm += 2; 972432d6c6bSstsp } 973432d6c6bSstsp 974432d6c6bSstsp return frm; 975432d6c6bSstsp } 976432d6c6bSstsp #endif 977432d6c6bSstsp 9788d0ac44dSdamien /* 97990af3bf7Sstsp * Add an RSN element to a frame (see 802.11-2012 8.4.2.27) 98005534718Sdamien */ 98105534718Sdamien u_int8_t * 9821247f240Sdamien ieee80211_add_rsn_body(u_int8_t *frm, struct ieee80211com *ic, 983e03e709cSdamien const struct ieee80211_node *ni, int wpa) 98405534718Sdamien { 985e03e709cSdamien const u_int8_t *oui = wpa ? MICROSOFT_OUI : IEEE80211_OUI; 9861247f240Sdamien u_int8_t *pcount; 987b0e5533eSstsp u_int16_t count, rsncaps; 98805534718Sdamien 98905534718Sdamien /* write Version field */ 99005534718Sdamien LE_WRITE_2(frm, 1); frm += 2; 99105534718Sdamien 99290af3bf7Sstsp /* write Group Data Cipher Suite field (see 802.11-2012 Table 8-99) */ 9931247f240Sdamien memcpy(frm, oui, 3); frm += 3; 994e03e709cSdamien switch (ni->ni_rsngroupcipher) { 99505534718Sdamien case IEEE80211_CIPHER_WEP40: 99605534718Sdamien *frm++ = 1; 99705534718Sdamien break; 99805534718Sdamien case IEEE80211_CIPHER_TKIP: 99905534718Sdamien *frm++ = 2; 100005534718Sdamien break; 100105534718Sdamien case IEEE80211_CIPHER_CCMP: 10020e5a82c2Sdamien *frm++ = 4; 100305534718Sdamien break; 100405534718Sdamien case IEEE80211_CIPHER_WEP104: 100505534718Sdamien *frm++ = 5; 100605534718Sdamien break; 1007e03e709cSdamien default: 1008e03e709cSdamien /* can't get there */ 1009db011a80Sdamien panic("invalid group data cipher!"); 101005534718Sdamien } 101105534718Sdamien 101205534718Sdamien pcount = frm; frm += 2; 101305534718Sdamien count = 0; 101405534718Sdamien /* write Pairwise Cipher Suite List */ 1015e03e709cSdamien if (ni->ni_rsnciphers & IEEE80211_CIPHER_USEGROUP) { 10161247f240Sdamien memcpy(frm, oui, 3); frm += 3; 10173a557689Sdamien *frm++ = 0; 10183a557689Sdamien count++; 10193a557689Sdamien } 1020e03e709cSdamien if (ni->ni_rsnciphers & IEEE80211_CIPHER_TKIP) { 10211247f240Sdamien memcpy(frm, oui, 3); frm += 3; 102205534718Sdamien *frm++ = 2; 102305534718Sdamien count++; 102405534718Sdamien } 1025e03e709cSdamien if (ni->ni_rsnciphers & IEEE80211_CIPHER_CCMP) { 10261247f240Sdamien memcpy(frm, oui, 3); frm += 3; 10270e5a82c2Sdamien *frm++ = 4; 102805534718Sdamien count++; 102905534718Sdamien } 103005534718Sdamien /* write Pairwise Cipher Suite Count field */ 103105534718Sdamien LE_WRITE_2(pcount, count); 103205534718Sdamien 103305534718Sdamien pcount = frm; frm += 2; 103405534718Sdamien count = 0; 103505534718Sdamien /* write AKM Suite List (see Table 20dc) */ 1036c7c53e5eSdamien if (ni->ni_rsnakms & IEEE80211_AKM_8021X) { 10371247f240Sdamien memcpy(frm, oui, 3); frm += 3; 103805534718Sdamien *frm++ = 1; 103905534718Sdamien count++; 104005534718Sdamien } 1041e03e709cSdamien if (ni->ni_rsnakms & IEEE80211_AKM_PSK) { 10421247f240Sdamien memcpy(frm, oui, 3); frm += 3; 104305534718Sdamien *frm++ = 2; 104405534718Sdamien count++; 104505534718Sdamien } 1046f91ff320Sdamien if (!wpa && (ni->ni_rsnakms & IEEE80211_AKM_SHA256_8021X)) { 1047c7c53e5eSdamien memcpy(frm, oui, 3); frm += 3; 1048c7c53e5eSdamien *frm++ = 5; 1049c7c53e5eSdamien count++; 1050c7c53e5eSdamien } 1051f91ff320Sdamien if (!wpa && (ni->ni_rsnakms & IEEE80211_AKM_SHA256_PSK)) { 1052c7c53e5eSdamien memcpy(frm, oui, 3); frm += 3; 1053c7c53e5eSdamien *frm++ = 6; 1054c7c53e5eSdamien count++; 1055c7c53e5eSdamien } 105605534718Sdamien /* write AKM Suite List Count field */ 105705534718Sdamien LE_WRITE_2(pcount, count); 105805534718Sdamien 1059db011a80Sdamien if (wpa) 1060db011a80Sdamien return frm; 1061db011a80Sdamien 106205534718Sdamien /* write RSN Capabilities field */ 1063b0e5533eSstsp rsncaps = (ni->ni_rsncaps & (IEEE80211_RSNCAP_PTKSA_RCNT_MASK | 1064b0e5533eSstsp IEEE80211_RSNCAP_GTKSA_RCNT_MASK)); 1065b0e5533eSstsp if (ic->ic_caps & IEEE80211_C_MFP) { 1066b0e5533eSstsp rsncaps |= IEEE80211_RSNCAP_MFPC; 1067b0e5533eSstsp if (ic->ic_flags & IEEE80211_F_MFPR) 1068b0e5533eSstsp rsncaps |= IEEE80211_RSNCAP_MFPR; 1069b0e5533eSstsp } 1070b0e5533eSstsp if (ic->ic_flags & IEEE80211_F_PBAR) 1071b0e5533eSstsp rsncaps |= IEEE80211_RSNCAP_PBAC; 1072b0e5533eSstsp LE_WRITE_2(frm, rsncaps); frm += 2; 107305534718Sdamien 1074f91ff320Sdamien if (ni->ni_flags & IEEE80211_NODE_PMKID) { 1075f91ff320Sdamien /* write PMKID Count field */ 1076f91ff320Sdamien LE_WRITE_2(frm, 1); frm += 2; 1077f91ff320Sdamien /* write PMKID List (only 1) */ 1078f91ff320Sdamien memcpy(frm, ni->ni_pmkid, IEEE80211_PMKID_LEN); 1079f91ff320Sdamien frm += IEEE80211_PMKID_LEN; 1080f91ff320Sdamien } 1081f91ff320Sdamien 1082db011a80Sdamien if (!(ic->ic_caps & IEEE80211_C_MFP)) 1083db011a80Sdamien return frm; 1084db011a80Sdamien 1085f5370d46Sstsp if ((ni->ni_flags & IEEE80211_NODE_PMKID) == 0) { 1086f5370d46Sstsp /* no PMKID (PMKID Count=0) */ 1087f5370d46Sstsp LE_WRITE_2(frm, 0); frm += 2; 1088f5370d46Sstsp } 1089f5370d46Sstsp 1090db011a80Sdamien /* write Group Integrity Cipher Suite field */ 1091db011a80Sdamien memcpy(frm, oui, 3); frm += 3; 1092db011a80Sdamien switch (ic->ic_rsngroupmgmtcipher) { 109345eec175Sdamien case IEEE80211_CIPHER_BIP: 1094db011a80Sdamien *frm++ = 6; 1095db011a80Sdamien break; 1096db011a80Sdamien default: 1097db011a80Sdamien /* can't get there */ 1098db011a80Sdamien panic("invalid integrity group cipher!"); 1099e03e709cSdamien } 11001247f240Sdamien return frm; 11011247f240Sdamien } 11021247f240Sdamien 11031247f240Sdamien u_int8_t * 11041247f240Sdamien ieee80211_add_rsn(u_int8_t *frm, struct ieee80211com *ic, 11051247f240Sdamien const struct ieee80211_node *ni) 11061247f240Sdamien { 11071247f240Sdamien u_int8_t *plen; 11081247f240Sdamien 11091247f240Sdamien *frm++ = IEEE80211_ELEMID_RSN; 11101247f240Sdamien plen = frm++; /* length filled in later */ 11111247f240Sdamien frm = ieee80211_add_rsn_body(frm, ic, ni, 0); 11121247f240Sdamien 11131247f240Sdamien /* write length field */ 1114c8535842Sdamien *plen = frm - plen - 1; 11151247f240Sdamien return frm; 11161247f240Sdamien } 11171247f240Sdamien 11181247f240Sdamien /* 1119e03e709cSdamien * Add a vendor-specific WPA element to a frame. 1120e03e709cSdamien * This is required for compatibility with Wi-Fi Alliance WPA. 11211247f240Sdamien */ 11221247f240Sdamien u_int8_t * 1123e03e709cSdamien ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic, 11241247f240Sdamien const struct ieee80211_node *ni) 11251247f240Sdamien { 11261247f240Sdamien u_int8_t *plen; 11271247f240Sdamien 11281247f240Sdamien *frm++ = IEEE80211_ELEMID_VENDOR; 11291247f240Sdamien plen = frm++; /* length filled in later */ 11301247f240Sdamien memcpy(frm, MICROSOFT_OUI, 3); frm += 3; 1131e03e709cSdamien *frm++ = 1; /* WPA */ 11321247f240Sdamien frm = ieee80211_add_rsn_body(frm, ic, ni, 1); 11331247f240Sdamien 113405534718Sdamien /* write length field */ 1135c8535842Sdamien *plen = frm - plen - 1; 113605534718Sdamien return frm; 113705534718Sdamien } 113805534718Sdamien 113905534718Sdamien /* 11408d0ac44dSdamien * Add an extended supported rates element to a frame (see 7.3.2.14). 11418d0ac44dSdamien */ 11428d0ac44dSdamien u_int8_t * 11438d0ac44dSdamien ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) 11448d0ac44dSdamien { 11458c4d58d2Sdamien int nrates; 11468c4d58d2Sdamien 11478c4d58d2Sdamien KASSERT(rs->rs_nrates > IEEE80211_RATE_SIZE); 11488c4d58d2Sdamien 11498d0ac44dSdamien *frm++ = IEEE80211_ELEMID_XRATES; 11508c4d58d2Sdamien nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; 11518d0ac44dSdamien *frm++ = nrates; 11528d0ac44dSdamien memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); 11538c4d58d2Sdamien return frm + nrates; 11546003857fSdamien } 11556003857fSdamien 115645eec175Sdamien /* 115745eec175Sdamien * Add an HT Capabilities element to a frame (see 7.3.2.57). 115845eec175Sdamien */ 115945eec175Sdamien u_int8_t * 116045eec175Sdamien ieee80211_add_htcaps(u_int8_t *frm, struct ieee80211com *ic) 116145eec175Sdamien { 116245eec175Sdamien *frm++ = IEEE80211_ELEMID_HTCAPS; 116345eec175Sdamien *frm++ = 26; 116445eec175Sdamien LE_WRITE_2(frm, ic->ic_htcaps); frm += 2; 1165bb1f4a0cSstsp *frm++ = ic->ic_ampdu_params; 1166ebc1d5f7Sstsp memcpy(frm, ic->ic_sup_mcs, 10); frm += 10; 1167ebc1d5f7Sstsp LE_WRITE_2(frm, (ic->ic_max_rxrate & IEEE80211_MCS_RX_RATE_HIGH)); 1168ebc1d5f7Sstsp frm += 2; 1169ebc1d5f7Sstsp *frm++ = ic->ic_tx_mcs_set; 1170ebc1d5f7Sstsp *frm++ = 0; /* reserved */ 1171ebc1d5f7Sstsp *frm++ = 0; /* reserved */ 1172ebc1d5f7Sstsp *frm++ = 0; /* reserved */ 117345eec175Sdamien LE_WRITE_2(frm, ic->ic_htxcaps); frm += 2; 117445eec175Sdamien LE_WRITE_4(frm, ic->ic_txbfcaps); frm += 4; 117545eec175Sdamien *frm++ = ic->ic_aselcaps; 117645eec175Sdamien return frm; 117745eec175Sdamien } 117845eec175Sdamien 1179ec69e05aSdamien #ifndef IEEE80211_STA_ONLY 118045eec175Sdamien /* 118145eec175Sdamien * Add an HT Operation element to a frame (see 7.3.2.58). 118245eec175Sdamien */ 118345eec175Sdamien u_int8_t * 118445eec175Sdamien ieee80211_add_htop(u_int8_t *frm, struct ieee80211com *ic) 118545eec175Sdamien { 118645eec175Sdamien *frm++ = IEEE80211_ELEMID_HTOP; 118745eec175Sdamien *frm++ = 22; 118845eec175Sdamien *frm++ = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); 1189bf61fa4aSstsp *frm++ = ic->ic_bss->ni_htop0; 1190bf61fa4aSstsp LE_WRITE_2(frm, ic->ic_bss->ni_htop1); frm += 2; 1191bf61fa4aSstsp LE_WRITE_2(frm, ic->ic_bss->ni_htop2); frm += 2; 119245eec175Sdamien memset(frm, 0, 16); frm += 16; 119345eec175Sdamien return frm; 119445eec175Sdamien } 1195ec69e05aSdamien #endif /* !IEEE80211_STA_ONLY */ 119645eec175Sdamien 119750e8fe1cSstsp /* 119850e8fe1cSstsp * Add a VHT Capabilities element to a frame (see 802.11ac-2013 8.4.2.160.2). 119950e8fe1cSstsp */ 120050e8fe1cSstsp u_int8_t * 120150e8fe1cSstsp ieee80211_add_vhtcaps(u_int8_t *frm, struct ieee80211com *ic) 120250e8fe1cSstsp { 120350e8fe1cSstsp *frm++ = IEEE80211_ELEMID_VHTCAPS; 120450e8fe1cSstsp *frm++ = 12; 120550e8fe1cSstsp LE_WRITE_4(frm, ic->ic_vhtcaps); frm += 4; 120650e8fe1cSstsp LE_WRITE_2(frm, ic->ic_vht_rxmcs); frm += 2; 120750e8fe1cSstsp LE_WRITE_2(frm, ic->ic_vht_rx_max_lgi_mbit_s); frm += 2; 120850e8fe1cSstsp LE_WRITE_2(frm, ic->ic_vht_txmcs); frm += 2; 120950e8fe1cSstsp LE_WRITE_2(frm, ic->ic_vht_tx_max_lgi_mbit_s); frm += 2; 121050e8fe1cSstsp return frm; 121150e8fe1cSstsp } 121250e8fe1cSstsp 121345eec175Sdamien #ifndef IEEE80211_STA_ONLY 121445eec175Sdamien /* 121545eec175Sdamien * Add a Timeout Interval element to a frame (see 7.3.2.49). 121645eec175Sdamien */ 121745eec175Sdamien u_int8_t * 121845eec175Sdamien ieee80211_add_tie(u_int8_t *frm, u_int8_t type, u_int32_t value) 121945eec175Sdamien { 122045eec175Sdamien *frm++ = IEEE80211_ELEMID_TIE; 122145eec175Sdamien *frm++ = 5; /* length */ 122245eec175Sdamien *frm++ = type; /* Timeout Interval type */ 122345eec175Sdamien LE_WRITE_4(frm, value); 122445eec175Sdamien return frm + 4; 122545eec175Sdamien } 122645eec175Sdamien #endif 122745eec175Sdamien 1228250085e6Sdamien struct mbuf * 1229e03e709cSdamien ieee80211_getmgmt(int flags, int type, u_int pktlen) 123091b2158bSmillert { 123191b2158bSmillert struct mbuf *m; 123291b2158bSmillert 1233e03e709cSdamien /* reserve space for 802.11 header */ 1234f6599d9eSdamien pktlen += sizeof(struct ieee80211_frame); 1235f6599d9eSdamien 1236fe6a7506Sjsg if (pktlen > MCLBYTES) 1237e03e709cSdamien panic("management frame too large: %u", pktlen); 123891b2158bSmillert MGETHDR(m, flags, type); 1239e03e709cSdamien if (m == NULL) 1240e03e709cSdamien return NULL; 1241f76734bcSdamien if (pktlen > MHLEN) { 124291b2158bSmillert MCLGET(m, flags); 1243b2a6e105Sdamien if (!(m->m_flags & M_EXT)) 1244e03e709cSdamien return m_free(m); 1245b2a6e105Sdamien } 1246e03e709cSdamien m->m_data += sizeof(struct ieee80211_frame); 124791b2158bSmillert return m; 124891b2158bSmillert } 124991b2158bSmillert 12505d49b5a5Sdamien /*- 12515d49b5a5Sdamien * Probe request frame format: 12525d49b5a5Sdamien * [tlv] SSID 12535d49b5a5Sdamien * [tlv] Supported rates 12545d49b5a5Sdamien * [tlv] Extended Supported Rates (802.11g) 125545eec175Sdamien * [tlv] HT Capabilities (802.11n) 125691b2158bSmillert */ 12575d49b5a5Sdamien struct mbuf * 12585d49b5a5Sdamien ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni) 125991b2158bSmillert { 12609b4ad7b6Sdamien const struct ieee80211_rateset *rs = 12619b4ad7b6Sdamien &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; 126291b2158bSmillert struct mbuf *m; 126391b2158bSmillert u_int8_t *frm; 126491b2158bSmillert 1265e03e709cSdamien m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 12661bb78573Sdamien 2 + ic->ic_des_esslen + 12679b4ad7b6Sdamien 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + 12689b4ad7b6Sdamien ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? 126945eec175Sdamien 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) + 127050e8fe1cSstsp ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) + 127150e8fe1cSstsp ((ic->ic_flags & IEEE80211_F_VHTON) ? 14 : 0)); 127291b2158bSmillert if (m == NULL) 12735d49b5a5Sdamien return NULL; 12745d49b5a5Sdamien 127591b2158bSmillert frm = mtod(m, u_int8_t *); 12765d49b5a5Sdamien frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); 12778c4d58d2Sdamien frm = ieee80211_add_rates(frm, rs); 12788c4d58d2Sdamien if (rs->rs_nrates > IEEE80211_RATE_SIZE) 12798c4d58d2Sdamien frm = ieee80211_add_xrates(frm, rs); 12806999278eSstsp if (ic->ic_flags & IEEE80211_F_HTON) { 128145eec175Sdamien frm = ieee80211_add_htcaps(frm, ic); 12826999278eSstsp frm = ieee80211_add_wme_info(frm, ic); 12836999278eSstsp } 128450e8fe1cSstsp if (ic->ic_flags & IEEE80211_F_VHTON) 128550e8fe1cSstsp frm = ieee80211_add_htcaps(frm, ic); 12865d49b5a5Sdamien 128791b2158bSmillert m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 128891b2158bSmillert 12895d49b5a5Sdamien return m; 12905d49b5a5Sdamien } 129191b2158bSmillert 1292171ac09aSdamien #ifndef IEEE80211_STA_ONLY 12935d49b5a5Sdamien /*- 12948d0ac44dSdamien * Probe response frame format: 12955d49b5a5Sdamien * [8] Timestamp 12965d49b5a5Sdamien * [2] Beacon interval 12975d49b5a5Sdamien * [2] Capability 12985d49b5a5Sdamien * [tlv] Service Set Identifier (SSID) 12995d49b5a5Sdamien * [tlv] Supported rates 130045eec175Sdamien * [tlv] DS Parameter Set (802.11g) 13015d49b5a5Sdamien * [tlv] ERP Information (802.11g) 13025d49b5a5Sdamien * [tlv] Extended Supported Rates (802.11g) 13035d49b5a5Sdamien * [tlv] RSN (802.11i) 13045d49b5a5Sdamien * [tlv] EDCA Parameter Set (802.11e) 130545eec175Sdamien * [tlv] HT Capabilities (802.11n) 130645eec175Sdamien * [tlv] HT Operation (802.11n) 130791b2158bSmillert */ 13085d49b5a5Sdamien struct mbuf * 1309774cd68cSstsp ieee80211_get_probe_resp(struct ieee80211com *ic) 13105d49b5a5Sdamien { 13119b4ad7b6Sdamien const struct ieee80211_rateset *rs = &ic->ic_bss->ni_rates; 13125d49b5a5Sdamien struct mbuf *m; 13135d49b5a5Sdamien u_int8_t *frm; 13145d49b5a5Sdamien 1315e03e709cSdamien m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 13169b4ad7b6Sdamien 8 + 2 + 2 + 1317774cd68cSstsp 2 + ic->ic_bss->ni_esslen + 13189b4ad7b6Sdamien 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + 1319e63afc57Sdamien 2 + 1 + 13209b4ad7b6Sdamien ((ic->ic_opmode == IEEE80211_M_IBSS) ? 2 + 2 : 0) + 13219b4ad7b6Sdamien ((ic->ic_curmode == IEEE80211_MODE_11G) ? 2 + 1 : 0) + 13229b4ad7b6Sdamien ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? 13239b4ad7b6Sdamien 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) + 1324e03e709cSdamien (((ic->ic_flags & IEEE80211_F_RSNON) && 13259654ac02Sdamien (ic->ic_bss->ni_rsnprotos & IEEE80211_PROTO_RSN)) ? 13269654ac02Sdamien 2 + IEEE80211_RSNIE_MAXLEN : 0) + 13279b4ad7b6Sdamien ((ic->ic_flags & IEEE80211_F_QOS) ? 2 + 18 : 0) + 1328e03e709cSdamien (((ic->ic_flags & IEEE80211_F_RSNON) && 13299654ac02Sdamien (ic->ic_bss->ni_rsnprotos & IEEE80211_PROTO_WPA)) ? 133045eec175Sdamien 2 + IEEE80211_WPAIE_MAXLEN : 0) + 13315fb37217Sstsp ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 24 + 26 : 0)); 133291b2158bSmillert if (m == NULL) 13335d49b5a5Sdamien return NULL; 133491b2158bSmillert 13355d49b5a5Sdamien frm = mtod(m, u_int8_t *); 13369b4ad7b6Sdamien memset(frm, 0, 8); frm += 8; /* timestamp is set by hardware */ 13378d0ac44dSdamien LE_WRITE_2(frm, ic->ic_bss->ni_intval); frm += 2; 1338774cd68cSstsp frm = ieee80211_add_capinfo(frm, ic, ic->ic_bss); 133991b2158bSmillert frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, 134091b2158bSmillert ic->ic_bss->ni_esslen); 13418c4d58d2Sdamien frm = ieee80211_add_rates(frm, rs); 1342774cd68cSstsp frm = ieee80211_add_ds_params(frm, ic, ic->ic_bss); 13438d0ac44dSdamien if (ic->ic_opmode == IEEE80211_M_IBSS) 1344774cd68cSstsp frm = ieee80211_add_ibss_params(frm, ic->ic_bss); 13451bb78573Sdamien if (ic->ic_curmode == IEEE80211_MODE_11G) 13461bb78573Sdamien frm = ieee80211_add_erp(frm, ic); 13478c4d58d2Sdamien if (rs->rs_nrates > IEEE80211_RATE_SIZE) 13488c4d58d2Sdamien frm = ieee80211_add_xrates(frm, rs); 1349e03e709cSdamien if ((ic->ic_flags & IEEE80211_F_RSNON) && 1350e03e709cSdamien (ic->ic_bss->ni_rsnprotos & IEEE80211_PROTO_RSN)) 13513a557689Sdamien frm = ieee80211_add_rsn(frm, ic, ic->ic_bss); 13526003857fSdamien if (ic->ic_flags & IEEE80211_F_QOS) 13536003857fSdamien frm = ieee80211_add_edca_params(frm, ic); 1354e03e709cSdamien if ((ic->ic_flags & IEEE80211_F_RSNON) && 1355e03e709cSdamien (ic->ic_bss->ni_rsnprotos & IEEE80211_PROTO_WPA)) 1356e03e709cSdamien frm = ieee80211_add_wpa(frm, ic, ic->ic_bss); 135745eec175Sdamien if (ic->ic_flags & IEEE80211_F_HTON) { 135845eec175Sdamien frm = ieee80211_add_htcaps(frm, ic); 135945eec175Sdamien frm = ieee80211_add_htop(frm, ic); 13605fb37217Sstsp frm = ieee80211_add_wme_param(frm, ic); 136145eec175Sdamien } 136291b2158bSmillert 13635d49b5a5Sdamien m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 13645d49b5a5Sdamien 13655d49b5a5Sdamien return m; 13665d49b5a5Sdamien } 1367171ac09aSdamien #endif /* IEEE80211_STA_ONLY */ 13685d49b5a5Sdamien 13695d49b5a5Sdamien /*- 13705d49b5a5Sdamien * Authentication frame format: 13715d49b5a5Sdamien * [2] Authentication algorithm number 13725d49b5a5Sdamien * [2] Authentication transaction sequence number 13735d49b5a5Sdamien * [2] Status code 13745d49b5a5Sdamien */ 13755d49b5a5Sdamien struct mbuf * 13765d49b5a5Sdamien ieee80211_get_auth(struct ieee80211com *ic, struct ieee80211_node *ni, 13775d49b5a5Sdamien u_int16_t status, u_int16_t seq) 13785d49b5a5Sdamien { 13795d49b5a5Sdamien struct mbuf *m; 13805d49b5a5Sdamien u_int8_t *frm; 13815d49b5a5Sdamien 138291b2158bSmillert MGETHDR(m, M_DONTWAIT, MT_DATA); 138391b2158bSmillert if (m == NULL) 13845d49b5a5Sdamien return NULL; 1385894ad73aSclaudio m_align(m, 2 * 3); 138691b2158bSmillert m->m_pkthdr.len = m->m_len = 2 * 3; 13875d49b5a5Sdamien 138891b2158bSmillert frm = mtod(m, u_int8_t *); 1389ecd3783dSdamien LE_WRITE_2(frm, IEEE80211_AUTH_ALG_OPEN); frm += 2; 13908d0ac44dSdamien LE_WRITE_2(frm, seq); frm += 2; 1391ecd3783dSdamien LE_WRITE_2(frm, status); 139291b2158bSmillert 13935d49b5a5Sdamien return m; 13945d49b5a5Sdamien } 139591b2158bSmillert 13965d49b5a5Sdamien /*- 13975d49b5a5Sdamien * Deauthentication frame format: 13985d49b5a5Sdamien * [2] Reason code 13995d49b5a5Sdamien */ 14005d49b5a5Sdamien struct mbuf * 14015d49b5a5Sdamien ieee80211_get_deauth(struct ieee80211com *ic, struct ieee80211_node *ni, 14025d49b5a5Sdamien u_int16_t reason) 14035d49b5a5Sdamien { 14045d49b5a5Sdamien struct mbuf *m; 14055d49b5a5Sdamien 140691b2158bSmillert MGETHDR(m, M_DONTWAIT, MT_DATA); 140791b2158bSmillert if (m == NULL) 14085d49b5a5Sdamien return NULL; 1409894ad73aSclaudio m_align(m, 2); 141091b2158bSmillert m->m_pkthdr.len = m->m_len = 2; 1411894ad73aSclaudio 14125d49b5a5Sdamien *mtod(m, u_int16_t *) = htole16(reason); 141391b2158bSmillert 14145d49b5a5Sdamien return m; 14155d49b5a5Sdamien } 14165d49b5a5Sdamien 14175d49b5a5Sdamien /*- 14185d49b5a5Sdamien * (Re)Association request frame format: 14195d49b5a5Sdamien * [2] Capability information 14205d49b5a5Sdamien * [2] Listen interval 14215d49b5a5Sdamien * [6*] Current AP address (Reassociation only) 14225d49b5a5Sdamien * [tlv] SSID 14235d49b5a5Sdamien * [tlv] Supported rates 14245d49b5a5Sdamien * [tlv] Extended Supported Rates (802.11g) 14255d49b5a5Sdamien * [tlv] RSN (802.11i) 14266003857fSdamien * [tlv] QoS Capability (802.11e) 142745eec175Sdamien * [tlv] HT Capabilities (802.11n) 142891b2158bSmillert */ 14295d49b5a5Sdamien struct mbuf * 14305d49b5a5Sdamien ieee80211_get_assoc_req(struct ieee80211com *ic, struct ieee80211_node *ni, 14314dfbe550Sdamien int type) 14325d49b5a5Sdamien { 14339b4ad7b6Sdamien const struct ieee80211_rateset *rs = &ni->ni_rates; 14345d49b5a5Sdamien struct mbuf *m; 14355d49b5a5Sdamien u_int8_t *frm; 14365d49b5a5Sdamien u_int16_t capinfo; 14375d49b5a5Sdamien 1438e03e709cSdamien m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 14399b4ad7b6Sdamien 2 + 2 + 14404dfbe550Sdamien ((type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) ? 14419b4ad7b6Sdamien IEEE80211_ADDR_LEN : 0) + 14429b4ad7b6Sdamien 2 + ni->ni_esslen + 14439b4ad7b6Sdamien 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + 14449b4ad7b6Sdamien ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? 14459b4ad7b6Sdamien 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) + 1446e03e709cSdamien (((ic->ic_flags & IEEE80211_F_RSNON) && 14479654ac02Sdamien (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) ? 14489654ac02Sdamien 2 + IEEE80211_RSNIE_MAXLEN : 0) + 144945eec175Sdamien ((ni->ni_flags & IEEE80211_NODE_QOS) ? 2 + 1 : 0) + 1450e03e709cSdamien (((ic->ic_flags & IEEE80211_F_RSNON) && 14519654ac02Sdamien (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ? 145245eec175Sdamien 2 + IEEE80211_WPAIE_MAXLEN : 0) + 145350e8fe1cSstsp ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) + 145450e8fe1cSstsp ((ic->ic_flags & IEEE80211_F_VHTON) ? 14 : 0)); 145591b2158bSmillert if (m == NULL) 14565d49b5a5Sdamien return NULL; 145791b2158bSmillert 14585d49b5a5Sdamien frm = mtod(m, u_int8_t *); 14591934f67dSstsp capinfo = IEEE80211_CAPINFO_ESS; 146091b2158bSmillert if (ic->ic_flags & IEEE80211_F_WEPON) 146191b2158bSmillert capinfo |= IEEE80211_CAPINFO_PRIVACY; 146291b2158bSmillert if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 146391b2158bSmillert IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) 146491b2158bSmillert capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 1465a0981291Sdamien if (ic->ic_caps & IEEE80211_C_SHSLOT) 146691b2158bSmillert capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 14678d0ac44dSdamien LE_WRITE_2(frm, capinfo); frm += 2; 14688d0ac44dSdamien LE_WRITE_2(frm, ic->ic_lintval); frm += 2; 14694dfbe550Sdamien if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 147091b2158bSmillert IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); 147191b2158bSmillert frm += IEEE80211_ADDR_LEN; 147291b2158bSmillert } 147391b2158bSmillert frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); 14748c4d58d2Sdamien frm = ieee80211_add_rates(frm, rs); 14758c4d58d2Sdamien if (rs->rs_nrates > IEEE80211_RATE_SIZE) 14768c4d58d2Sdamien frm = ieee80211_add_xrates(frm, rs); 1477e03e709cSdamien if ((ic->ic_flags & IEEE80211_F_RSNON) && 1478e03e709cSdamien (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) 1479e03e709cSdamien frm = ieee80211_add_rsn(frm, ic, ni); 148045eec175Sdamien if (ni->ni_flags & IEEE80211_NODE_QOS) 14816003857fSdamien frm = ieee80211_add_qos_capability(frm, ic); 1482e03e709cSdamien if ((ic->ic_flags & IEEE80211_F_RSNON) && 1483e03e709cSdamien (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) 1484e03e709cSdamien frm = ieee80211_add_wpa(frm, ic, ni); 14856999278eSstsp if (ic->ic_flags & IEEE80211_F_HTON) { 148645eec175Sdamien frm = ieee80211_add_htcaps(frm, ic); 14876999278eSstsp frm = ieee80211_add_wme_info(frm, ic); 14886999278eSstsp } 148950e8fe1cSstsp if (ic->ic_flags & IEEE80211_F_VHTON) 149050e8fe1cSstsp frm = ieee80211_add_vhtcaps(frm, ic); 14915d49b5a5Sdamien 149291b2158bSmillert m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 149391b2158bSmillert 14945d49b5a5Sdamien return m; 14955d49b5a5Sdamien } 149691b2158bSmillert 1497171ac09aSdamien #ifndef IEEE80211_STA_ONLY 14985d49b5a5Sdamien /*- 14995d49b5a5Sdamien * (Re)Association response frame format: 15005d49b5a5Sdamien * [2] Capability information 15015d49b5a5Sdamien * [2] Status code 15025d49b5a5Sdamien * [2] Association ID (AID) 15035d49b5a5Sdamien * [tlv] Supported rates 15045d49b5a5Sdamien * [tlv] Extended Supported Rates (802.11g) 15055d49b5a5Sdamien * [tlv] EDCA Parameter Set (802.11e) 150645eec175Sdamien * [tlv] Timeout Interval (802.11w) 150745eec175Sdamien * [tlv] HT Capabilities (802.11n) 150845eec175Sdamien * [tlv] HT Operation (802.11n) 150991b2158bSmillert */ 15105d49b5a5Sdamien struct mbuf * 15115d49b5a5Sdamien ieee80211_get_assoc_resp(struct ieee80211com *ic, struct ieee80211_node *ni, 15125d49b5a5Sdamien u_int16_t status) 15135d49b5a5Sdamien { 15149b4ad7b6Sdamien const struct ieee80211_rateset *rs = &ni->ni_rates; 15155d49b5a5Sdamien struct mbuf *m; 15165d49b5a5Sdamien u_int8_t *frm; 15175d49b5a5Sdamien 1518e03e709cSdamien m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 15199b4ad7b6Sdamien 2 + 2 + 2 + 15209b4ad7b6Sdamien 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + 15219b4ad7b6Sdamien ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? 15229b4ad7b6Sdamien 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) + 152345eec175Sdamien ((ni->ni_flags & IEEE80211_NODE_QOS) ? 2 + 18 : 0) + 152445eec175Sdamien ((status == IEEE80211_STATUS_TRY_AGAIN_LATER) ? 2 + 7 : 0) + 15255fb37217Sstsp ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 24 + 26 : 0)); 152691b2158bSmillert if (m == NULL) 15275d49b5a5Sdamien return NULL; 152891b2158bSmillert 15295d49b5a5Sdamien frm = mtod(m, u_int8_t *); 15308d0ac44dSdamien frm = ieee80211_add_capinfo(frm, ic, ni); 15318d0ac44dSdamien LE_WRITE_2(frm, status); frm += 2; 15325d49b5a5Sdamien if (status == IEEE80211_STATUS_SUCCESS) 15338d0ac44dSdamien LE_WRITE_2(frm, ni->ni_associd); 15348d0ac44dSdamien else 15358d0ac44dSdamien LE_WRITE_2(frm, 0); 153691b2158bSmillert frm += 2; 15378c4d58d2Sdamien frm = ieee80211_add_rates(frm, rs); 15388c4d58d2Sdamien if (rs->rs_nrates > IEEE80211_RATE_SIZE) 15398c4d58d2Sdamien frm = ieee80211_add_xrates(frm, rs); 154045eec175Sdamien if (ni->ni_flags & IEEE80211_NODE_QOS) 15416003857fSdamien frm = ieee80211_add_edca_params(frm, ic); 154245eec175Sdamien if ((ni->ni_flags & IEEE80211_NODE_MFP) && 154345eec175Sdamien status == IEEE80211_STATUS_TRY_AGAIN_LATER) { 154445eec175Sdamien /* Association Comeback Time */ 154545eec175Sdamien frm = ieee80211_add_tie(frm, 3, 1000 /* XXX */); 154645eec175Sdamien } 15476999278eSstsp if (ic->ic_flags & IEEE80211_F_HTON) { 154845eec175Sdamien frm = ieee80211_add_htcaps(frm, ic); 154945eec175Sdamien frm = ieee80211_add_htop(frm, ic); 15505fb37217Sstsp frm = ieee80211_add_wme_param(frm, ic); 155145eec175Sdamien } 15525d49b5a5Sdamien 155391b2158bSmillert m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 15545d49b5a5Sdamien 15555d49b5a5Sdamien return m; 15565d49b5a5Sdamien } 1557171ac09aSdamien #endif /* IEEE80211_STA_ONLY */ 15585d49b5a5Sdamien 15595d49b5a5Sdamien /*- 15605d49b5a5Sdamien * Disassociation frame format: 15615d49b5a5Sdamien * [2] Reason code 15625d49b5a5Sdamien */ 15635d49b5a5Sdamien struct mbuf * 15645d49b5a5Sdamien ieee80211_get_disassoc(struct ieee80211com *ic, struct ieee80211_node *ni, 15655d49b5a5Sdamien u_int16_t reason) 15665d49b5a5Sdamien { 15675d49b5a5Sdamien struct mbuf *m; 15685d49b5a5Sdamien 15695d49b5a5Sdamien MGETHDR(m, M_DONTWAIT, MT_DATA); 15705d49b5a5Sdamien if (m == NULL) 15715d49b5a5Sdamien return NULL; 1572894ad73aSclaudio m_align(m, 2); 15735d49b5a5Sdamien m->m_pkthdr.len = m->m_len = 2; 1574894ad73aSclaudio 15755d49b5a5Sdamien *mtod(m, u_int16_t *) = htole16(reason); 15765d49b5a5Sdamien 15775d49b5a5Sdamien return m; 15785d49b5a5Sdamien } 15795d49b5a5Sdamien 158045eec175Sdamien /*- 158145eec175Sdamien * ADDBA Request frame format: 158245eec175Sdamien * [1] Category 158345eec175Sdamien * [1] Action 158445eec175Sdamien * [1] Dialog Token 158545eec175Sdamien * [2] Block Ack Parameter Set 158645eec175Sdamien * [2] Block Ack Timeout Value 158745eec175Sdamien * [2] Block Ack Starting Sequence Control 158845eec175Sdamien */ 158945eec175Sdamien struct mbuf * 159045eec175Sdamien ieee80211_get_addba_req(struct ieee80211com *ic, struct ieee80211_node *ni, 159145eec175Sdamien u_int8_t tid) 159245eec175Sdamien { 1593ec69e05aSdamien struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; 159445eec175Sdamien struct mbuf *m; 159545eec175Sdamien u_int8_t *frm; 159645eec175Sdamien 159745eec175Sdamien m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 9); 159845eec175Sdamien if (m == NULL) 159945eec175Sdamien return m; 160045eec175Sdamien 160145eec175Sdamien frm = mtod(m, u_int8_t *); 160245eec175Sdamien *frm++ = IEEE80211_CATEG_BA; 160345eec175Sdamien *frm++ = IEEE80211_ACTION_ADDBA_REQ; 160445eec175Sdamien *frm++ = ba->ba_token; 16053367136cSstsp LE_WRITE_2(frm, ba->ba_params); frm += 2; 1606387ad762Sstsp LE_WRITE_2(frm, ba->ba_timeout_val / IEEE80211_DUR_TU); frm += 2; 1607aefc44daSstsp LE_WRITE_2(frm, ba->ba_winstart << IEEE80211_SEQ_SEQ_SHIFT); frm += 2; 160845eec175Sdamien 160945eec175Sdamien m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 161045eec175Sdamien 161145eec175Sdamien return m; 161245eec175Sdamien } 161345eec175Sdamien 1614aefc44daSstsp /* Move Tx BA window forward to the specified SSN. */ 1615aefc44daSstsp void 1616aefc44daSstsp ieee80211_output_ba_move_window(struct ieee80211com *ic, 1617aefc44daSstsp struct ieee80211_node *ni, uint8_t tid, uint16_t ssn) 1618aefc44daSstsp { 1619aefc44daSstsp struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; 1620aefc44daSstsp uint16_t s = ba->ba_winstart; 1621aefc44daSstsp 1622aefc44daSstsp while (SEQ_LT(s, ssn) && ba->ba_bitmap) { 1623aefc44daSstsp s = (s + 1) % 0xfff; 1624aefc44daSstsp ba->ba_bitmap >>= 1; 1625aefc44daSstsp } 1626aefc44daSstsp 1627aefc44daSstsp ba->ba_winstart = (ssn & 0xfff); 1628aefc44daSstsp ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff; 1629aefc44daSstsp } 1630aefc44daSstsp 1631aefc44daSstsp /* 1632aefc44daSstsp * Move Tx BA window forward up to the first hole in the bitmap 1633aefc44daSstsp * or up to the specified SSN, whichever comes first. 1634aefc44daSstsp * After calling this function, frames before the start of the 1635aefc44daSstsp * potentially changed BA window should be discarded. 1636aefc44daSstsp */ 1637aefc44daSstsp void 1638aefc44daSstsp ieee80211_output_ba_move_window_to_first_unacked(struct ieee80211com *ic, 1639aefc44daSstsp struct ieee80211_node *ni, uint8_t tid, uint16_t ssn) 1640aefc44daSstsp { 1641aefc44daSstsp struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; 1642aefc44daSstsp uint16_t s = ba->ba_winstart; 1643aefc44daSstsp uint64_t bitmap = ba->ba_bitmap; 1644aefc44daSstsp int can_move_window = 0; 1645aefc44daSstsp 1646aefc44daSstsp while (bitmap && SEQ_LT(s, ssn)) { 1647aefc44daSstsp if ((bitmap & 1) == 0) 1648aefc44daSstsp break; 1649aefc44daSstsp s = (s + 1) % 0xfff; 1650aefc44daSstsp bitmap >>= 1; 1651aefc44daSstsp can_move_window = 1; 1652aefc44daSstsp } 1653aefc44daSstsp 1654aefc44daSstsp if (can_move_window) 1655aefc44daSstsp ieee80211_output_ba_move_window(ic, ni, tid, s); 1656aefc44daSstsp } 1657aefc44daSstsp 1658aefc44daSstsp /* Record an ACK for a frame with a given SSN within the Tx BA window. */ 1659aefc44daSstsp void 1660aefc44daSstsp ieee80211_output_ba_record_ack(struct ieee80211com *ic, 1661aefc44daSstsp struct ieee80211_node *ni, uint8_t tid, uint16_t ssn) 1662aefc44daSstsp { 1663aefc44daSstsp struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; 1664aefc44daSstsp int i = 0; 1665aefc44daSstsp uint16_t s = ba->ba_winstart; 1666aefc44daSstsp 1667aefc44daSstsp KASSERT(!SEQ_LT(ssn, ba->ba_winstart)); 1668aefc44daSstsp KASSERT(!SEQ_LT(ba->ba_winend, ssn)); 1669aefc44daSstsp 1670aefc44daSstsp while (SEQ_LT(s, ssn)) { 1671aefc44daSstsp s = (s + 1) % 0xfff; 1672aefc44daSstsp i++; 1673aefc44daSstsp } 1674aefc44daSstsp if (i < ba->ba_winsize) 1675aefc44daSstsp ba->ba_bitmap |= (1 << i); 1676aefc44daSstsp } 1677aefc44daSstsp 167845eec175Sdamien /*- 167945eec175Sdamien * ADDBA Response frame format: 168045eec175Sdamien * [1] Category 168145eec175Sdamien * [1] Action 168245eec175Sdamien * [1] Dialog Token 168345eec175Sdamien * [2] Status Code 168445eec175Sdamien * [2] Block Ack Parameter Set 168545eec175Sdamien * [2] Block Ack Timeout Value 168645eec175Sdamien */ 168745eec175Sdamien struct mbuf * 168845eec175Sdamien ieee80211_get_addba_resp(struct ieee80211com *ic, struct ieee80211_node *ni, 1689ec69e05aSdamien u_int8_t tid, u_int8_t token, u_int16_t status) 169045eec175Sdamien { 16913b09a507Sdamien struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; 169245eec175Sdamien struct mbuf *m; 169345eec175Sdamien u_int8_t *frm; 169445eec175Sdamien u_int16_t params; 169545eec175Sdamien 169645eec175Sdamien m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 9); 169745eec175Sdamien if (m == NULL) 169845eec175Sdamien return m; 169945eec175Sdamien 170045eec175Sdamien frm = mtod(m, u_int8_t *); 170145eec175Sdamien *frm++ = IEEE80211_CATEG_BA; 170245eec175Sdamien *frm++ = IEEE80211_ACTION_ADDBA_RESP; 1703ec69e05aSdamien *frm++ = token; 170445eec175Sdamien LE_WRITE_2(frm, status); frm += 2; 1705ec69e05aSdamien if (status == 0) 17063367136cSstsp params = ba->ba_params; 17073367136cSstsp else 17083367136cSstsp params = tid << IEEE80211_ADDBA_TID_SHIFT; 170945eec175Sdamien LE_WRITE_2(frm, params); frm += 2; 1710ec69e05aSdamien if (status == 0) 1711387ad762Sstsp LE_WRITE_2(frm, ba->ba_timeout_val / IEEE80211_DUR_TU); 1712ec69e05aSdamien else 1713ec69e05aSdamien LE_WRITE_2(frm, 0); 1714ec69e05aSdamien frm += 2; 171545eec175Sdamien 171645eec175Sdamien m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 171745eec175Sdamien 171845eec175Sdamien return m; 171945eec175Sdamien } 172045eec175Sdamien 172145eec175Sdamien /*- 172245eec175Sdamien * DELBA frame format: 172345eec175Sdamien * [1] Category 172445eec175Sdamien * [1] Action 172545eec175Sdamien * [2] DELBA Parameter Set 172645eec175Sdamien * [2] Reason Code 172745eec175Sdamien */ 172845eec175Sdamien struct mbuf * 172945eec175Sdamien ieee80211_get_delba(struct ieee80211com *ic, struct ieee80211_node *ni, 1730ec69e05aSdamien u_int8_t tid, u_int8_t dir, u_int16_t reason) 173145eec175Sdamien { 173245eec175Sdamien struct mbuf *m; 173345eec175Sdamien u_int8_t *frm; 173445eec175Sdamien u_int16_t params; 173545eec175Sdamien 173645eec175Sdamien m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 6); 173745eec175Sdamien if (m == NULL) 173845eec175Sdamien return m; 173945eec175Sdamien 174045eec175Sdamien frm = mtod(m, u_int8_t *); 174145eec175Sdamien *frm++ = IEEE80211_CATEG_BA; 174245eec175Sdamien *frm++ = IEEE80211_ACTION_DELBA; 1743ec69e05aSdamien params = tid << 12; 1744ec69e05aSdamien if (dir) 1745ec69e05aSdamien params |= IEEE80211_DELBA_INITIATOR; 174645eec175Sdamien LE_WRITE_2(frm, params); frm += 2; 174745eec175Sdamien LE_WRITE_2(frm, reason); frm += 2; 174845eec175Sdamien 174945eec175Sdamien m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 175045eec175Sdamien 175145eec175Sdamien return m; 175245eec175Sdamien } 175345eec175Sdamien 175445eec175Sdamien /*- 1755678831beSjsg * SA Query Request/Response frame format: 175645eec175Sdamien * [1] Category 175745eec175Sdamien * [1] Action 175845eec175Sdamien * [16] Transaction Identifier 175945eec175Sdamien */ 176045eec175Sdamien struct mbuf * 176145eec175Sdamien ieee80211_get_sa_query(struct ieee80211com *ic, struct ieee80211_node *ni, 176245eec175Sdamien u_int8_t action) 176345eec175Sdamien { 176445eec175Sdamien struct mbuf *m; 176545eec175Sdamien u_int8_t *frm; 176645eec175Sdamien 1767199d5af3Sdamien m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 4); 176845eec175Sdamien if (m == NULL) 176945eec175Sdamien return NULL; 177045eec175Sdamien 177145eec175Sdamien frm = mtod(m, u_int8_t *); 177245eec175Sdamien *frm++ = IEEE80211_CATEG_SA_QUERY; 177345eec175Sdamien *frm++ = action; /* ACTION_SA_QUERY_REQ/RESP */ 1774199d5af3Sdamien LE_WRITE_2(frm, ni->ni_sa_query_trid); frm += 2; 1775199d5af3Sdamien 1776199d5af3Sdamien m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 177745eec175Sdamien 177845eec175Sdamien return m; 177945eec175Sdamien } 178045eec175Sdamien 178145eec175Sdamien struct mbuf * 178245eec175Sdamien ieee80211_get_action(struct ieee80211com *ic, struct ieee80211_node *ni, 178345eec175Sdamien u_int8_t categ, u_int8_t action, int arg) 178445eec175Sdamien { 178545eec175Sdamien struct mbuf *m = NULL; 178645eec175Sdamien 178745eec175Sdamien switch (categ) { 178845eec175Sdamien case IEEE80211_CATEG_BA: 178945eec175Sdamien switch (action) { 179045eec175Sdamien case IEEE80211_ACTION_ADDBA_REQ: 179145eec175Sdamien m = ieee80211_get_addba_req(ic, ni, arg & 0xffff); 179245eec175Sdamien break; 179345eec175Sdamien case IEEE80211_ACTION_ADDBA_RESP: 1794ec69e05aSdamien m = ieee80211_get_addba_resp(ic, ni, arg & 0xff, 1795ec69e05aSdamien arg >> 8, arg >> 16); 179645eec175Sdamien break; 179745eec175Sdamien case IEEE80211_ACTION_DELBA: 1798ec69e05aSdamien m = ieee80211_get_delba(ic, ni, arg & 0xff, arg >> 8, 179945eec175Sdamien arg >> 16); 180045eec175Sdamien break; 180145eec175Sdamien } 180245eec175Sdamien break; 180345eec175Sdamien case IEEE80211_CATEG_SA_QUERY: 180445eec175Sdamien switch (action) { 180545eec175Sdamien #ifndef IEEE80211_STA_ONLY 180645eec175Sdamien case IEEE80211_ACTION_SA_QUERY_REQ: 180745eec175Sdamien #endif 180845eec175Sdamien case IEEE80211_ACTION_SA_QUERY_RESP: 180945eec175Sdamien m = ieee80211_get_sa_query(ic, ni, action); 181045eec175Sdamien break; 181145eec175Sdamien } 181245eec175Sdamien break; 181345eec175Sdamien } 181445eec175Sdamien return m; 181545eec175Sdamien } 181645eec175Sdamien 18175d49b5a5Sdamien /* 18185d49b5a5Sdamien * Send a management frame. The node is for the destination (or ic_bss 18195d49b5a5Sdamien * when in station mode). Nodes other than ic_bss have their reference 1820*54fbbda3Sjsg * count bumped to reflect our use for an indeterminate time. 18215d49b5a5Sdamien */ 18225d49b5a5Sdamien int 18235d49b5a5Sdamien ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, 182445eec175Sdamien int type, int arg1, int arg2) 18255d49b5a5Sdamien { 18265d49b5a5Sdamien #define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) 18275d49b5a5Sdamien struct ifnet *ifp = &ic->ic_if; 18285d49b5a5Sdamien struct mbuf *m; 18295d49b5a5Sdamien int ret, timer; 18305d49b5a5Sdamien 18315d49b5a5Sdamien if (ni == NULL) 18325d49b5a5Sdamien panic("null node"); 18335d49b5a5Sdamien 18345d49b5a5Sdamien /* 18355d49b5a5Sdamien * Hold a reference on the node so it doesn't go away until after 18365d49b5a5Sdamien * the xmit is complete all the way in the driver. On error we 18375d49b5a5Sdamien * will remove our reference. 18385d49b5a5Sdamien */ 18395d49b5a5Sdamien ieee80211_ref_node(ni); 18405d49b5a5Sdamien timer = 0; 18415d49b5a5Sdamien switch (type) { 18425d49b5a5Sdamien case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 18435d49b5a5Sdamien if ((m = ieee80211_get_probe_req(ic, ni)) == NULL) 18445d49b5a5Sdamien senderr(ENOMEM, is_tx_nombuf); 18455d49b5a5Sdamien 18465d49b5a5Sdamien timer = IEEE80211_TRANS_WAIT; 18475d49b5a5Sdamien break; 1848171ac09aSdamien #ifndef IEEE80211_STA_ONLY 18495d49b5a5Sdamien case IEEE80211_FC0_SUBTYPE_PROBE_RESP: 1850774cd68cSstsp if ((m = ieee80211_get_probe_resp(ic)) == NULL) 18515d49b5a5Sdamien senderr(ENOMEM, is_tx_nombuf); 18525d49b5a5Sdamien break; 1853171ac09aSdamien #endif 18545d49b5a5Sdamien case IEEE80211_FC0_SUBTYPE_AUTH: 185545eec175Sdamien m = ieee80211_get_auth(ic, ni, arg1 >> 16, arg1 & 0xffff); 18565d49b5a5Sdamien if (m == NULL) 18575d49b5a5Sdamien senderr(ENOMEM, is_tx_nombuf); 18585d49b5a5Sdamien 18595d49b5a5Sdamien if (ic->ic_opmode == IEEE80211_M_STA) 18605d49b5a5Sdamien timer = IEEE80211_TRANS_WAIT; 18615d49b5a5Sdamien break; 18625d49b5a5Sdamien 18635d49b5a5Sdamien case IEEE80211_FC0_SUBTYPE_DEAUTH: 186445eec175Sdamien if ((m = ieee80211_get_deauth(ic, ni, arg1)) == NULL) 18655d49b5a5Sdamien senderr(ENOMEM, is_tx_nombuf); 1866a5879dd6Sstsp #ifndef IEEE80211_STA_ONLY 1867bc633e0cSstsp if ((ifp->if_flags & IFF_DEBUG) && 1868bc633e0cSstsp (ic->ic_opmode == IEEE80211_M_HOSTAP || 1869bc633e0cSstsp ic->ic_opmode == IEEE80211_M_IBSS)) 18705d49b5a5Sdamien printf("%s: station %s deauthenticate (reason %d)\n", 187145eec175Sdamien ifp->if_xname, ether_sprintf(ni->ni_macaddr), 187245eec175Sdamien arg1); 1873a5879dd6Sstsp #endif 18745d49b5a5Sdamien break; 18755d49b5a5Sdamien 18765d49b5a5Sdamien case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: 18775d49b5a5Sdamien case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: 18785d49b5a5Sdamien if ((m = ieee80211_get_assoc_req(ic, ni, type)) == NULL) 18795d49b5a5Sdamien senderr(ENOMEM, is_tx_nombuf); 18805d49b5a5Sdamien 18815d49b5a5Sdamien timer = IEEE80211_TRANS_WAIT; 18825d49b5a5Sdamien break; 1883171ac09aSdamien #ifndef IEEE80211_STA_ONLY 18845d49b5a5Sdamien case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: 18855d49b5a5Sdamien case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: 188645eec175Sdamien if ((m = ieee80211_get_assoc_resp(ic, ni, arg1)) == NULL) 18875d49b5a5Sdamien senderr(ENOMEM, is_tx_nombuf); 188891b2158bSmillert break; 1889171ac09aSdamien #endif 189091b2158bSmillert case IEEE80211_FC0_SUBTYPE_DISASSOC: 189145eec175Sdamien if ((m = ieee80211_get_disassoc(ic, ni, arg1)) == NULL) 18925d49b5a5Sdamien senderr(ENOMEM, is_tx_nombuf); 1893a5879dd6Sstsp #ifndef IEEE80211_STA_ONLY 1894bc633e0cSstsp if ((ifp->if_flags & IFF_DEBUG) && 1895bc633e0cSstsp (ic->ic_opmode == IEEE80211_M_HOSTAP || 1896bc633e0cSstsp ic->ic_opmode == IEEE80211_M_IBSS)) 1897a0068c42Sjsg printf("%s: station %s disassociate (reason %d)\n", 189845eec175Sdamien ifp->if_xname, ether_sprintf(ni->ni_macaddr), 189945eec175Sdamien arg1); 1900a5879dd6Sstsp #endif 190191b2158bSmillert break; 190291b2158bSmillert 190345eec175Sdamien case IEEE80211_FC0_SUBTYPE_ACTION: 190445eec175Sdamien m = ieee80211_get_action(ic, ni, arg1 >> 16, arg1 & 0xffff, 190545eec175Sdamien arg2); 190645eec175Sdamien if (m == NULL) 190745eec175Sdamien senderr(ENOMEM, is_tx_nombuf); 190845eec175Sdamien break; 190945eec175Sdamien 191091b2158bSmillert default: 1911932b9027Sdamien DPRINTF(("invalid mgmt frame type %u\n", type)); 191291b2158bSmillert senderr(EINVAL, is_tx_unknownmgt); 191391b2158bSmillert /* NOTREACHED */ 191491b2158bSmillert } 191591b2158bSmillert 191691b2158bSmillert ret = ieee80211_mgmt_output(ifp, ni, m, type); 191791b2158bSmillert if (ret == 0) { 191891b2158bSmillert if (timer) 191991b2158bSmillert ic->ic_mgt_timer = timer; 192091b2158bSmillert } else { 192191b2158bSmillert bad: 19220fd4e251Sreyk ieee80211_release_node(ic, ni); 192391b2158bSmillert } 192491b2158bSmillert return ret; 192591b2158bSmillert #undef senderr 192691b2158bSmillert } 192791b2158bSmillert 1928b277585aSdamien /* 1929eba1a6b6Sdamien * Build a RTS (Request To Send) control frame (see 7.2.1.1). 1930b277585aSdamien */ 1931b277585aSdamien struct mbuf * 1932b277585aSdamien ieee80211_get_rts(struct ieee80211com *ic, const struct ieee80211_frame *wh, 1933b277585aSdamien u_int16_t dur) 1934b277585aSdamien { 1935b277585aSdamien struct ieee80211_frame_rts *rts; 1936b277585aSdamien struct mbuf *m; 1937b277585aSdamien 1938b277585aSdamien MGETHDR(m, M_DONTWAIT, MT_DATA); 1939acc347deSdamien if (m == NULL) 1940b277585aSdamien return NULL; 1941acc347deSdamien 1942b277585aSdamien m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts); 1943b277585aSdamien 1944b277585aSdamien rts = mtod(m, struct ieee80211_frame_rts *); 1945b277585aSdamien rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL | 1946b277585aSdamien IEEE80211_FC0_SUBTYPE_RTS; 1947b277585aSdamien rts->i_fc[1] = IEEE80211_FC1_DIR_NODS; 1948b39feb1dSdamien *(u_int16_t *)rts->i_dur = htole16(dur); 1949b277585aSdamien IEEE80211_ADDR_COPY(rts->i_ra, wh->i_addr1); 1950b277585aSdamien IEEE80211_ADDR_COPY(rts->i_ta, wh->i_addr2); 1951b277585aSdamien 1952b277585aSdamien return m; 1953b277585aSdamien } 1954b277585aSdamien 1955b277585aSdamien /* 1956eba1a6b6Sdamien * Build a CTS-to-self (Clear To Send) control frame (see 7.2.1.2). 1957b277585aSdamien */ 1958b277585aSdamien struct mbuf * 1959b277585aSdamien ieee80211_get_cts_to_self(struct ieee80211com *ic, u_int16_t dur) 1960b277585aSdamien { 1961b277585aSdamien struct ieee80211_frame_cts *cts; 1962b277585aSdamien struct mbuf *m; 1963b277585aSdamien 1964b277585aSdamien MGETHDR(m, M_DONTWAIT, MT_DATA); 1965acc347deSdamien if (m == NULL) 1966b277585aSdamien return NULL; 1967acc347deSdamien 1968b277585aSdamien m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts); 1969b277585aSdamien 1970b277585aSdamien cts = mtod(m, struct ieee80211_frame_cts *); 1971b277585aSdamien cts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL | 1972b277585aSdamien IEEE80211_FC0_SUBTYPE_CTS; 1973b277585aSdamien cts->i_fc[1] = IEEE80211_FC1_DIR_NODS; 1974b39feb1dSdamien *(u_int16_t *)cts->i_dur = htole16(dur); 1975b277585aSdamien IEEE80211_ADDR_COPY(cts->i_ra, ic->ic_myaddr); 1976b277585aSdamien 1977b277585aSdamien return m; 1978b277585aSdamien } 1979b277585aSdamien 1980aefc44daSstsp /* 1981aefc44daSstsp * Build a compressed Block Ack Request control frame. 1982aefc44daSstsp */ 1983aefc44daSstsp struct mbuf * 1984aefc44daSstsp ieee80211_get_compressed_bar(struct ieee80211com *ic, 1985aefc44daSstsp struct ieee80211_node *ni, int tid, uint16_t ssn) 1986aefc44daSstsp { 1987aefc44daSstsp struct ieee80211_frame_min *wh; 1988aefc44daSstsp uint8_t *frm; 1989aefc44daSstsp uint16_t ctl; 1990aefc44daSstsp struct mbuf *m; 1991aefc44daSstsp 1992aefc44daSstsp MGETHDR(m, M_DONTWAIT, MT_DATA); 1993aefc44daSstsp if (m == NULL) 1994aefc44daSstsp return NULL; 1995aefc44daSstsp 1996aefc44daSstsp m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_min) + 1997aefc44daSstsp sizeof(ctl) + sizeof(ssn); 1998aefc44daSstsp 1999aefc44daSstsp wh = mtod(m, struct ieee80211_frame_min *); 2000aefc44daSstsp wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL | 2001aefc44daSstsp IEEE80211_FC0_SUBTYPE_BAR; 2002aefc44daSstsp wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 2003aefc44daSstsp *(u_int16_t *)wh->i_dur = 0; 2004aefc44daSstsp IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); 2005aefc44daSstsp IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); 2006aefc44daSstsp frm = (uint8_t *)&wh[1]; 2007aefc44daSstsp 2008aefc44daSstsp ctl = IEEE80211_BA_COMPRESSED | (tid << IEEE80211_BA_TID_INFO_SHIFT); 2009aefc44daSstsp LE_WRITE_2(frm, ctl); 2010aefc44daSstsp frm += 2; 2011aefc44daSstsp 2012aefc44daSstsp LE_WRITE_2(frm, ssn << IEEE80211_SEQ_SEQ_SHIFT); 2013aefc44daSstsp frm += 2; 2014aefc44daSstsp 2015aefc44daSstsp m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 2016aefc44daSstsp m->m_pkthdr.ph_cookie = ni; 2017aefc44daSstsp 2018aefc44daSstsp return m; 2019aefc44daSstsp } 2020aefc44daSstsp 2021171ac09aSdamien #ifndef IEEE80211_STA_ONLY 20228d0ac44dSdamien /*- 20238d0ac44dSdamien * Beacon frame format: 20248d0ac44dSdamien * [8] Timestamp 20258d0ac44dSdamien * [2] Beacon interval 20268d0ac44dSdamien * [2] Capability 20278d0ac44dSdamien * [tlv] Service Set Identifier (SSID) 20288d0ac44dSdamien * [tlv] Supported rates 202945eec175Sdamien * [tlv] DS Parameter Set (802.11g) 203045eec175Sdamien * [tlv] IBSS Parameter Set 20318d0ac44dSdamien * [tlv] Traffic Indication Map (TIM) 20328d0ac44dSdamien * [tlv] ERP Information (802.11g) 20338d0ac44dSdamien * [tlv] Extended Supported Rates (802.11g) 20348d0ac44dSdamien * [tlv] RSN (802.11i) 20358d0ac44dSdamien * [tlv] EDCA Parameter Set (802.11e) 203645eec175Sdamien * [tlv] HT Capabilities (802.11n) 203745eec175Sdamien * [tlv] HT Operation (802.11n) 20388d0ac44dSdamien */ 2039313f2acbSdamien struct mbuf * 2040313f2acbSdamien ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni) 2041313f2acbSdamien { 20429b4ad7b6Sdamien const struct ieee80211_rateset *rs = &ni->ni_rates; 2043313f2acbSdamien struct ieee80211_frame *wh; 2044313f2acbSdamien struct mbuf *m; 2045313f2acbSdamien u_int8_t *frm; 2046313f2acbSdamien 2047e03e709cSdamien m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 20489b4ad7b6Sdamien 8 + 2 + 2 + 2049534bf8f4Sstsp 2 + ((ic->ic_userflags & IEEE80211_F_HIDENWID) ? 2050534bf8f4Sstsp 0 : ni->ni_esslen) + 20519b4ad7b6Sdamien 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + 2052e63afc57Sdamien 2 + 1 + 20539b4ad7b6Sdamien 2 + ((ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 254) + 20549b4ad7b6Sdamien ((ic->ic_curmode == IEEE80211_MODE_11G) ? 2 + 1 : 0) + 20559b4ad7b6Sdamien ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? 20569b4ad7b6Sdamien 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) + 2057e03e709cSdamien (((ic->ic_flags & IEEE80211_F_RSNON) && 20589654ac02Sdamien (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) ? 20599654ac02Sdamien 2 + IEEE80211_RSNIE_MAXLEN : 0) + 20609b4ad7b6Sdamien ((ic->ic_flags & IEEE80211_F_QOS) ? 2 + 18 : 0) + 2061e03e709cSdamien (((ic->ic_flags & IEEE80211_F_RSNON) && 20629654ac02Sdamien (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ? 206345eec175Sdamien 2 + IEEE80211_WPAIE_MAXLEN : 0) + 20645fb37217Sstsp ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 24 + 26 : 0)); 2065313f2acbSdamien if (m == NULL) 2066313f2acbSdamien return NULL; 2067313f2acbSdamien 2068e03e709cSdamien M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); 2069e03e709cSdamien if (m == NULL) 2070e03e709cSdamien return NULL; 2071313f2acbSdamien wh = mtod(m, struct ieee80211_frame *); 2072313f2acbSdamien wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 2073313f2acbSdamien IEEE80211_FC0_SUBTYPE_BEACON; 2074313f2acbSdamien wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 2075313f2acbSdamien *(u_int16_t *)wh->i_dur = 0; 2076313f2acbSdamien IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr); 2077313f2acbSdamien IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); 2078313f2acbSdamien IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); 2079313f2acbSdamien *(u_int16_t *)wh->i_seq = 0; 2080313f2acbSdamien 2081313f2acbSdamien frm = (u_int8_t *)&wh[1]; 20829b4ad7b6Sdamien memset(frm, 0, 8); frm += 8; /* timestamp is set by hardware */ 20838d0ac44dSdamien LE_WRITE_2(frm, ni->ni_intval); frm += 2; 20848d0ac44dSdamien frm = ieee80211_add_capinfo(frm, ic, ni); 2085534bf8f4Sstsp if (ic->ic_userflags & IEEE80211_F_HIDENWID) 2086a6ad66bcSdamien frm = ieee80211_add_ssid(frm, NULL, 0); 208775175d43Sreyk else 20886e5bd35cSdamien frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); 2089313f2acbSdamien frm = ieee80211_add_rates(frm, rs); 20908d0ac44dSdamien frm = ieee80211_add_ds_params(frm, ic, ni); 2091158c4605Sdamien if (ic->ic_opmode == IEEE80211_M_IBSS) 20928d0ac44dSdamien frm = ieee80211_add_ibss_params(frm, ni); 2093158c4605Sdamien else 2094158c4605Sdamien frm = ieee80211_add_tim(frm, ic); 20951bb78573Sdamien if (ic->ic_curmode == IEEE80211_MODE_11G) 20961bb78573Sdamien frm = ieee80211_add_erp(frm, ic); 20978c4d58d2Sdamien if (rs->rs_nrates > IEEE80211_RATE_SIZE) 2098313f2acbSdamien frm = ieee80211_add_xrates(frm, rs); 2099e03e709cSdamien if ((ic->ic_flags & IEEE80211_F_RSNON) && 2100e03e709cSdamien (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) 21013a557689Sdamien frm = ieee80211_add_rsn(frm, ic, ni); 21026003857fSdamien if (ic->ic_flags & IEEE80211_F_QOS) 21036003857fSdamien frm = ieee80211_add_edca_params(frm, ic); 2104e03e709cSdamien if ((ic->ic_flags & IEEE80211_F_RSNON) && 2105e03e709cSdamien (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) 2106e03e709cSdamien frm = ieee80211_add_wpa(frm, ic, ni); 210745eec175Sdamien if (ic->ic_flags & IEEE80211_F_HTON) { 210845eec175Sdamien frm = ieee80211_add_htcaps(frm, ic); 210945eec175Sdamien frm = ieee80211_add_htop(frm, ic); 21105fb37217Sstsp frm = ieee80211_add_wme_param(frm, ic); 211145eec175Sdamien } 21128d0ac44dSdamien 2113313f2acbSdamien m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); 21146da4b19dSmpi m->m_pkthdr.ph_cookie = ni; 21151bb78573Sdamien 2116313f2acbSdamien return m; 2117313f2acbSdamien } 2118313f2acbSdamien 211993d01030Sdamien /* 212093d01030Sdamien * Check if an outgoing MSDU or management frame should be buffered into 212193d01030Sdamien * the AP for power management. Return 1 if the frame was buffered into 212293d01030Sdamien * the AP, or 0 if the frame shall be transmitted immediately. 212393d01030Sdamien */ 212493d01030Sdamien int 212593d01030Sdamien ieee80211_pwrsave(struct ieee80211com *ic, struct mbuf *m, 212693d01030Sdamien struct ieee80211_node *ni) 212791b2158bSmillert { 212893d01030Sdamien const struct ieee80211_frame *wh; 2129ca7fda7eSstsp int pssta = 0; 213091b2158bSmillert 213193d01030Sdamien KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP); 213293d01030Sdamien if (!(ic->ic_caps & IEEE80211_C_APPMGT)) 213393d01030Sdamien return 0; 213493d01030Sdamien 213593d01030Sdamien wh = mtod(m, struct ieee80211_frame *); 213693d01030Sdamien if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 213793d01030Sdamien /* 213893d01030Sdamien * Buffer group addressed MSDUs with the Order bit clear 213993d01030Sdamien * if any associated STAs are in PS mode. 214093d01030Sdamien */ 2141ca7fda7eSstsp ieee80211_iterate_nodes(ic, ieee80211_count_pssta, &pssta); 2142ca7fda7eSstsp if ((wh->i_fc[1] & IEEE80211_FC1_ORDER) || pssta == 0) 214393d01030Sdamien return 0; 214493d01030Sdamien ic->ic_tim_mcast_pending = 1; 214591b2158bSmillert } else { 2146158c4605Sdamien /* 214793d01030Sdamien * Buffer MSDUs, A-MSDUs or management frames destined for 214893d01030Sdamien * PS STAs. 214991b2158bSmillert */ 215093d01030Sdamien if (ni->ni_pwrsave == IEEE80211_PS_AWAKE || 215193d01030Sdamien (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == 215293d01030Sdamien IEEE80211_FC0_TYPE_CTL) 215393d01030Sdamien return 0; 2154351e1934Sdlg if (mq_empty(&ni->ni_savedq)) 215593d01030Sdamien (*ic->ic_set_tim)(ic, ni->ni_associd, 1); 215693d01030Sdamien } 215793d01030Sdamien /* NB: ni == ic->ic_bss for broadcast/multicast */ 215893d01030Sdamien /* 21596da4b19dSmpi * Similar to ieee80211_mgmt_output, store the node in a 21606da4b19dSmpi * special pkthdr field. 216193d01030Sdamien */ 21626da4b19dSmpi m->m_pkthdr.ph_cookie = ni; 2163351e1934Sdlg mq_enqueue(&ni->ni_savedq, m); 216493d01030Sdamien return 1; 216591b2158bSmillert } 2166171ac09aSdamien #endif /* IEEE80211_STA_ONLY */ 2167