diff options
Diffstat (limited to 'usr/src/uts/common/io/net80211/net80211_output.c')
-rw-r--r-- | usr/src/uts/common/io/net80211/net80211_output.c | 723 |
1 files changed, 723 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/net80211/net80211_output.c b/usr/src/uts/common/io/net80211/net80211_output.c new file mode 100644 index 0000000000..ca04fb0b4e --- /dev/null +++ b/usr/src/uts/common/io/net80211/net80211_output.c @@ -0,0 +1,723 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * Send out 802.11 frames + */ + +#include <sys/byteorder.h> +#include <sys/strsun.h> +#include "net80211_impl.h" + +/* + * Set the direction field and address fields of an outgoing + * non-QoS frame. Note this should be called early on in + * constructing a frame as it sets i_fc[1]; other bits can + * then be or'd in. + */ +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) +{ + wh->i_fc[0] = (uint8_t)(IEEE80211_FC0_VERSION_0 | type); + if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + IEEE80211_ADDR_COPY(wh->i_addr1, bssid); + IEEE80211_ADDR_COPY(wh->i_addr2, sa); + IEEE80211_ADDR_COPY(wh->i_addr3, da); + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, sa); + IEEE80211_ADDR_COPY(wh->i_addr3, bssid); + break; + default: + ieee80211_err("ieee80211_send_setup: " + "Invalid mode %u\n", ic->ic_opmode); + return; + } + } else { + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, sa); + IEEE80211_ADDR_COPY(wh->i_addr3, bssid); + } + *(uint16_t *)&wh->i_dur[0] = 0; /* set duration */ + *(uint16_t *)&wh->i_seq[0] = /* set sequence number */ + LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); + in->in_txseqs[0]++; /* increase sequence number by 1 */ +} + +/* + * Send a management frame to the specified node. The node pointer + * must have a reference as the pointer will be passed to the driver + * and potentially held for a long time. If the frame is successfully + * dispatched to the driver, then it is responsible for freeing the + * reference (and potentially free'ing up any associated storage). + * + * Return 0 on success + */ +static int +ieee80211_mgmt_output(ieee80211com_t *ic, ieee80211_node_t *in, mblk_t *mp, + int type, int timer) +{ + ieee80211_impl_t *im = ic->ic_private; + struct ieee80211_frame *wh; + + ASSERT(in != NULL); + + wh = (struct ieee80211_frame *)mp->b_rptr; + ieee80211_send_setup(ic, in, wh, IEEE80211_FC0_TYPE_MGT | type, + ic->ic_macaddr, in->in_macaddr, in->in_bssid); + if (in->in_challenge != NULL) + wh->i_fc[1] |= IEEE80211_FC1_WEP; + + if (timer > 0) { + /* + * Set the mgt frame timeout. + */ + im->im_mgt_timer = timer; + ieee80211_start_watchdog(ic, 1); + } + return ((*ic->ic_xmit)(ic, mp, IEEE80211_FC0_TYPE_MGT)); +} + +/* + * Send a null data frame to the specified node. + * + * NB: the caller is assumed to have setup a node reference + * for use; this is necessary to deal with a race condition + * when probing for inactive stations. + */ +int +ieee80211_send_nulldata(ieee80211_node_t *in) +{ + ieee80211com_t *ic = in->in_ic; + mblk_t *m; + struct ieee80211_frame *wh; + uint8_t *frm; + + m = ieee80211_getmgtframe(&frm, 0); + if (m == NULL) { + ic->ic_stats.is_tx_nobuf++; + return (ENOMEM); + } + + wh = (struct ieee80211_frame *)m->b_rptr; + ieee80211_send_setup(ic, in, wh, + IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, + ic->ic_macaddr, in->in_macaddr, in->in_bssid); + /* NB: power management bit is never sent by an AP */ + if ((in->in_flags & IEEE80211_NODE_PWR_MGT) && + ic->ic_opmode != IEEE80211_M_HOSTAP) + wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; + m->b_wptr = m->b_rptr + sizeof (struct ieee80211_frame); + + ieee80211_dbg(IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, "net80211: " + "send null data frame on channel %u, pwr mgt %s\n", + ieee80211_macaddr_sprintf(in->in_macaddr), + ieee80211_chan2ieee(ic, ic->ic_curchan), + wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); + + (void) (*ic->ic_xmit)(ic, m, IEEE80211_FC0_TYPE_MGT); + + return (0); +} + +/* + * Encapsulate an outbound data frame for GLDv3 based driver. + * Fill in the variable part of the 80211 frame + */ +/* ARGSUSED */ +mblk_t * +ieee80211_encap(ieee80211com_t *ic, mblk_t *mp, ieee80211_node_t *in) +{ + struct ieee80211_frame *wh; + + ASSERT(mp != NULL && MBLKL(mp) >= sizeof (struct ieee80211_frame)); + wh = (struct ieee80211_frame *)mp->b_rptr; + *(uint16_t *)wh->i_dur = 0; + *(uint16_t *)wh->i_seq = + LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); + in->in_txseqs[0]++; + + return (mp); +} + +/* + * Add supported rates information element to a frame. + */ +static uint8_t * +ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs) +{ + uint8_t nrates; + + *frm++ = IEEE80211_ELEMID_RATES; + nrates = rs->ir_nrates; + if (nrates > IEEE80211_RATE_SIZE) + nrates = IEEE80211_RATE_SIZE; + *frm++ = nrates; + bcopy(rs->ir_rates, frm, nrates); + return (frm + nrates); +} + +/* + * Add extended supported rates element to a frame, usually for 11g mode + */ +static uint8_t * +ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) +{ + if (rs->ir_nrates > IEEE80211_RATE_SIZE) { + uint8_t nrates = rs->ir_nrates - IEEE80211_RATE_SIZE; + + *frm++ = IEEE80211_ELEMID_XRATES; + *frm++ = nrates; + bcopy(rs->ir_rates + IEEE80211_RATE_SIZE, frm, nrates); + frm += nrates; + } + return (frm); +} + +/* + * Add SSID element to a frame + */ +static uint8_t * +ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, uint32_t len) +{ + *frm++ = IEEE80211_ELEMID_SSID; + *frm++ = (uint8_t)len; + bcopy(ssid, frm, len); + return (frm + len); +} + +/* + * Add an erp element to a frame. + */ +static uint8_t * +ieee80211_add_erp(uint8_t *frm, ieee80211com_t *ic) +{ + uint8_t erp; + + *frm++ = IEEE80211_ELEMID_ERP; + *frm++ = 1; + erp = 0; + if (ic->ic_flags & IEEE80211_F_USEPROT) + erp |= IEEE80211_ERP_USE_PROTECTION; + if (ic->ic_flags & IEEE80211_F_USEBARKER) + erp |= IEEE80211_ERP_LONG_PREAMBLE; + *frm++ = erp; + return (frm); +} + +/* + * Get capability information from the interface softc, ic. + */ +static uint16_t +ieee80211_get_capinfo(ieee80211com_t *ic) +{ + uint16_t capinfo; + + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; + else + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + } + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + + return (capinfo); +} + +/* + * Send a probe request frame with the specified ssid + * and any optional information element data. + */ +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) +{ + mblk_t *mp; + ieee80211com_t *ic = in->in_ic; + enum ieee80211_phymode mode; + struct ieee80211_frame *wh; + uint8_t *frm; + + /* + * prreq frame format ([tlv] - 1 byte element ID + 1 byte length) + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] user-specified ie's + */ + mp = ieee80211_getmgtframe(&frm, + 2 + IEEE80211_NWID_LEN + + 2 + IEEE80211_RATE_SIZE + + + 2 + IEEE80211_XRATE_SIZE + + optielen); + if (mp == NULL) + return (ENOMEM); + + frm = ieee80211_add_ssid(frm, ssid, ssidlen); + mode = ieee80211_chan2mode(ic, ic->ic_curchan); + frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]); + frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]); + if (optie != NULL) { + (void) memcpy(frm, optie, optielen); + frm += optielen; + } + mp->b_wptr = frm; + + wh = (struct ieee80211_frame *)mp->b_rptr; + ieee80211_send_setup(ic, in, wh, + IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, + sa, da, bssid); + + ieee80211_dbg(IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + "[%s] send probe req on channel %u\n", + ieee80211_macaddr_sprintf(wh->i_addr1), + ieee80211_chan2ieee(ic, ic->ic_curchan)); + + (void) (*ic->ic_xmit)(ic, mp, IEEE80211_FC0_TYPE_MGT); + return (0); +} + +/* + * Send a management frame. The node is for the destination (or ic_bss + * when in station mode). Nodes other than ic_bss have their reference + * count bumped to reflect our use for an indeterminant time. + */ +int +ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg) +{ + mblk_t *mp; + uint8_t *frm; + uint16_t capinfo; + struct ieee80211_key *key; + boolean_t has_challenge; + boolean_t is_shared_key; + int ret; + int timer; + int status; + + ASSERT(in != NULL); + + timer = 0; + switch (type) { + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + /* + * probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] capability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] parameter set (FH/DS) + * [tlv] parameter set (IBSS) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] WPA + * [tlv] WME (optional) + */ + mp = ieee80211_getmgtframe(&frm, + 8 /* time stamp */ + + sizeof (uint16_t) /* beacon interval */ + + sizeof (uint16_t) /* capability */ + + 2 + IEEE80211_NWID_LEN + + 2 + IEEE80211_RATE_SIZE + + 2 + IEEE80211_FH_LEN + + 2 + IEEE80211_IBSS_LEN + + 2 + IEEE80211_ERP_LEN + + 2 + IEEE80211_XRATE_SIZE + + (ic->ic_flags & IEEE80211_F_WPA ? + 2 * sizeof (struct ieee80211_ie_wpa) : 0) + /* [tlv] WPA */ + + (ic->ic_flags & IEEE80211_F_WME ? + sizeof (struct ieee80211_wme_param) : 0)); + /* [tlv] WME */ + if (mp == NULL) + return (ENOMEM); + + bzero(frm, 8); /* timestamp is set by hardware/driver */ + frm += 8; + *(uint16_t *)frm = LE_16(in->in_intval); + frm += 2; + capinfo = ieee80211_get_capinfo(ic); + *(uint16_t *)frm = LE_16(capinfo); + frm += 2; + + frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen); + frm = ieee80211_add_rates(frm, &in->in_rates); + + if (ic->ic_phytype == IEEE80211_T_FH) { + *frm++ = IEEE80211_ELEMID_FHPARMS; + *frm++ = IEEE80211_FH_LEN; + *frm++ = in->in_fhdwell & 0x00ff; + *frm++ = (in->in_fhdwell >> 8) & 0x00ff; + *frm++ = IEEE80211_FH_CHANSET( + ieee80211_chan2ieee(ic, ic->ic_curchan)); + *frm++ = IEEE80211_FH_CHANPAT( + ieee80211_chan2ieee(ic, ic->ic_curchan)); + *frm++ = in->in_fhindex; + } else { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = IEEE80211_DS_LEN; + *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan); + } + + if (ic->ic_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = IEEE80211_IBSS_LEN; + *frm++ = 0; *frm++ = 0; /* ATIM window */ + } + frm = ieee80211_add_xrates(frm, &in->in_rates); + break; + + case IEEE80211_FC0_SUBTYPE_AUTH: + status = arg >> 16; + arg &= 0xffff; + has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || + arg == IEEE80211_AUTH_SHARED_RESPONSE) && + in->in_challenge != NULL); + + /* + * Deduce whether we're doing open authentication or + * shared key authentication. We do the latter if + * we're in the middle of a shared key authentication + * handshake or if we're initiating an authentication + * request and configured to use shared key. + */ + is_shared_key = has_challenge || + arg >= IEEE80211_AUTH_SHARED_RESPONSE || + (arg == IEEE80211_AUTH_SHARED_REQUEST && + ic->ic_bss->in_authmode == IEEE80211_AUTH_SHARED); + + if (has_challenge && status == IEEE80211_STATUS_SUCCESS) + key = ieee80211_crypto_getkey(ic); + else + key = NULL; + + mp = ieee80211_getmgtframe(&frm, + 3 * sizeof (uint16_t) + + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? + sizeof (uint16_t) + IEEE80211_CHALLENGE_LEN : 0) + + (key != NULL ? key->wk_cipher->ic_header : 0)); + if (mp == NULL) + return (ENOMEM); + + if (key != NULL) + frm += key->wk_cipher->ic_header; + + ((uint16_t *)frm)[0] = + (is_shared_key) ? LE_16(IEEE80211_AUTH_ALG_SHARED) + : LE_16(IEEE80211_AUTH_ALG_OPEN); + ((uint16_t *)frm)[1] = LE_16(arg); /* sequence number */ + ((uint16_t *)frm)[2] = LE_16(status); /* status */ + + if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { + frm += IEEE80211_AUTH_ELEM_MIN; + *frm = IEEE80211_ELEMID_CHALLENGE; + frm++; + *frm = IEEE80211_CHALLENGE_LEN; + frm++; + bcopy(in->in_challenge, frm, IEEE80211_CHALLENGE_LEN); + } + + if (ic->ic_opmode == IEEE80211_M_STA) + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_DEAUTH: + mp = ieee80211_getmgtframe(&frm, sizeof (uint16_t)); + if (mp == NULL) + return (ENOMEM); + + *(uint16_t *)frm = LE_16(arg); /* reason */ + + ieee80211_node_unauthorize(in); /* port closed */ + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + /* + * asreq frame format + * [2] capability information + * [2] listen interval + * [6*] current AP address (reassoc only) + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] WME + * [tlv] user-specified ie's + */ + mp = ieee80211_getmgtframe(&frm, + sizeof (uint16_t) + + sizeof (uint16_t) + IEEE80211_ADDR_LEN + + 2 + IEEE80211_NWID_LEN + + 2 + IEEE80211_RATE_SIZE + + 2 + IEEE80211_XRATE_SIZE + + ic->ic_opt_ie_len); + if (mp == NULL) + return (ENOMEM); + + capinfo = ieee80211_get_capinfo(ic); + if (!(in->in_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) || + !(ic->ic_caps & IEEE80211_C_SHSLOT)) { + capinfo &= ~IEEE80211_CAPINFO_SHORT_SLOTTIME; + } + *(uint16_t *)frm = LE_16(capinfo); + frm += 2; + + *(uint16_t *)frm = LE_16(ic->ic_lintval); + frm += 2; + + if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { + IEEE80211_ADDR_COPY(frm, ic->ic_bss->in_bssid); + frm += IEEE80211_ADDR_LEN; + } + + frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen); + frm = ieee80211_add_rates(frm, &in->in_rates); + frm = ieee80211_add_xrates(frm, &in->in_rates); + if (ic->ic_opt_ie != NULL) { + bcopy(ic->ic_opt_ie, frm, ic->ic_opt_ie_len); + frm += ic->ic_opt_ie_len; + } + mp->b_wptr = frm; /* allocated is greater than used */ + + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + /* + * asreq frame format + * [2] capability information + * [2] status + * [2] association ID + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] WME (if enabled and STA enabled) + */ + mp = ieee80211_getmgtframe(&frm, + 3 * sizeof (uint16_t) + + 2 + IEEE80211_RATE_SIZE + + 2 + IEEE80211_XRATE_SIZE); + if (mp == NULL) + return (ENOMEM); + + capinfo = ieee80211_get_capinfo(ic); + *(uint16_t *)frm = LE_16(capinfo); + frm += 2; + + *(uint16_t *)frm = LE_16(arg); /* status */ + frm += 2; + + if (arg == IEEE80211_STATUS_SUCCESS) + *(uint16_t *)frm = LE_16(in->in_associd); + else + *(uint16_t *)frm = LE_16(0); + frm += 2; + + frm = ieee80211_add_rates(frm, &in->in_rates); + frm = ieee80211_add_xrates(frm, &in->in_rates); + break; + + case IEEE80211_FC0_SUBTYPE_DISASSOC: + mp = ieee80211_getmgtframe(&frm, sizeof (uint16_t)); + if (mp == NULL) + return (ENOMEM); + *(uint16_t *)frm = LE_16(arg); /* reason */ + break; + + default: + ieee80211_dbg(IEEE80211_MSG_ANY, + "[%s] invalid mgmt frame type %u\n", + ieee80211_macaddr_sprintf(in->in_macaddr), type); + return (EINVAL); + } /* type */ + ret = ieee80211_mgmt_output(ic, in, mp, type, timer); + return (ret); +} + +/* + * Allocate a beacon frame and fillin the appropriate bits. + */ +mblk_t * +ieee80211_beacon_alloc(ieee80211com_t *ic, ieee80211_node_t *in, + struct ieee80211_beacon_offsets *bo) +{ + struct ieee80211_frame *wh; + struct ieee80211_rateset *rs; + mblk_t *m; + uint8_t *frm; + uint8_t *efrm; + int pktlen; + uint16_t capinfo; + + IEEE80211_LOCK(ic); + /* + * beacon frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [3] parameter set (DS) + * [tlv] parameter set (IBSS/TIM) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] WME parameters + * [tlv] WPA/RSN parameters + * Vendor-specific OIDs (e.g. Atheros) + * NB: we allocate the max space required for the TIM bitmap. + */ + rs = &in->in_rates; + pktlen = 8 /* time stamp */ + + sizeof (uint16_t) /* beacon interval */ + + sizeof (uint16_t) /* capabilities */ + + 2 + in->in_esslen /* ssid */ + + 2 + IEEE80211_RATE_SIZE /* supported rates */ + + 2 + 1 /* DS parameters */ + + 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */ + + 2 + 1 /* ERP */ + + 2 + IEEE80211_XRATE_SIZE; + m = ieee80211_getmgtframe(&frm, pktlen); + if (m == NULL) { + ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_beacon_alloc: " + "cannot get buf; size %u\n", pktlen); + IEEE80211_UNLOCK(ic); + return (NULL); + } + + /* timestamp is set by hardware/driver */ + (void) memset(frm, 0, 8); + frm += 8; + *(uint16_t *)frm = LE_16(in->in_intval); + frm += 2; + capinfo = ieee80211_get_capinfo(ic); + bo->bo_caps = (uint16_t *)frm; + *(uint16_t *)frm = LE_16(capinfo); + frm += 2; + *frm++ = IEEE80211_ELEMID_SSID; + if (!(ic->ic_flags & IEEE80211_F_HIDESSID)) { + *frm++ = in->in_esslen; + bcopy(in->in_essid, frm, in->in_esslen); + frm += in->in_esslen; + } else { + *frm++ = 0; + } + frm = ieee80211_add_rates(frm, rs); + if (ic->ic_curmode != IEEE80211_MODE_FH) { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = ieee80211_chan2ieee(ic, in->in_chan); + } + bo->bo_tim = frm; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + bo->bo_tim_len = 0; + } else { + struct ieee80211_tim_ie *tie = + (struct ieee80211_tim_ie *)frm; + + tie->tim_ie = IEEE80211_ELEMID_TIM; + tie->tim_len = 4; /* length */ + tie->tim_count = 0; /* DTIM count */ + tie->tim_period = IEEE80211_DTIM_DEFAULT; + tie->tim_bitctl = 0; /* bitmap control */ + tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */ + frm += sizeof (struct ieee80211_tim_ie); + bo->bo_tim_len = 1; + } + bo->bo_trailer = frm; + + if (ic->ic_curmode == IEEE80211_MODE_11G) { + bo->bo_erp = frm; + frm = ieee80211_add_erp(frm, ic); + } + efrm = ieee80211_add_xrates(frm, rs); + bo->bo_trailer_len = efrm - bo->bo_trailer; + m->b_wptr = efrm; + + wh = (struct ieee80211_frame *)m->b_rptr; + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_BEACON; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(uint16_t *)wh->i_dur = 0; + IEEE80211_ADDR_COPY(wh->i_addr1, wifi_bcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, in->in_bssid); + *(uint16_t *)wh->i_seq = 0; + + IEEE80211_UNLOCK(ic); + return (m); +} + +/* + * Update the dynamic parts of a beacon frame based on the current state. + */ +/* ARGSUSED */ +int +ieee80211_beacon_update(ieee80211com_t *ic, ieee80211_node_t *in, + struct ieee80211_beacon_offsets *bo, mblk_t *mp, int mcast) +{ + uint16_t capinfo; + + IEEE80211_LOCK(ic); + + capinfo = ieee80211_get_capinfo(ic); + *bo->bo_caps = LE_16(capinfo); + + IEEE80211_UNLOCK(ic); + return (0); +} |