#include <sys/byteorder.h>
#include <sys/strsun.h>
#include "net80211_impl.h"
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;
*(uint16_t *)&wh->i_seq[0] =
LE_16(in->in_txseqs[IEEE80211_NONQOS_TID] <<
IEEE80211_SEQ_SEQ_SHIFT);
in->in_txseqs[IEEE80211_NONQOS_TID]++;
}
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) {
im->im_mgt_timer = timer;
ieee80211_start_watchdog(ic, 1);
}
return ((*ic->ic_xmit)(ic, mp, IEEE80211_FC0_TYPE_MGT));
}
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);
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);
}
mblk_t *
ieee80211_encap(ieee80211com_t *ic, mblk_t *mp, ieee80211_node_t *in)
{
struct ieee80211_frame *wh;
struct ieee80211_key *key;
int addqos, ac, tid;
ASSERT(mp != NULL && MBLKL(mp) >= sizeof (struct ieee80211_frame));
addqos = in->in_flags & (IEEE80211_NODE_QOS | IEEE80211_NODE_HT);
wh = (struct ieee80211_frame *)mp->b_rptr;
*(uint16_t *)wh->i_dur = 0;
if (addqos) {
struct ieee80211_qosframe *qwh =
(struct ieee80211_qosframe *)wh;
ac = ieee80211_classify(ic, mp, in);
tid = WME_AC_TO_TID(ac);
qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
if ((in->in_flags & IEEE80211_NODE_AMPDU_TX) &&
(ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
struct ieee80211_tx_ampdu *tap = &in->in_tx_ampdu[ac];
if (IEEE80211_AMPDU_RUNNING(tap)) {
qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
} else if (!IEEE80211_AMPDU_REQUESTED(tap)) {
(void) ieee80211_ampdu_request(in, tap);
}
}
if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].
wmep_noackPolicy) {
qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
}
*(uint16_t *)wh->i_seq =
LE_16(in->in_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
in->in_txseqs[tid]++;
} else {
*(uint16_t *)wh->i_seq =
LE_16(in->in_txseqs[IEEE80211_NONQOS_TID] <<
IEEE80211_SEQ_SEQ_SHIFT);
in->in_txseqs[IEEE80211_NONQOS_TID]++;
}
if (ic->ic_flags & IEEE80211_F_PRIVACY)
key = ieee80211_crypto_getkey(ic);
else
key = NULL;
if (key != NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
wh->i_fc[1] |= IEEE80211_FC1_WEP;
if (!ieee80211_crypto_enmic(isc, key, mp, 0))
ieee80211_err("ieee80211_crypto_enmic failed.\n");
}
return (mp);
}
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);
}
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);
}
#define WME_OUI_BYTES 0x00, 0x50, 0xf2
static uint8_t *
ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme)
{
static const struct ieee80211_wme_info info = {
.wme_id = IEEE80211_ELEMID_VENDOR,
.wme_len = sizeof (struct ieee80211_wme_info) - 2,
.wme_oui = { WME_OUI_BYTES },
.wme_type = WME_OUI_TYPE,
.wme_subtype = WME_INFO_OUI_SUBTYPE,
.wme_version = WME_VERSION,
.wme_info = 0,
};
(void) memcpy(frm, &info, sizeof (info));
return (frm + sizeof (info));
}
static uint8_t *
ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme)
{
#define SM(_v, _f) (((_v) << _f##_S) & _f)
#define ADDSHORT(frm, v) do { \
_NOTE(CONSTCOND) \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
_NOTE(CONSTCOND) \
} while (0)
static const struct ieee80211_wme_info param = {
.wme_id = IEEE80211_ELEMID_VENDOR,
.wme_len = sizeof (struct ieee80211_wme_param) - 2,
.wme_oui = { WME_OUI_BYTES },
.wme_type = WME_OUI_TYPE,
.wme_subtype = WME_PARAM_OUI_SUBTYPE,
.wme_version = WME_VERSION,
};
int i;
(void) memcpy(frm, ¶m, sizeof (param));
frm += offsetof(struct ieee80211_wme_info, wme_info);
*frm++ = wme->wme_bssChanParams.cap_info;
*frm++ = 0;
for (i = 0; i < WME_NUM_AC; i++) {
const struct wmeParams *ac =
&wme->wme_bssChanParams.cap_wmeParams[i];
*frm++ = SM(i, WME_PARAM_ACI)
| SM(ac->wmep_acm, WME_PARAM_ACM)
| SM(ac->wmep_aifsn, WME_PARAM_AIFSN);
*frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX)
| SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN);
ADDSHORT(frm, ac->wmep_txopLimit);
}
return (frm);
#undef SM
#undef ADDSHORT
}
#undef WME_OUI_BYTES
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);
}
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);
}
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);
}
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;
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);
}
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:
mp = ieee80211_getmgtframe(&frm,
8
+ sizeof (uint16_t)
+ sizeof (uint16_t)
+ 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)
+ (ic->ic_flags & IEEE80211_F_WME ?
sizeof (struct ieee80211_wme_param) : 0)
+ 2 * sizeof (struct ieee80211_ie_htcap) + 4
+ 2 * sizeof (struct ieee80211_ie_htinfo) + 4);
if (mp == NULL)
return (ENOMEM);
bzero(frm, 8);
frm += 8;
*(uint16_t *)frm = LE_16(ic->ic_bss->in_intval);
frm += 2;
capinfo = ieee80211_get_capinfo(ic);
*(uint16_t *)frm = LE_16(capinfo);
frm += 2;
frm = ieee80211_add_ssid(frm, ic->ic_bss->in_essid,
ic->ic_bss->in_esslen);
frm = ieee80211_add_rates(frm, &in->in_rates);
if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) {
*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;
}
if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
frm = ieee80211_add_erp(frm, ic);
frm = ieee80211_add_xrates(frm, &in->in_rates);
if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
arg != IEEE80211_SEND_LEGACY_11B) {
frm = ieee80211_add_htcap(frm, in);
frm = ieee80211_add_htinfo(frm, in);
}
if (ic->ic_flags & IEEE80211_F_WME)
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
(ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
arg != IEEE80211_SEND_LEGACY_11B) {
frm = ieee80211_add_htcap_vendor(frm, in);
frm = ieee80211_add_htinfo_vendor(frm, in);
}
mp->b_wptr = frm;
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);
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);
((uint16_t *)frm)[2] = LE_16(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);
ieee80211_node_unauthorize(in);
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
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
+ sizeof (struct ieee80211_wme_info)
+ 2 * sizeof (struct ieee80211_ie_htcap) + 4
+ 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;
} else {
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
}
if (!(in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) ||
!(ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
capinfo &= ~IEEE80211_CAPINFO_SHORT_PREAMBLE;
} else {
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
}
*(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_flags_ext & IEEE80211_FEXT_HT) &&
in->in_htcap_ie != NULL &&
in->in_htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
frm = ieee80211_add_htcap(frm, in);
if ((ic->ic_flags & IEEE80211_F_WME) && in->in_wme_ie != NULL)
frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
in->in_htcap_ie != NULL &&
in->in_htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
frm = ieee80211_add_htcap_vendor(frm, in);
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;
timer = IEEE80211_TRANS_WAIT;
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
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);
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);
mp->b_wptr = frm;
break;
case IEEE80211_FC0_SUBTYPE_DISASSOC:
mp = ieee80211_getmgtframe(&frm, sizeof (uint16_t));
if (mp == NULL)
return (ENOMEM);
*(uint16_t *)frm = LE_16(arg);
break;
default:
ieee80211_dbg(IEEE80211_MSG_ANY,
"[%s] invalid mgmt frame type %u\n",
ieee80211_macaddr_sprintf(in->in_macaddr), type);
return (EINVAL);
}
ret = ieee80211_mgmt_output(ic, in, mp, type, timer);
return (ret);
}
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;
int pktlen;
uint16_t capinfo;
IEEE80211_LOCK(ic);
rs = &in->in_rates;
pktlen = 8
+ sizeof (uint16_t)
+ sizeof (uint16_t)
+ 2 + in->in_esslen
+ 2 + IEEE80211_RATE_SIZE
+ 2 + 1
+ 2 + 4 + ic->ic_tim_len
+ 2 + 1
+ 2 + IEEE80211_XRATE_SIZE
+ (ic->ic_caps & IEEE80211_C_WME ?
sizeof (struct ieee80211_wme_param) : 0)
+ 4 + 2 * sizeof (struct ieee80211_ie_htcap)
+ 4 + 2 * sizeof (struct ieee80211_ie_htinfo);
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);
}
(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;
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;
tie->tim_count = 0;
tie->tim_period = IEEE80211_DTIM_DEFAULT;
tie->tim_bitctl = 0;
tie->tim_bitmap[0] = 0;
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);
}
frm = ieee80211_add_xrates(frm, rs);
if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) {
frm = ieee80211_add_htcap(frm, in);
bo->bo_htinfo = frm;
frm = ieee80211_add_htinfo(frm, in);
}
if (ic->ic_flags & IEEE80211_F_WME) {
bo->bo_wme = frm;
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
}
if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
(ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
frm = ieee80211_add_htcap_vendor(frm, in);
frm = ieee80211_add_htinfo_vendor(frm, in);
}
bo->bo_trailer_len = _PTRDIFF(frm, bo->bo_trailer);
m->b_wptr = frm;
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);
}
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);
}
int
ieee80211_classify(struct ieee80211com *ic, mblk_t *m,
struct ieee80211_node *ni)
{
int ac;
if ((ni->in_flags & IEEE80211_NODE_QOS) == 0)
return (WME_AC_BE);
ac = WME_AC_BE;
if (ic->ic_opmode == IEEE80211_M_STA) {
static const int acmap[4] = {
WME_AC_BK,
WME_AC_BK,
WME_AC_BE,
WME_AC_VI,
};
while (ac != WME_AC_BK &&
ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].
wmep_acm) {
ac = acmap[ac];
}
}
return (ac);
}