10ba2cbe9Sxc /*
219d332feSfei feng - Sun Microsystems - Beijing China  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
30ba2cbe9Sxc  * Use is subject to license terms.
4*034536e9SHans Rosenfeld  *
5*034536e9SHans Rosenfeld  * Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
60ba2cbe9Sxc  */
70ba2cbe9Sxc 
80ba2cbe9Sxc /*
90ba2cbe9Sxc  * Copyright (c) 2001 Atsushi Onoe
1019d332feSfei feng - Sun Microsystems - Beijing China  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
110ba2cbe9Sxc  * All rights reserved.
120ba2cbe9Sxc  *
130ba2cbe9Sxc  * Redistribution and use in source and binary forms, with or without
140ba2cbe9Sxc  * modification, are permitted provided that the following conditions
150ba2cbe9Sxc  * are met:
160ba2cbe9Sxc  * 1. Redistributions of source code must retain the above copyright
170ba2cbe9Sxc  *    notice, this list of conditions and the following disclaimer.
180ba2cbe9Sxc  * 2. Redistributions in binary form must reproduce the above copyright
190ba2cbe9Sxc  *    notice, this list of conditions and the following disclaimer in the
200ba2cbe9Sxc  *    documentation and/or other materials provided with the distribution.
210ba2cbe9Sxc  * 3. The name of the author may not be used to endorse or promote products
220ba2cbe9Sxc  *    derived from this software without specific prior written permission.
230ba2cbe9Sxc  *
240ba2cbe9Sxc  * Alternatively, this software may be distributed under the terms of the
250ba2cbe9Sxc  * GNU General Public License ("GPL") version 2 as published by the Free
260ba2cbe9Sxc  * Software Foundation.
270ba2cbe9Sxc  *
280ba2cbe9Sxc  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
290ba2cbe9Sxc  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
300ba2cbe9Sxc  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
310ba2cbe9Sxc  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
320ba2cbe9Sxc  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
330ba2cbe9Sxc  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
340ba2cbe9Sxc  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
350ba2cbe9Sxc  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
360ba2cbe9Sxc  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
370ba2cbe9Sxc  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
380ba2cbe9Sxc  */
390ba2cbe9Sxc 
400ba2cbe9Sxc /*
410ba2cbe9Sxc  * Send out 802.11 frames
420ba2cbe9Sxc  */
430ba2cbe9Sxc 
440ba2cbe9Sxc #include <sys/byteorder.h>
450ba2cbe9Sxc #include <sys/strsun.h>
460ba2cbe9Sxc #include "net80211_impl.h"
470ba2cbe9Sxc 
480ba2cbe9Sxc /*
490ba2cbe9Sxc  * Set the direction field and address fields of an outgoing
500ba2cbe9Sxc  * non-QoS frame.  Note this should be called early on in
510ba2cbe9Sxc  * constructing a frame as it sets i_fc[1]; other bits can
520ba2cbe9Sxc  * then be or'd in.
530ba2cbe9Sxc  */
540ba2cbe9Sxc static void
ieee80211_send_setup(ieee80211com_t * ic,ieee80211_node_t * in,struct ieee80211_frame * wh,int type,const uint8_t * sa,const uint8_t * da,const uint8_t * bssid)550ba2cbe9Sxc ieee80211_send_setup(ieee80211com_t *ic, ieee80211_node_t *in,
560ba2cbe9Sxc     struct ieee80211_frame *wh, int type, const uint8_t *sa, const uint8_t *da,
570ba2cbe9Sxc     const uint8_t *bssid)
580ba2cbe9Sxc {
590ba2cbe9Sxc 	wh->i_fc[0] = (uint8_t)(IEEE80211_FC0_VERSION_0 | type);
600ba2cbe9Sxc 	if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
610ba2cbe9Sxc 		switch (ic->ic_opmode) {
620ba2cbe9Sxc 		case IEEE80211_M_STA:
630ba2cbe9Sxc 			wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
640ba2cbe9Sxc 			IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
650ba2cbe9Sxc 			IEEE80211_ADDR_COPY(wh->i_addr2, sa);
660ba2cbe9Sxc 			IEEE80211_ADDR_COPY(wh->i_addr3, da);
670ba2cbe9Sxc 			break;
680ba2cbe9Sxc 		case IEEE80211_M_IBSS:
690ba2cbe9Sxc 		case IEEE80211_M_AHDEMO:
700ba2cbe9Sxc 			wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
710ba2cbe9Sxc 			IEEE80211_ADDR_COPY(wh->i_addr1, da);
720ba2cbe9Sxc 			IEEE80211_ADDR_COPY(wh->i_addr2, sa);
730ba2cbe9Sxc 			IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
740ba2cbe9Sxc 			break;
750ba2cbe9Sxc 		default:
760ba2cbe9Sxc 			ieee80211_err("ieee80211_send_setup: "
77239e91abShx 			    "Invalid mode %u\n", ic->ic_opmode);
780ba2cbe9Sxc 			return;
790ba2cbe9Sxc 		}
800ba2cbe9Sxc 	} else {
810ba2cbe9Sxc 		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
820ba2cbe9Sxc 		IEEE80211_ADDR_COPY(wh->i_addr1, da);
830ba2cbe9Sxc 		IEEE80211_ADDR_COPY(wh->i_addr2, sa);
840ba2cbe9Sxc 		IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
850ba2cbe9Sxc 	}
860ba2cbe9Sxc 	*(uint16_t *)&wh->i_dur[0] = 0;	/* set duration */
87e2cf88acSQuaker Fang 	/* NB: use non-QoS tid */
88e2cf88acSQuaker Fang 	*(uint16_t *)&wh->i_seq[0] =
89e2cf88acSQuaker Fang 	    LE_16(in->in_txseqs[IEEE80211_NONQOS_TID] <<
90e2cf88acSQuaker Fang 	    IEEE80211_SEQ_SEQ_SHIFT);
91e2cf88acSQuaker Fang 	in->in_txseqs[IEEE80211_NONQOS_TID]++;
920ba2cbe9Sxc }
930ba2cbe9Sxc 
940ba2cbe9Sxc /*
950ba2cbe9Sxc  * Send a management frame to the specified node.  The node pointer
960ba2cbe9Sxc  * must have a reference as the pointer will be passed to the driver
970ba2cbe9Sxc  * and potentially held for a long time.  If the frame is successfully
980ba2cbe9Sxc  * dispatched to the driver, then it is responsible for freeing the
990ba2cbe9Sxc  * reference (and potentially free'ing up any associated storage).
1000ba2cbe9Sxc  *
1010ba2cbe9Sxc  * Return 0 on success
1020ba2cbe9Sxc  */
103e2cf88acSQuaker Fang int
ieee80211_mgmt_output(ieee80211com_t * ic,ieee80211_node_t * in,mblk_t * mp,int type,int timer)1040ba2cbe9Sxc ieee80211_mgmt_output(ieee80211com_t *ic, ieee80211_node_t *in, mblk_t *mp,
1050ba2cbe9Sxc     int type, int timer)
1060ba2cbe9Sxc {
1070ba2cbe9Sxc 	ieee80211_impl_t *im = ic->ic_private;
1080ba2cbe9Sxc 	struct ieee80211_frame *wh;
1090ba2cbe9Sxc 
1100ba2cbe9Sxc 	ASSERT(in != NULL);
1110ba2cbe9Sxc 
1120ba2cbe9Sxc 	wh = (struct ieee80211_frame *)mp->b_rptr;
1130ba2cbe9Sxc 	ieee80211_send_setup(ic, in, wh, IEEE80211_FC0_TYPE_MGT | type,
114239e91abShx 	    ic->ic_macaddr, in->in_macaddr, in->in_bssid);
1150ba2cbe9Sxc 	if (in->in_challenge != NULL)
1160ba2cbe9Sxc 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
1170ba2cbe9Sxc 
1180ba2cbe9Sxc 	if (timer > 0) {
1190ba2cbe9Sxc 		/*
1200ba2cbe9Sxc 		 * Set the mgt frame timeout.
1210ba2cbe9Sxc 		 */
1220ba2cbe9Sxc 		im->im_mgt_timer = timer;
1230ba2cbe9Sxc 		ieee80211_start_watchdog(ic, 1);
1240ba2cbe9Sxc 	}
1250ba2cbe9Sxc 	return ((*ic->ic_xmit)(ic, mp, IEEE80211_FC0_TYPE_MGT));
1260ba2cbe9Sxc }
1270ba2cbe9Sxc 
1280ba2cbe9Sxc /*
1290ba2cbe9Sxc  * Send a null data frame to the specified node.
1300ba2cbe9Sxc  *
1310ba2cbe9Sxc  * NB: the caller is assumed to have setup a node reference
1320ba2cbe9Sxc  *     for use; this is necessary to deal with a race condition
1330ba2cbe9Sxc  *     when probing for inactive stations.
1340ba2cbe9Sxc  */
1350ba2cbe9Sxc int
ieee80211_send_nulldata(ieee80211_node_t * in)1360ba2cbe9Sxc ieee80211_send_nulldata(ieee80211_node_t *in)
1370ba2cbe9Sxc {
1380ba2cbe9Sxc 	ieee80211com_t *ic = in->in_ic;
1390ba2cbe9Sxc 	mblk_t *m;
1400ba2cbe9Sxc 	struct ieee80211_frame *wh;
1410ba2cbe9Sxc 	uint8_t *frm;
1420ba2cbe9Sxc 
1430ba2cbe9Sxc 	m = ieee80211_getmgtframe(&frm, 0);
1440ba2cbe9Sxc 	if (m == NULL) {
1450ba2cbe9Sxc 		ic->ic_stats.is_tx_nobuf++;
1460ba2cbe9Sxc 		return (ENOMEM);
1470ba2cbe9Sxc 	}
1480ba2cbe9Sxc 
1490ba2cbe9Sxc 	wh = (struct ieee80211_frame *)m->b_rptr;
1500ba2cbe9Sxc 	ieee80211_send_setup(ic, in, wh,
151239e91abShx 	    IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA,
152239e91abShx 	    ic->ic_macaddr, in->in_macaddr, in->in_bssid);
1530ba2cbe9Sxc 	/* NB: power management bit is never sent by an AP */
1540ba2cbe9Sxc 	if ((in->in_flags & IEEE80211_NODE_PWR_MGT) &&
1550ba2cbe9Sxc 	    ic->ic_opmode != IEEE80211_M_HOSTAP)
1560ba2cbe9Sxc 		wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
1570ba2cbe9Sxc 	m->b_wptr = m->b_rptr + sizeof (struct ieee80211_frame);
1580ba2cbe9Sxc 
1590ba2cbe9Sxc 	ieee80211_dbg(IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, "net80211: "
160239e91abShx 	    "send null data frame on channel %u, pwr mgt %s\n",
161239e91abShx 	    ieee80211_macaddr_sprintf(in->in_macaddr),
162239e91abShx 	    ieee80211_chan2ieee(ic, ic->ic_curchan),
163239e91abShx 	    wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
1640ba2cbe9Sxc 
1650ba2cbe9Sxc 	(void) (*ic->ic_xmit)(ic, m, IEEE80211_FC0_TYPE_MGT);
1660ba2cbe9Sxc 
1670ba2cbe9Sxc 	return (0);
1680ba2cbe9Sxc }
1690ba2cbe9Sxc 
1700ba2cbe9Sxc /*
1710ba2cbe9Sxc  * Encapsulate an outbound data frame for GLDv3 based driver.
1720ba2cbe9Sxc  * Fill in the variable part of the 80211 frame
1730ba2cbe9Sxc  */
1740ba2cbe9Sxc /* ARGSUSED */
1750ba2cbe9Sxc mblk_t *
ieee80211_encap(ieee80211com_t * ic,mblk_t * mp,ieee80211_node_t * in)1760ba2cbe9Sxc ieee80211_encap(ieee80211com_t *ic, mblk_t *mp, ieee80211_node_t *in)
1770ba2cbe9Sxc {
1780ba2cbe9Sxc 	struct ieee80211_frame	*wh;
179a399b765Szf 	struct ieee80211_key *key;
180e2cf88acSQuaker Fang 	int addqos, ac, tid;
1810ba2cbe9Sxc 
1820ba2cbe9Sxc 	ASSERT(mp != NULL && MBLKL(mp) >= sizeof (struct ieee80211_frame));
183e2cf88acSQuaker Fang 	/*
184e2cf88acSQuaker Fang 	 * Some ap's don't handle QoS-encapsulated EAPOL
185e2cf88acSQuaker Fang 	 * frames so suppress use.  This may be an issue if other
186e2cf88acSQuaker Fang 	 * ap's require all data frames to be QoS-encapsulated
187e2cf88acSQuaker Fang 	 * once negotiated in which case we'll need to make this
188e2cf88acSQuaker Fang 	 * configurable.
189e2cf88acSQuaker Fang 	 */
190e2cf88acSQuaker Fang 	addqos = in->in_flags & (IEEE80211_NODE_QOS | IEEE80211_NODE_HT);
1910ba2cbe9Sxc 	wh = (struct ieee80211_frame *)mp->b_rptr;
1920ba2cbe9Sxc 	*(uint16_t *)wh->i_dur = 0;
193e2cf88acSQuaker Fang 	if (addqos) {
194e2cf88acSQuaker Fang 		struct ieee80211_qosframe *qwh =
195e2cf88acSQuaker Fang 		    (struct ieee80211_qosframe *)wh;
196e2cf88acSQuaker Fang 
197e2cf88acSQuaker Fang 		ac = ieee80211_classify(ic, mp, in);
198e2cf88acSQuaker Fang 		/* map from access class/queue to 11e header priorty value */
199e2cf88acSQuaker Fang 		tid = WME_AC_TO_TID(ac);
200e2cf88acSQuaker Fang 		qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
201e2cf88acSQuaker Fang 		/*
202e2cf88acSQuaker Fang 		 * Check if A-MPDU tx aggregation is setup or if we
203e2cf88acSQuaker Fang 		 * should try to enable it.  The sta must be associated
204e2cf88acSQuaker Fang 		 * with HT and A-MPDU enabled for use.  On the first
205e2cf88acSQuaker Fang 		 * frame that goes out We issue an ADDBA request and
206e2cf88acSQuaker Fang 		 * wait for a reply.  The frame being encapsulated
207e2cf88acSQuaker Fang 		 * will go out w/o using A-MPDU, or possibly it might
208e2cf88acSQuaker Fang 		 * be collected by the driver and held/retransmit.
209e2cf88acSQuaker Fang 		 * ieee80211_ampdu_request handles staggering requests
210e2cf88acSQuaker Fang 		 * in case the receiver NAK's us or we are otherwise
211e2cf88acSQuaker Fang 		 * unable to establish a BA stream.
212e2cf88acSQuaker Fang 		 */
213e2cf88acSQuaker Fang 		if ((in->in_flags & IEEE80211_NODE_AMPDU_TX) &&
214e2cf88acSQuaker Fang 		    (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
215e2cf88acSQuaker Fang 			struct ieee80211_tx_ampdu *tap = &in->in_tx_ampdu[ac];
216e2cf88acSQuaker Fang 
217e2cf88acSQuaker Fang 			if (IEEE80211_AMPDU_RUNNING(tap)) {
218e2cf88acSQuaker Fang 				/*
219e2cf88acSQuaker Fang 				 * Operational, mark frame for aggregation.
220e2cf88acSQuaker Fang 				 */
221e2cf88acSQuaker Fang 				qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
222e2cf88acSQuaker Fang 			} else if (!IEEE80211_AMPDU_REQUESTED(tap)) {
223e2cf88acSQuaker Fang 				/*
224e2cf88acSQuaker Fang 				 * Not negotiated yet, request service.
225e2cf88acSQuaker Fang 				 */
226e2cf88acSQuaker Fang 				(void) ieee80211_ampdu_request(in, tap);
227e2cf88acSQuaker Fang 			}
228e2cf88acSQuaker Fang 		}
229e2cf88acSQuaker Fang 		/* works even when BA marked above */
230e2cf88acSQuaker Fang 		if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].
231e2cf88acSQuaker Fang 		    wmep_noackPolicy) {
232e2cf88acSQuaker Fang 			qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
233e2cf88acSQuaker Fang 		}
234e2cf88acSQuaker Fang 
235e2cf88acSQuaker Fang 		*(uint16_t *)wh->i_seq =
236e2cf88acSQuaker Fang 		    LE_16(in->in_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
237e2cf88acSQuaker Fang 		in->in_txseqs[tid]++;
238e2cf88acSQuaker Fang 	} else {
239e2cf88acSQuaker Fang 		*(uint16_t *)wh->i_seq =
240e2cf88acSQuaker Fang 		    LE_16(in->in_txseqs[IEEE80211_NONQOS_TID] <<
241e2cf88acSQuaker Fang 		    IEEE80211_SEQ_SEQ_SHIFT);
242e2cf88acSQuaker Fang 		in->in_txseqs[IEEE80211_NONQOS_TID]++;
243e2cf88acSQuaker Fang 	}
2440ba2cbe9Sxc 
245a399b765Szf 	if (ic->ic_flags & IEEE80211_F_PRIVACY)
246a399b765Szf 		key = ieee80211_crypto_getkey(ic);
247a399b765Szf 	else
248a399b765Szf 		key = NULL;
249a399b765Szf 
250a399b765Szf 	/*
251a399b765Szf 	 * IEEE 802.1X: send EAPOL frames always in the clear.
252a399b765Szf 	 * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
253a399b765Szf 	 */
254a399b765Szf 	if (key != NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
255a399b765Szf 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
256e2cf88acSQuaker Fang 		if (!ieee80211_crypto_enmic(isc, key, mp, 0))
257a399b765Szf 			ieee80211_err("ieee80211_crypto_enmic failed.\n");
258a399b765Szf 	}
259a399b765Szf 
2600ba2cbe9Sxc 	return (mp);
2610ba2cbe9Sxc }
2620ba2cbe9Sxc 
2630ba2cbe9Sxc /*
2640ba2cbe9Sxc  * Add supported rates information element to a frame.
2650ba2cbe9Sxc  */
2660ba2cbe9Sxc static uint8_t *
ieee80211_add_rates(uint8_t * frm,const struct ieee80211_rateset * rs)2670ba2cbe9Sxc ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
2680ba2cbe9Sxc {
2690ba2cbe9Sxc 	uint8_t nrates;
2700ba2cbe9Sxc 
2710ba2cbe9Sxc 	*frm++ = IEEE80211_ELEMID_RATES;
2720ba2cbe9Sxc 	nrates = rs->ir_nrates;
2730ba2cbe9Sxc 	if (nrates > IEEE80211_RATE_SIZE)
2740ba2cbe9Sxc 		nrates = IEEE80211_RATE_SIZE;
2750ba2cbe9Sxc 	*frm++ = nrates;
2760ba2cbe9Sxc 	bcopy(rs->ir_rates, frm, nrates);
2770ba2cbe9Sxc 	return (frm + nrates);
2780ba2cbe9Sxc }
2790ba2cbe9Sxc 
2800ba2cbe9Sxc /*
2810ba2cbe9Sxc  * Add extended supported rates element to a frame, usually for 11g mode
2820ba2cbe9Sxc  */
2830ba2cbe9Sxc static uint8_t *
ieee80211_add_xrates(uint8_t * frm,const struct ieee80211_rateset * rs)2840ba2cbe9Sxc ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
2850ba2cbe9Sxc {
2860ba2cbe9Sxc 	if (rs->ir_nrates > IEEE80211_RATE_SIZE) {
2870ba2cbe9Sxc 		uint8_t nrates = rs->ir_nrates - IEEE80211_RATE_SIZE;
2880ba2cbe9Sxc 
2890ba2cbe9Sxc 		*frm++ = IEEE80211_ELEMID_XRATES;
2900ba2cbe9Sxc 		*frm++ = nrates;
2910ba2cbe9Sxc 		bcopy(rs->ir_rates + IEEE80211_RATE_SIZE, frm, nrates);
2920ba2cbe9Sxc 		frm += nrates;
2930ba2cbe9Sxc 	}
2940ba2cbe9Sxc 	return (frm);
2950ba2cbe9Sxc }
2960ba2cbe9Sxc 
297e2cf88acSQuaker Fang #define	WME_OUI_BYTES		0x00, 0x50, 0xf2
298e2cf88acSQuaker Fang /*
299e2cf88acSQuaker Fang  * Add a WME information element to a frame.
300e2cf88acSQuaker Fang  */
301e2cf88acSQuaker Fang /* ARGSUSED */
302e2cf88acSQuaker Fang static uint8_t *
ieee80211_add_wme_info(uint8_t * frm,struct ieee80211_wme_state * wme)303e2cf88acSQuaker Fang ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme)
304e2cf88acSQuaker Fang {
305e2cf88acSQuaker Fang 	static const struct ieee80211_wme_info info = {
306e2cf88acSQuaker Fang 		.wme_id		= IEEE80211_ELEMID_VENDOR,
307e2cf88acSQuaker Fang 		.wme_len	= sizeof (struct ieee80211_wme_info) - 2,
308e2cf88acSQuaker Fang 		.wme_oui	= { WME_OUI_BYTES },
309e2cf88acSQuaker Fang 		.wme_type	= WME_OUI_TYPE,
310e2cf88acSQuaker Fang 		.wme_subtype	= WME_INFO_OUI_SUBTYPE,
311e2cf88acSQuaker Fang 		.wme_version	= WME_VERSION,
312e2cf88acSQuaker Fang 		.wme_info	= 0,
313e2cf88acSQuaker Fang 	};
314e2cf88acSQuaker Fang 	(void) memcpy(frm, &info, sizeof (info));
315e2cf88acSQuaker Fang 	return (frm + sizeof (info));
316e2cf88acSQuaker Fang }
317e2cf88acSQuaker Fang 
318e2cf88acSQuaker Fang /*
319e2cf88acSQuaker Fang  * Add a WME parameters element to a frame.
320e2cf88acSQuaker Fang  */
321e2cf88acSQuaker Fang static uint8_t *
ieee80211_add_wme_param(uint8_t * frm,struct ieee80211_wme_state * wme)322e2cf88acSQuaker Fang ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme)
323e2cf88acSQuaker Fang {
324e2cf88acSQuaker Fang #define	SM(_v, _f)	(((_v) << _f##_S) & _f)
325e2cf88acSQuaker Fang #define	ADDSHORT(frm, v) do {			\
326e2cf88acSQuaker Fang 	_NOTE(CONSTCOND)			\
327e2cf88acSQuaker Fang 	frm[0] = (v) & 0xff;			\
328e2cf88acSQuaker Fang 	frm[1] = (v) >> 8;			\
329e2cf88acSQuaker Fang 	frm += 2;				\
330e2cf88acSQuaker Fang 	_NOTE(CONSTCOND)			\
331e2cf88acSQuaker Fang } while (0)
332e2cf88acSQuaker Fang 	/* NB: this works 'cuz a param has an info at the front */
333e2cf88acSQuaker Fang 	static const struct ieee80211_wme_info param = {
334e2cf88acSQuaker Fang 		.wme_id		= IEEE80211_ELEMID_VENDOR,
335e2cf88acSQuaker Fang 		.wme_len	= sizeof (struct ieee80211_wme_param) - 2,
336e2cf88acSQuaker Fang 		.wme_oui	= { WME_OUI_BYTES },
337e2cf88acSQuaker Fang 		.wme_type	= WME_OUI_TYPE,
338e2cf88acSQuaker Fang 		.wme_subtype	= WME_PARAM_OUI_SUBTYPE,
339e2cf88acSQuaker Fang 		.wme_version	= WME_VERSION,
340e2cf88acSQuaker Fang 	};
341e2cf88acSQuaker Fang 	int i;
342e2cf88acSQuaker Fang 
343e2cf88acSQuaker Fang 	(void) memcpy(frm, &param, sizeof (param));
344e2cf88acSQuaker Fang 	frm += offsetof(struct ieee80211_wme_info, wme_info);
345e2cf88acSQuaker Fang 	*frm++ = wme->wme_bssChanParams.cap_info;	/* AC info */
346e2cf88acSQuaker Fang 	*frm++ = 0;					/* reserved field */
347e2cf88acSQuaker Fang 	for (i = 0; i < WME_NUM_AC; i++) {
348e2cf88acSQuaker Fang 		const struct wmeParams *ac =
349e2cf88acSQuaker Fang 		    &wme->wme_bssChanParams.cap_wmeParams[i];
350e2cf88acSQuaker Fang 		*frm++ = SM(i, WME_PARAM_ACI)
351e2cf88acSQuaker Fang 		    | SM(ac->wmep_acm, WME_PARAM_ACM)
352e2cf88acSQuaker Fang 		    | SM(ac->wmep_aifsn, WME_PARAM_AIFSN);
353e2cf88acSQuaker Fang 		*frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX)
354e2cf88acSQuaker Fang 		    | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN);
355e2cf88acSQuaker Fang 		ADDSHORT(frm, ac->wmep_txopLimit);
356e2cf88acSQuaker Fang 	}
357e2cf88acSQuaker Fang 	return (frm);
358e2cf88acSQuaker Fang #undef SM
359e2cf88acSQuaker Fang #undef ADDSHORT
360e2cf88acSQuaker Fang }
361e2cf88acSQuaker Fang #undef WME_OUI_BYTES
362e2cf88acSQuaker Fang 
3630ba2cbe9Sxc /*
3640ba2cbe9Sxc  * Add SSID element to a frame
3650ba2cbe9Sxc  */
3660ba2cbe9Sxc static uint8_t *
ieee80211_add_ssid(uint8_t * frm,const uint8_t * ssid,uint32_t len)3670ba2cbe9Sxc ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, uint32_t len)
3680ba2cbe9Sxc {
3690ba2cbe9Sxc 	*frm++ = IEEE80211_ELEMID_SSID;
3700ba2cbe9Sxc 	*frm++ = (uint8_t)len;
3710ba2cbe9Sxc 	bcopy(ssid, frm, len);
3720ba2cbe9Sxc 	return (frm + len);
3730ba2cbe9Sxc }
3740ba2cbe9Sxc 
3750ba2cbe9Sxc /*
3760ba2cbe9Sxc  * Add an erp element to a frame.
3770ba2cbe9Sxc  */
3780ba2cbe9Sxc static uint8_t *
ieee80211_add_erp(uint8_t * frm,ieee80211com_t * ic)3790ba2cbe9Sxc ieee80211_add_erp(uint8_t *frm, ieee80211com_t *ic)
3800ba2cbe9Sxc {
3810ba2cbe9Sxc 	uint8_t erp;
3820ba2cbe9Sxc 
3830ba2cbe9Sxc 	*frm++ = IEEE80211_ELEMID_ERP;
3840ba2cbe9Sxc 	*frm++ = 1;
3850ba2cbe9Sxc 	erp = 0;
3860ba2cbe9Sxc 	if (ic->ic_flags & IEEE80211_F_USEPROT)
3870ba2cbe9Sxc 		erp |= IEEE80211_ERP_USE_PROTECTION;
3880ba2cbe9Sxc 	if (ic->ic_flags & IEEE80211_F_USEBARKER)
3890ba2cbe9Sxc 		erp |= IEEE80211_ERP_LONG_PREAMBLE;
3900ba2cbe9Sxc 	*frm++ = erp;
3910ba2cbe9Sxc 	return (frm);
3920ba2cbe9Sxc }
3930ba2cbe9Sxc 
3940ba2cbe9Sxc /*
3950ba2cbe9Sxc  * Get capability information from the interface softc, ic.
3960ba2cbe9Sxc  */
3970ba2cbe9Sxc static uint16_t
ieee80211_get_capinfo(ieee80211com_t * ic)3980ba2cbe9Sxc ieee80211_get_capinfo(ieee80211com_t *ic)
3990ba2cbe9Sxc {
4000ba2cbe9Sxc 	uint16_t capinfo;
4010ba2cbe9Sxc 
4020ba2cbe9Sxc 	if (ic->ic_opmode == IEEE80211_M_IBSS)
4030ba2cbe9Sxc 		capinfo = IEEE80211_CAPINFO_IBSS;
4040ba2cbe9Sxc 	else
4050ba2cbe9Sxc 		capinfo = IEEE80211_CAPINFO_ESS;
4060ba2cbe9Sxc 	if (ic->ic_flags & IEEE80211_F_PRIVACY)
4070ba2cbe9Sxc 		capinfo |= IEEE80211_CAPINFO_PRIVACY;
4080ba2cbe9Sxc 	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
4090ba2cbe9Sxc 	    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
4100ba2cbe9Sxc 		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
4110ba2cbe9Sxc 	}
4120ba2cbe9Sxc 	if (ic->ic_flags & IEEE80211_F_SHSLOT)
4130ba2cbe9Sxc 		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
4140ba2cbe9Sxc 
4150ba2cbe9Sxc 	return (capinfo);
4160ba2cbe9Sxc }
4170ba2cbe9Sxc 
4180ba2cbe9Sxc /*
4190ba2cbe9Sxc  * Send a probe request frame with the specified ssid
4200ba2cbe9Sxc  * and any optional information element data.
4210ba2cbe9Sxc  */
4220ba2cbe9Sxc int
ieee80211_send_probereq(ieee80211_node_t * in,const uint8_t * sa,const uint8_t * da,const uint8_t * bssid,const uint8_t * ssid,size_t ssidlen,const void * optie,size_t optielen)4230ba2cbe9Sxc ieee80211_send_probereq(ieee80211_node_t *in,
4240ba2cbe9Sxc     const uint8_t *sa, const uint8_t *da, const uint8_t *bssid,
4250ba2cbe9Sxc     const uint8_t *ssid, size_t ssidlen, const void *optie, size_t optielen)
4260ba2cbe9Sxc {
4270ba2cbe9Sxc 	mblk_t *mp;
4280ba2cbe9Sxc 	ieee80211com_t *ic = in->in_ic;
4290ba2cbe9Sxc 	enum ieee80211_phymode mode;
4300ba2cbe9Sxc 	struct ieee80211_frame *wh;
4310ba2cbe9Sxc 	uint8_t *frm;
4320ba2cbe9Sxc 
4330ba2cbe9Sxc 	/*
4340ba2cbe9Sxc 	 * prreq frame format ([tlv] - 1 byte element ID + 1 byte length)
4350ba2cbe9Sxc 	 *	[tlv] ssid
4360ba2cbe9Sxc 	 *	[tlv] supported rates
4370ba2cbe9Sxc 	 *	[tlv] extended supported rates
4380ba2cbe9Sxc 	 *	[tlv] user-specified ie's
4390ba2cbe9Sxc 	 */
4400ba2cbe9Sxc 	mp = ieee80211_getmgtframe(&frm,
441239e91abShx 	    2 + IEEE80211_NWID_LEN
442239e91abShx 	    + 2 + IEEE80211_RATE_SIZE +
443239e91abShx 	    + 2 + IEEE80211_XRATE_SIZE
444239e91abShx 	    + optielen);
4450ba2cbe9Sxc 	if (mp == NULL)
4460ba2cbe9Sxc 		return (ENOMEM);
4470ba2cbe9Sxc 
4480ba2cbe9Sxc 	frm = ieee80211_add_ssid(frm, ssid, ssidlen);
4490ba2cbe9Sxc 	mode = ieee80211_chan2mode(ic, ic->ic_curchan);
4500ba2cbe9Sxc 	frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
4510ba2cbe9Sxc 	frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
4520ba2cbe9Sxc 	if (optie != NULL) {
4530ba2cbe9Sxc 		(void) memcpy(frm, optie, optielen);
4540ba2cbe9Sxc 		frm += optielen;
4550ba2cbe9Sxc 	}
4560ba2cbe9Sxc 	mp->b_wptr = frm;
4570ba2cbe9Sxc 
4580ba2cbe9Sxc 	wh = (struct ieee80211_frame *)mp->b_rptr;
4590ba2cbe9Sxc 	ieee80211_send_setup(ic, in, wh,
460239e91abShx 	    IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
461239e91abShx 	    sa, da, bssid);
4620ba2cbe9Sxc 
4630ba2cbe9Sxc 	ieee80211_dbg(IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
464239e91abShx 	    "[%s] send probe req on channel %u\n",
465239e91abShx 	    ieee80211_macaddr_sprintf(wh->i_addr1),
466239e91abShx 	    ieee80211_chan2ieee(ic, ic->ic_curchan));
4670ba2cbe9Sxc 
4680ba2cbe9Sxc 	(void) (*ic->ic_xmit)(ic, mp, IEEE80211_FC0_TYPE_MGT);
4690ba2cbe9Sxc 	return (0);
4700ba2cbe9Sxc }
4710ba2cbe9Sxc 
4720ba2cbe9Sxc /*
4730ba2cbe9Sxc  * Send a management frame.  The node is for the destination (or ic_bss
4740ba2cbe9Sxc  * when in station mode).  Nodes other than ic_bss have their reference
4750ba2cbe9Sxc  * count bumped to reflect our use for an indeterminant time.
4760ba2cbe9Sxc  */
4770ba2cbe9Sxc int
ieee80211_send_mgmt(ieee80211com_t * ic,ieee80211_node_t * in,int type,int arg)4780ba2cbe9Sxc ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg)
4790ba2cbe9Sxc {
4800ba2cbe9Sxc 	mblk_t *mp;
4810ba2cbe9Sxc 	uint8_t *frm;
4820ba2cbe9Sxc 	uint16_t capinfo;
4830ba2cbe9Sxc 	struct ieee80211_key *key;
4840ba2cbe9Sxc 	boolean_t has_challenge;
4850ba2cbe9Sxc 	boolean_t is_shared_key;
4860ba2cbe9Sxc 	int ret;
4870ba2cbe9Sxc 	int timer;
4880ba2cbe9Sxc 	int status;
4890ba2cbe9Sxc 
4900ba2cbe9Sxc 	ASSERT(in != NULL);
4910ba2cbe9Sxc 
4920ba2cbe9Sxc 	timer = 0;
4930ba2cbe9Sxc 	switch (type) {
4940ba2cbe9Sxc 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
4950ba2cbe9Sxc 		/*
4960ba2cbe9Sxc 		 * probe response frame format
4970ba2cbe9Sxc 		 *	[8] time stamp
4980ba2cbe9Sxc 		 *	[2] beacon interval
4990ba2cbe9Sxc 		 *	[2] capability information
5000ba2cbe9Sxc 		 *	[tlv] ssid
5010ba2cbe9Sxc 		 *	[tlv] supported rates
5020ba2cbe9Sxc 		 *	[tlv] parameter set (FH/DS)
5030ba2cbe9Sxc 		 *	[tlv] parameter set (IBSS)
5040ba2cbe9Sxc 		 *	[tlv] extended rate phy (ERP)
5050ba2cbe9Sxc 		 *	[tlv] extended supported rates
5060ba2cbe9Sxc 		 *	[tlv] WPA
5070ba2cbe9Sxc 		 *	[tlv] WME (optional)
508e2cf88acSQuaker Fang 		 *	[tlv] HT capabilities
509e2cf88acSQuaker Fang 		 *	[tlv] HT information
510e2cf88acSQuaker Fang 		 *	[tlv] Vendor OUI HT capabilities (optional)
511e2cf88acSQuaker Fang 		 *	[tlv] Vendor OUI HT information (optional)
5120ba2cbe9Sxc 		 */
5130ba2cbe9Sxc 		mp = ieee80211_getmgtframe(&frm,
514239e91abShx 		    8			/* time stamp  */
515239e91abShx 		    + sizeof (uint16_t)	/* beacon interval  */
516239e91abShx 		    + sizeof (uint16_t)	/* capability  */
517239e91abShx 		    + 2 + IEEE80211_NWID_LEN
518239e91abShx 		    + 2 + IEEE80211_RATE_SIZE
519239e91abShx 		    + 2 + IEEE80211_FH_LEN
520239e91abShx 		    + 2 + IEEE80211_IBSS_LEN
521239e91abShx 		    + 2 + IEEE80211_ERP_LEN
522239e91abShx 		    + 2 + IEEE80211_XRATE_SIZE
523239e91abShx 		    + (ic->ic_flags & IEEE80211_F_WPA ?
524239e91abShx 		    2 * sizeof (struct ieee80211_ie_wpa) : 0)
525239e91abShx 					/* [tlv] WPA  */
526239e91abShx 		    + (ic->ic_flags & IEEE80211_F_WME ?
527e2cf88acSQuaker Fang 		    sizeof (struct ieee80211_wme_param) : 0)
528239e91abShx 					/* [tlv] WME  */
529e2cf88acSQuaker Fang 		    /* check for cluster requirement */
530e2cf88acSQuaker Fang 		    + 2 * sizeof (struct ieee80211_ie_htcap) + 4
531e2cf88acSQuaker Fang 		    + 2 * sizeof (struct ieee80211_ie_htinfo) + 4);
532e2cf88acSQuaker Fang 
5330ba2cbe9Sxc 		if (mp == NULL)
5340ba2cbe9Sxc 			return (ENOMEM);
5350ba2cbe9Sxc 
53619d332feSfei feng - Sun Microsystems - Beijing China 		bzero(frm, 8);	/* timestamp should be filled later */
5370ba2cbe9Sxc 		frm += 8;
53819d332feSfei feng - Sun Microsystems - Beijing China 		*(uint16_t *)frm = LE_16(ic->ic_bss->in_intval);
5390ba2cbe9Sxc 		frm += 2;
5400ba2cbe9Sxc 		capinfo = ieee80211_get_capinfo(ic);
5410ba2cbe9Sxc 		*(uint16_t *)frm = LE_16(capinfo);
5420ba2cbe9Sxc 		frm += 2;
5430ba2cbe9Sxc 
54419d332feSfei feng - Sun Microsystems - Beijing China 		/* ssid */
54519d332feSfei feng - Sun Microsystems - Beijing China 		frm = ieee80211_add_ssid(frm, ic->ic_bss->in_essid,
54619d332feSfei feng - Sun Microsystems - Beijing China 		    ic->ic_bss->in_esslen);
54719d332feSfei feng - Sun Microsystems - Beijing China 		/* supported rates */
5480ba2cbe9Sxc 		frm = ieee80211_add_rates(frm, &in->in_rates);
5490ba2cbe9Sxc 
55019d332feSfei feng - Sun Microsystems - Beijing China 		if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) {
5510ba2cbe9Sxc 			*frm++ = IEEE80211_ELEMID_FHPARMS;
5520ba2cbe9Sxc 			*frm++ = IEEE80211_FH_LEN;
5530ba2cbe9Sxc 			*frm++ = in->in_fhdwell & 0x00ff;
5540ba2cbe9Sxc 			*frm++ = (in->in_fhdwell >> 8) & 0x00ff;
5550ba2cbe9Sxc 			*frm++ = IEEE80211_FH_CHANSET(
556239e91abShx 			    ieee80211_chan2ieee(ic, ic->ic_curchan));
5570ba2cbe9Sxc 			*frm++ = IEEE80211_FH_CHANPAT(
558239e91abShx 			    ieee80211_chan2ieee(ic, ic->ic_curchan));
5590ba2cbe9Sxc 			*frm++ = in->in_fhindex;
5600ba2cbe9Sxc 		} else {
5610ba2cbe9Sxc 			*frm++ = IEEE80211_ELEMID_DSPARMS;
5620ba2cbe9Sxc 			*frm++ = IEEE80211_DS_LEN;
5630ba2cbe9Sxc 			*frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
5640ba2cbe9Sxc 		}
5650ba2cbe9Sxc 
5660ba2cbe9Sxc 		if (ic->ic_opmode == IEEE80211_M_IBSS) {
5670ba2cbe9Sxc 			*frm++ = IEEE80211_ELEMID_IBSSPARMS;
5680ba2cbe9Sxc 			*frm++ = IEEE80211_IBSS_LEN;
5690ba2cbe9Sxc 			*frm++ = 0; *frm++ = 0;		/* ATIM window */
5700ba2cbe9Sxc 		}
57119d332feSfei feng - Sun Microsystems - Beijing China 		if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
57219d332feSfei feng - Sun Microsystems - Beijing China 			frm = ieee80211_add_erp(frm, ic);
5730ba2cbe9Sxc 		frm = ieee80211_add_xrates(frm, &in->in_rates);
574e2cf88acSQuaker Fang 		/*
575e2cf88acSQuaker Fang 		 * NB: legacy 11b clients do not get certain ie's.
576e2cf88acSQuaker Fang 		 * The caller identifies such clients by passing
577e2cf88acSQuaker Fang 		 * a token in arg to us.  Could expand this to be
578e2cf88acSQuaker Fang 		 * any legacy client for stuff like HT ie's.
579e2cf88acSQuaker Fang 		 */
580e2cf88acSQuaker Fang 		if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
581e2cf88acSQuaker Fang 		    arg != IEEE80211_SEND_LEGACY_11B) {
582e2cf88acSQuaker Fang 			frm = ieee80211_add_htcap(frm, in);
583e2cf88acSQuaker Fang 			frm = ieee80211_add_htinfo(frm, in);
584e2cf88acSQuaker Fang 		}
585e2cf88acSQuaker Fang 		if (ic->ic_flags & IEEE80211_F_WME)
586e2cf88acSQuaker Fang 			frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
587e2cf88acSQuaker Fang 		if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
588e2cf88acSQuaker Fang 		    (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
589e2cf88acSQuaker Fang 		    arg != IEEE80211_SEND_LEGACY_11B) {
590e2cf88acSQuaker Fang 			frm = ieee80211_add_htcap_vendor(frm, in);
591e2cf88acSQuaker Fang 			frm = ieee80211_add_htinfo_vendor(frm, in);
592e2cf88acSQuaker Fang 		}
593e2cf88acSQuaker Fang 		mp->b_wptr = frm;	/* allocated is greater than used */
594e2cf88acSQuaker Fang 
5950ba2cbe9Sxc 		break;
5960ba2cbe9Sxc 
5970ba2cbe9Sxc 	case IEEE80211_FC0_SUBTYPE_AUTH:
5980ba2cbe9Sxc 		status = arg >> 16;
5990ba2cbe9Sxc 		arg &= 0xffff;
6000ba2cbe9Sxc 		has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE ||
6010ba2cbe9Sxc 		    arg == IEEE80211_AUTH_SHARED_RESPONSE) &&
6020ba2cbe9Sxc 		    in->in_challenge != NULL);
6030ba2cbe9Sxc 
6040ba2cbe9Sxc 		/*
6050ba2cbe9Sxc 		 * Deduce whether we're doing open authentication or
6060ba2cbe9Sxc 		 * shared key authentication.  We do the latter if
6070ba2cbe9Sxc 		 * we're in the middle of a shared key authentication
6080ba2cbe9Sxc 		 * handshake or if we're initiating an authentication
6090ba2cbe9Sxc 		 * request and configured to use shared key.
6100ba2cbe9Sxc 		 */
6110ba2cbe9Sxc 		is_shared_key = has_challenge ||
6120ba2cbe9Sxc 		    arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
6130ba2cbe9Sxc 		    (arg == IEEE80211_AUTH_SHARED_REQUEST &&
6140ba2cbe9Sxc 		    ic->ic_bss->in_authmode == IEEE80211_AUTH_SHARED);
6150ba2cbe9Sxc 
6160ba2cbe9Sxc 		if (has_challenge && status == IEEE80211_STATUS_SUCCESS)
6170ba2cbe9Sxc 			key = ieee80211_crypto_getkey(ic);
6180ba2cbe9Sxc 		else
6190ba2cbe9Sxc 			key = NULL;
6200ba2cbe9Sxc 
6210ba2cbe9Sxc 		mp = ieee80211_getmgtframe(&frm,
622239e91abShx 		    3 * sizeof (uint16_t)
623239e91abShx 		    + (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
624239e91abShx 		    sizeof (uint16_t) + IEEE80211_CHALLENGE_LEN : 0)
625239e91abShx 		    + (key != NULL ? key->wk_cipher->ic_header : 0));
6260ba2cbe9Sxc 		if (mp == NULL)
6270ba2cbe9Sxc 			return (ENOMEM);
6280ba2cbe9Sxc 
6290ba2cbe9Sxc 		if (key != NULL)
6300ba2cbe9Sxc 			frm += key->wk_cipher->ic_header;
6310ba2cbe9Sxc 
6320ba2cbe9Sxc 		((uint16_t *)frm)[0] =
633239e91abShx 		    (is_shared_key) ? LE_16(IEEE80211_AUTH_ALG_SHARED)
634239e91abShx 		    : LE_16(IEEE80211_AUTH_ALG_OPEN);
6350ba2cbe9Sxc 		((uint16_t *)frm)[1] = LE_16(arg);	/* sequence number */
6360ba2cbe9Sxc 		((uint16_t *)frm)[2] = LE_16(status);	/* status */
6370ba2cbe9Sxc 
6380ba2cbe9Sxc 		if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
6390ba2cbe9Sxc 			frm += IEEE80211_AUTH_ELEM_MIN;
6400ba2cbe9Sxc 			*frm = IEEE80211_ELEMID_CHALLENGE;
6410ba2cbe9Sxc 			frm++;
6420ba2cbe9Sxc 			*frm = IEEE80211_CHALLENGE_LEN;
6430ba2cbe9Sxc 			frm++;
6440ba2cbe9Sxc 			bcopy(in->in_challenge, frm, IEEE80211_CHALLENGE_LEN);
6450ba2cbe9Sxc 		}
6460ba2cbe9Sxc 
6470ba2cbe9Sxc 		if (ic->ic_opmode == IEEE80211_M_STA)
6480ba2cbe9Sxc 			timer = IEEE80211_TRANS_WAIT;
6490ba2cbe9Sxc 		break;
6500ba2cbe9Sxc 
6510ba2cbe9Sxc 	case IEEE80211_FC0_SUBTYPE_DEAUTH:
6520ba2cbe9Sxc 		mp = ieee80211_getmgtframe(&frm, sizeof (uint16_t));
6530ba2cbe9Sxc 		if (mp == NULL)
6540ba2cbe9Sxc 			return (ENOMEM);
6550ba2cbe9Sxc 
6560ba2cbe9Sxc 		*(uint16_t *)frm = LE_16(arg);	/* reason */
6570ba2cbe9Sxc 
6580ba2cbe9Sxc 		ieee80211_node_unauthorize(in);	/* port closed */
6590ba2cbe9Sxc 		break;
6600ba2cbe9Sxc 
6610ba2cbe9Sxc 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
6620ba2cbe9Sxc 	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
6630ba2cbe9Sxc 		/*
6640ba2cbe9Sxc 		 * asreq frame format
6650ba2cbe9Sxc 		 *	[2] capability information
6660ba2cbe9Sxc 		 *	[2] listen interval
6670ba2cbe9Sxc 		 *	[6*] current AP address (reassoc only)
6680ba2cbe9Sxc 		 *	[tlv] ssid
6690ba2cbe9Sxc 		 *	[tlv] supported rates
6700ba2cbe9Sxc 		 *	[tlv] extended supported rates
6710ba2cbe9Sxc 		 *	[tlv] WME
672e2cf88acSQuaker Fang 		 *	[tlv] HT capabilities
673e2cf88acSQuaker Fang 		 *	[tlv] Vendor OUI HT capabilities (optional)
6740ba2cbe9Sxc 		 *	[tlv] user-specified ie's
6750ba2cbe9Sxc 		 */
6760ba2cbe9Sxc 		mp = ieee80211_getmgtframe(&frm,
677239e91abShx 		    sizeof (uint16_t)
678239e91abShx 		    + sizeof (uint16_t) + IEEE80211_ADDR_LEN
679239e91abShx 		    + 2 + IEEE80211_NWID_LEN
680239e91abShx 		    + 2 + IEEE80211_RATE_SIZE
681239e91abShx 		    + 2 + IEEE80211_XRATE_SIZE
682e2cf88acSQuaker Fang 		    + sizeof (struct ieee80211_wme_info)
683e2cf88acSQuaker Fang 		    + 2 * sizeof (struct ieee80211_ie_htcap) + 4
684239e91abShx 		    + ic->ic_opt_ie_len);
6850ba2cbe9Sxc 		if (mp == NULL)
6860ba2cbe9Sxc 			return (ENOMEM);
6870ba2cbe9Sxc 
6880ba2cbe9Sxc 		capinfo = ieee80211_get_capinfo(ic);
6890ba2cbe9Sxc 		if (!(in->in_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) ||
6900ba2cbe9Sxc 		    !(ic->ic_caps & IEEE80211_C_SHSLOT)) {
6910ba2cbe9Sxc 			capinfo &= ~IEEE80211_CAPINFO_SHORT_SLOTTIME;
692c1500db9Szf 		} else {
693c1500db9Szf 			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
6940ba2cbe9Sxc 		}
695239e91abShx 		if (!(in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) ||
696239e91abShx 		    !(ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
697239e91abShx 			capinfo &= ~IEEE80211_CAPINFO_SHORT_PREAMBLE;
698239e91abShx 		} else {
699239e91abShx 			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
700239e91abShx 		}
7010ba2cbe9Sxc 		*(uint16_t *)frm = LE_16(capinfo);
7020ba2cbe9Sxc 		frm += 2;
7030ba2cbe9Sxc 
7040ba2cbe9Sxc 		*(uint16_t *)frm = LE_16(ic->ic_lintval);
7050ba2cbe9Sxc 		frm += 2;
7060ba2cbe9Sxc 
7070ba2cbe9Sxc 		if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
7080ba2cbe9Sxc 			IEEE80211_ADDR_COPY(frm, ic->ic_bss->in_bssid);
7090ba2cbe9Sxc 			frm += IEEE80211_ADDR_LEN;
7100ba2cbe9Sxc 		}
7110ba2cbe9Sxc 
7120ba2cbe9Sxc 		frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
7130ba2cbe9Sxc 		frm = ieee80211_add_rates(frm, &in->in_rates);
7140ba2cbe9Sxc 		frm = ieee80211_add_xrates(frm, &in->in_rates);
715e2cf88acSQuaker Fang 		if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
716e2cf88acSQuaker Fang 		    in->in_htcap_ie != NULL &&
717e2cf88acSQuaker Fang 		    in->in_htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
718e2cf88acSQuaker Fang 			frm = ieee80211_add_htcap(frm, in);
719e2cf88acSQuaker Fang 		if ((ic->ic_flags & IEEE80211_F_WME) && in->in_wme_ie != NULL)
720e2cf88acSQuaker Fang 			frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
721e2cf88acSQuaker Fang 		if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
722e2cf88acSQuaker Fang 		    in->in_htcap_ie != NULL &&
723e2cf88acSQuaker Fang 		    in->in_htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
724e2cf88acSQuaker Fang 			frm = ieee80211_add_htcap_vendor(frm, in);
7250ba2cbe9Sxc 		if (ic->ic_opt_ie != NULL) {
7260ba2cbe9Sxc 			bcopy(ic->ic_opt_ie, frm, ic->ic_opt_ie_len);
7270ba2cbe9Sxc 			frm += ic->ic_opt_ie_len;
7280ba2cbe9Sxc 		}
7290ba2cbe9Sxc 		mp->b_wptr = frm;	/* allocated is greater than used */
7300ba2cbe9Sxc 
7310ba2cbe9Sxc 		timer = IEEE80211_TRANS_WAIT;
7320ba2cbe9Sxc 		break;
7330ba2cbe9Sxc 
7340ba2cbe9Sxc 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
7350ba2cbe9Sxc 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
7360ba2cbe9Sxc 		/*
7370ba2cbe9Sxc 		 * asreq frame format
7380ba2cbe9Sxc 		 *	[2] capability information
7390ba2cbe9Sxc 		 *	[2] status
7400ba2cbe9Sxc 		 *	[2] association ID
7410ba2cbe9Sxc 		 *	[tlv] supported rates
7420ba2cbe9Sxc 		 *	[tlv] extended supported rates
7430ba2cbe9Sxc 		 *	[tlv] WME (if enabled and STA enabled)
744e2cf88acSQuaker Fang 		 *	[tlv] HT capabilities (standard or vendor OUI)
745e2cf88acSQuaker Fang 		 *	[tlv] HT information (standard or vendor OUI)
7460ba2cbe9Sxc 		 */
7470ba2cbe9Sxc 		mp = ieee80211_getmgtframe(&frm,
748239e91abShx 		    3 * sizeof (uint16_t)
749239e91abShx 		    + 2 + IEEE80211_RATE_SIZE
750239e91abShx 		    + 2 + IEEE80211_XRATE_SIZE);
7510ba2cbe9Sxc 		if (mp == NULL)
7520ba2cbe9Sxc 			return (ENOMEM);
7530ba2cbe9Sxc 
7540ba2cbe9Sxc 		capinfo = ieee80211_get_capinfo(ic);
7550ba2cbe9Sxc 		*(uint16_t *)frm = LE_16(capinfo);
7560ba2cbe9Sxc 		frm += 2;
7570ba2cbe9Sxc 
7580ba2cbe9Sxc 		*(uint16_t *)frm = LE_16(arg);	/* status */
7590ba2cbe9Sxc 		frm += 2;
7600ba2cbe9Sxc 
7610ba2cbe9Sxc 		if (arg == IEEE80211_STATUS_SUCCESS)
7620ba2cbe9Sxc 			*(uint16_t *)frm = LE_16(in->in_associd);
7630ba2cbe9Sxc 		else
7640ba2cbe9Sxc 			*(uint16_t *)frm = LE_16(0);
7650ba2cbe9Sxc 		frm += 2;
7660ba2cbe9Sxc 
7670ba2cbe9Sxc 		frm = ieee80211_add_rates(frm, &in->in_rates);
7680ba2cbe9Sxc 		frm = ieee80211_add_xrates(frm, &in->in_rates);
76919d332feSfei feng - Sun Microsystems - Beijing China 		mp->b_wptr = frm;
7700ba2cbe9Sxc 		break;
7710ba2cbe9Sxc 
7720ba2cbe9Sxc 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
7730ba2cbe9Sxc 		mp = ieee80211_getmgtframe(&frm, sizeof (uint16_t));
7740ba2cbe9Sxc 		if (mp == NULL)
7750ba2cbe9Sxc 			return (ENOMEM);
7760ba2cbe9Sxc 		*(uint16_t *)frm = LE_16(arg);	/* reason */
7770ba2cbe9Sxc 		break;
7780ba2cbe9Sxc 
7790ba2cbe9Sxc 	default:
7800ba2cbe9Sxc 		ieee80211_dbg(IEEE80211_MSG_ANY,
781239e91abShx 		    "[%s] invalid mgmt frame type %u\n",
782239e91abShx 		    ieee80211_macaddr_sprintf(in->in_macaddr), type);
7830ba2cbe9Sxc 		return (EINVAL);
7840ba2cbe9Sxc 	} /* type */
7850ba2cbe9Sxc 	ret = ieee80211_mgmt_output(ic, in, mp, type, timer);
7860ba2cbe9Sxc 	return (ret);
7870ba2cbe9Sxc }
7880ba2cbe9Sxc 
7890ba2cbe9Sxc /*
7900ba2cbe9Sxc  * Allocate a beacon frame and fillin the appropriate bits.
7910ba2cbe9Sxc  */
7920ba2cbe9Sxc mblk_t *
ieee80211_beacon_alloc(ieee80211com_t * ic,ieee80211_node_t * in,struct ieee80211_beacon_offsets * bo)7930ba2cbe9Sxc ieee80211_beacon_alloc(ieee80211com_t *ic, ieee80211_node_t *in,
7940ba2cbe9Sxc     struct ieee80211_beacon_offsets *bo)
7950ba2cbe9Sxc {
7960ba2cbe9Sxc 	struct ieee80211_frame *wh;
7970ba2cbe9Sxc 	struct ieee80211_rateset *rs;
7980ba2cbe9Sxc 	mblk_t *m;
7990ba2cbe9Sxc 	uint8_t *frm;
8000ba2cbe9Sxc 	int pktlen;
8010ba2cbe9Sxc 	uint16_t capinfo;
8020ba2cbe9Sxc 
8030ba2cbe9Sxc 	IEEE80211_LOCK(ic);
8040ba2cbe9Sxc 	/*
8050ba2cbe9Sxc 	 * beacon frame format
8060ba2cbe9Sxc 	 *	[8] time stamp
8070ba2cbe9Sxc 	 *	[2] beacon interval
8080ba2cbe9Sxc 	 *	[2] cabability information
8090ba2cbe9Sxc 	 *	[tlv] ssid
8100ba2cbe9Sxc 	 *	[tlv] supported rates
8110ba2cbe9Sxc 	 *	[3] parameter set (DS)
8120ba2cbe9Sxc 	 *	[tlv] parameter set (IBSS/TIM)
8130ba2cbe9Sxc 	 *	[tlv] extended rate phy (ERP)
8140ba2cbe9Sxc 	 *	[tlv] extended supported rates
8150ba2cbe9Sxc 	 *	[tlv] WME parameters
8160ba2cbe9Sxc 	 *	[tlv] WPA/RSN parameters
817e2cf88acSQuaker Fang 	 *	[tlv] HT capabilities
818e2cf88acSQuaker Fang 	 *	[tlv] HT information
819e2cf88acSQuaker Fang 	 *	[tlv] Vendor OUI HT capabilities (optional)
820e2cf88acSQuaker Fang 	 *	[tlv] Vendor OUI HT information (optional)
8210ba2cbe9Sxc 	 * Vendor-specific OIDs (e.g. Atheros)
8220ba2cbe9Sxc 	 * NB: we allocate the max space required for the TIM bitmap.
8230ba2cbe9Sxc 	 */
8240ba2cbe9Sxc 	rs = &in->in_rates;
825239e91abShx 	pktlen =  8			/* time stamp */
826239e91abShx 	    + sizeof (uint16_t)		/* beacon interval */
827239e91abShx 	    + sizeof (uint16_t)		/* capabilities */
828239e91abShx 	    + 2 + in->in_esslen		/* ssid */
829239e91abShx 	    + 2 + IEEE80211_RATE_SIZE	/* supported rates */
830239e91abShx 	    + 2 + 1			/* DS parameters */
831239e91abShx 	    + 2 + 4 + ic->ic_tim_len	/* DTIM/IBSSPARMS */
832239e91abShx 	    + 2 + 1			/* ERP */
833e2cf88acSQuaker Fang 	    + 2 + IEEE80211_XRATE_SIZE
834e2cf88acSQuaker Fang 	    + (ic->ic_caps & IEEE80211_C_WME ?	/* WME */
835e2cf88acSQuaker Fang 	    sizeof (struct ieee80211_wme_param) : 0)
836e2cf88acSQuaker Fang 	    /* conditional? */
837e2cf88acSQuaker Fang 	    + 4 + 2 * sizeof (struct ieee80211_ie_htcap)	/* HT caps */
838e2cf88acSQuaker Fang 	    + 4 + 2 * sizeof (struct ieee80211_ie_htinfo);	/* HT info */
839e2cf88acSQuaker Fang 
8400ba2cbe9Sxc 	m = ieee80211_getmgtframe(&frm, pktlen);
8410ba2cbe9Sxc 	if (m == NULL) {
8420ba2cbe9Sxc 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_beacon_alloc: "
843239e91abShx 		    "cannot get buf; size %u\n", pktlen);
8440ba2cbe9Sxc 		IEEE80211_UNLOCK(ic);
8450ba2cbe9Sxc 		return (NULL);
8460ba2cbe9Sxc 	}
8470ba2cbe9Sxc 
8480ba2cbe9Sxc 	/* timestamp is set by hardware/driver */
8490ba2cbe9Sxc 	(void) memset(frm, 0, 8);
8500ba2cbe9Sxc 	frm += 8;
8510ba2cbe9Sxc 	*(uint16_t *)frm = LE_16(in->in_intval);
8520ba2cbe9Sxc 	frm += 2;
8530ba2cbe9Sxc 	capinfo = ieee80211_get_capinfo(ic);
8540ba2cbe9Sxc 	bo->bo_caps = (uint16_t *)frm;
8550ba2cbe9Sxc 	*(uint16_t *)frm = LE_16(capinfo);
8560ba2cbe9Sxc 	frm += 2;
8570ba2cbe9Sxc 	*frm++ = IEEE80211_ELEMID_SSID;
8580ba2cbe9Sxc 	if (!(ic->ic_flags & IEEE80211_F_HIDESSID)) {
8590ba2cbe9Sxc 		*frm++ = in->in_esslen;
8600ba2cbe9Sxc 		bcopy(in->in_essid, frm, in->in_esslen);
8610ba2cbe9Sxc 		frm += in->in_esslen;
8620ba2cbe9Sxc 	} else {
8630ba2cbe9Sxc 		*frm++ = 0;
8640ba2cbe9Sxc 	}
8650ba2cbe9Sxc 	frm = ieee80211_add_rates(frm, rs);
8660ba2cbe9Sxc 	if (ic->ic_curmode != IEEE80211_MODE_FH) {
8670ba2cbe9Sxc 		*frm++ = IEEE80211_ELEMID_DSPARMS;
8680ba2cbe9Sxc 		*frm++ = 1;
8690ba2cbe9Sxc 		*frm++ = ieee80211_chan2ieee(ic, in->in_chan);
8700ba2cbe9Sxc 	}
8710ba2cbe9Sxc 	bo->bo_tim = frm;
8720ba2cbe9Sxc 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
8730ba2cbe9Sxc 		*frm++ = IEEE80211_ELEMID_IBSSPARMS;
8740ba2cbe9Sxc 		*frm++ = 2;
8750ba2cbe9Sxc 		*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
8760ba2cbe9Sxc 		bo->bo_tim_len = 0;
8770ba2cbe9Sxc 	} else {
8780ba2cbe9Sxc 		struct ieee80211_tim_ie *tie =
879239e91abShx 		    (struct ieee80211_tim_ie *)frm;
8800ba2cbe9Sxc 
8810ba2cbe9Sxc 		tie->tim_ie = IEEE80211_ELEMID_TIM;
8820ba2cbe9Sxc 		tie->tim_len = 4;	/* length */
8830ba2cbe9Sxc 		tie->tim_count = 0;	/* DTIM count */
8840ba2cbe9Sxc 		tie->tim_period = IEEE80211_DTIM_DEFAULT;
8850ba2cbe9Sxc 		tie->tim_bitctl = 0;	/* bitmap control */
8860ba2cbe9Sxc 		tie->tim_bitmap[0] = 0;	/* Partial Virtual Bitmap */
8870ba2cbe9Sxc 		frm += sizeof (struct ieee80211_tim_ie);
8880ba2cbe9Sxc 		bo->bo_tim_len = 1;
8890ba2cbe9Sxc 	}
8900ba2cbe9Sxc 	bo->bo_trailer = frm;
8910ba2cbe9Sxc 
8920ba2cbe9Sxc 	if (ic->ic_curmode == IEEE80211_MODE_11G) {
8930ba2cbe9Sxc 		bo->bo_erp = frm;
8940ba2cbe9Sxc 		frm = ieee80211_add_erp(frm, ic);
8950ba2cbe9Sxc 	}
896e2cf88acSQuaker Fang 	frm = ieee80211_add_xrates(frm, rs);
897e2cf88acSQuaker Fang 	if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) {
898e2cf88acSQuaker Fang 		frm = ieee80211_add_htcap(frm, in);
899e2cf88acSQuaker Fang 		bo->bo_htinfo = frm;
900e2cf88acSQuaker Fang 		frm = ieee80211_add_htinfo(frm, in);
901e2cf88acSQuaker Fang 	}
902e2cf88acSQuaker Fang 	if (ic->ic_flags & IEEE80211_F_WME) {
903e2cf88acSQuaker Fang 		bo->bo_wme = frm;
904e2cf88acSQuaker Fang 		frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
905e2cf88acSQuaker Fang 	}
906e2cf88acSQuaker Fang 	if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
907e2cf88acSQuaker Fang 	    (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
908e2cf88acSQuaker Fang 		frm = ieee80211_add_htcap_vendor(frm, in);
909e2cf88acSQuaker Fang 		frm = ieee80211_add_htinfo_vendor(frm, in);
910e2cf88acSQuaker Fang 	}
911e2cf88acSQuaker Fang 	bo->bo_trailer_len = _PTRDIFF(frm, bo->bo_trailer);
912e2cf88acSQuaker Fang 	m->b_wptr = frm;
9130ba2cbe9Sxc 
9140ba2cbe9Sxc 	wh = (struct ieee80211_frame *)m->b_rptr;
9150ba2cbe9Sxc 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
916239e91abShx 	    IEEE80211_FC0_SUBTYPE_BEACON;
9170ba2cbe9Sxc 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
9180ba2cbe9Sxc 	*(uint16_t *)wh->i_dur = 0;
9190ba2cbe9Sxc 	IEEE80211_ADDR_COPY(wh->i_addr1, wifi_bcastaddr);
9200ba2cbe9Sxc 	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr);
9210ba2cbe9Sxc 	IEEE80211_ADDR_COPY(wh->i_addr3, in->in_bssid);
9220ba2cbe9Sxc 	*(uint16_t *)wh->i_seq = 0;
9230ba2cbe9Sxc 
9240ba2cbe9Sxc 	IEEE80211_UNLOCK(ic);
9250ba2cbe9Sxc 	return (m);
9260ba2cbe9Sxc }
9270ba2cbe9Sxc 
9280ba2cbe9Sxc /*
9290ba2cbe9Sxc  * Update the dynamic parts of a beacon frame based on the current state.
9300ba2cbe9Sxc  */
9310ba2cbe9Sxc /* ARGSUSED */
9320ba2cbe9Sxc int
ieee80211_beacon_update(ieee80211com_t * ic,ieee80211_node_t * in,struct ieee80211_beacon_offsets * bo,mblk_t * mp,int mcast)9330ba2cbe9Sxc ieee80211_beacon_update(ieee80211com_t *ic, ieee80211_node_t *in,
9340ba2cbe9Sxc     struct ieee80211_beacon_offsets *bo, mblk_t *mp, int mcast)
9350ba2cbe9Sxc {
9360ba2cbe9Sxc 	uint16_t capinfo;
9370ba2cbe9Sxc 
9380ba2cbe9Sxc 	IEEE80211_LOCK(ic);
9390ba2cbe9Sxc 
9400ba2cbe9Sxc 	capinfo = ieee80211_get_capinfo(ic);
9410ba2cbe9Sxc 	*bo->bo_caps = LE_16(capinfo);
9420ba2cbe9Sxc 
9430ba2cbe9Sxc 	IEEE80211_UNLOCK(ic);
9440ba2cbe9Sxc 	return (0);
9450ba2cbe9Sxc }
946e2cf88acSQuaker Fang 
947e2cf88acSQuaker Fang /*
948e2cf88acSQuaker Fang  * Assign priority to a frame based on any vlan tag assigned
949e2cf88acSQuaker Fang  * to the station and/or any Diffserv setting in an IP header.
950e2cf88acSQuaker Fang  * Finally, if an ACM policy is setup (in station mode) it's
951e2cf88acSQuaker Fang  * applied.
952e2cf88acSQuaker Fang  */
953*034536e9SHans Rosenfeld /* ARGSUSED */
954e2cf88acSQuaker Fang int
ieee80211_classify(struct ieee80211com * ic,mblk_t * m,struct ieee80211_node * ni)955e2cf88acSQuaker Fang ieee80211_classify(struct ieee80211com *ic, mblk_t *m,
956e2cf88acSQuaker Fang     struct ieee80211_node *ni)
957e2cf88acSQuaker Fang {
958e2cf88acSQuaker Fang 	int ac;
959e2cf88acSQuaker Fang 
960e2cf88acSQuaker Fang 	if ((ni->in_flags & IEEE80211_NODE_QOS) == 0)
961e2cf88acSQuaker Fang 		return (WME_AC_BE);
962e2cf88acSQuaker Fang 
963e2cf88acSQuaker Fang 	/* Process VLan */
964e2cf88acSQuaker Fang 	/* Process IPQoS */
965e2cf88acSQuaker Fang 
966e2cf88acSQuaker Fang 	ac = WME_AC_BE;
967e2cf88acSQuaker Fang 
968e2cf88acSQuaker Fang 	/*
969e2cf88acSQuaker Fang 	 * Apply ACM policy.
970e2cf88acSQuaker Fang 	 */
971e2cf88acSQuaker Fang 	if (ic->ic_opmode == IEEE80211_M_STA) {
972e2cf88acSQuaker Fang 		static const int acmap[4] = {
973e2cf88acSQuaker Fang 			WME_AC_BK,	/* WME_AC_BE */
974e2cf88acSQuaker Fang 			WME_AC_BK,	/* WME_AC_BK */
975e2cf88acSQuaker Fang 			WME_AC_BE,	/* WME_AC_VI */
976e2cf88acSQuaker Fang 			WME_AC_VI,	/* WME_AC_VO */
977e2cf88acSQuaker Fang 		};
978e2cf88acSQuaker Fang 		while (ac != WME_AC_BK &&
979e2cf88acSQuaker Fang 		    ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].
980e2cf88acSQuaker Fang 		    wmep_acm) {
981e2cf88acSQuaker Fang 			ac = acmap[ac];
982e2cf88acSQuaker Fang 		}
983e2cf88acSQuaker Fang 	}
984e2cf88acSQuaker Fang 
985e2cf88acSQuaker Fang 	return (ac);
986e2cf88acSQuaker Fang }
987