xref: /openbsd-src/sys/net80211/ieee80211_output.c (revision 54fbbda3b5f8c42357b8601b12a514e2d25a2771)
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