ath_ieee80211.c revision 7a1306a70fee0e017a445bde1dcfd1997f691cf4
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Copyright (c) 2002, 2003 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,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
*
*/
/*
* IEEE 802.11 generic handler
*
* This code is derived from NetBSD code; their copyright notice follows.
*/
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Atsushi Onoe.
*
* 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
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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.
*/
#include <sys/ethernet.h>
#include <sys/byteorder.h>
#include <sys/byteorder.h>
#include <inet/wifi_ioctl.h>
#include "ath_ieee80211.h"
#include "ath_impl.h"
static const char *ieee80211_mgt_subtype_name[] = {
"assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
"probe_req", "probe_resp", "reserved#6", "reserved#7",
"beacon", "atim", "disassoc", "auth",
"deauth", "reserved#13", "reserved#14", "reserved#15"
};
extern pri_t minclsyspri;
static void
{
}
static uint8_t
{
/* no recent samples, use last known value */
}
static void
{
int i;
for (i = 0; i < IEEE80211_RECV_HIST_LEN; ++i) {
}
}
static void
{
in->in_hist_cur = 0;
}
/*
* Convert MHz frequency to IEEE channel number.
*/
{
if (freq == 2484)
return (14);
if (freq < 2484)
else
} else { /* either, guess */
if (freq == 2484)
return (14);
if (freq < 2484)
if (freq < 5000)
}
}
/*
* Convert channel to IEEE channel number.
*/
{
else if (ch == IEEE80211_CHAN_ANYC)
return (IEEE80211_CHAN_ANY);
else {
"invalid channel freq %u flags %x\n",
return (0);
}
}
/*
* Convert IEEE channel number to MHz frequency.
*/
{
if (chan == 14)
return (2484);
if (chan < 14)
else
} else { /* either, guess */
if (chan == 14)
return (2484);
}
}
static void
{
struct ieee80211_node *in1;
/* remove in from list of isc->isc_in_list */
/* remove in from list of isc->isc_inhash_list */
done = 0;
for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) {
done = 1;
break;
}
}
if (done)
break;
}
isc->isc_inact_timeout = 0;
}
/*
* This function is only running in software interrupt thread,
* and it will probably call back to LLD by isc_mgmt_send() or isc_new_state(),
* to avoid recursive mutex entry, we must make sure that
* the callers have release all LLD mutexs before calling ieee80211_input().
*/
void
{
struct ieee80211_node *in;
struct ieee80211_frame *wh;
"discard pkt with wrong version"));
goto out;
}
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
goto out_with_mutex;
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
case IEEE80211_M_HOSTAP:
if (dir == IEEE80211_FC1_DIR_NODS)
else
gld_p->gldm_broadcast_addr)) {
/* not interested in */
"ieee80211_input(): other bss %s\n",
goto out_with_mutex;
}
"ieee80211_input(): unknown src %s\n",
/*
* NB: Node allocation is handled in the
* management handling routines. Just fake
* up a reference to the hosts's node to do
* the stuff below.
*/
}
break;
default:
/* catch bad values */
break;
}
/* fragment */
/* duplicate, silently discarded */
goto out_with_mutex;
}
}
case IEEE80211_FC0_TYPE_DATA:
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
if (dir != IEEE80211_FC1_DIR_FROMDS) {
"ieee80211_input(): discard frame "
"with invalid direction %x\n", dir));
goto out_with_mutex;
}
gld_p->gldm_vendor_addr)) {
/*
* In IEEE802.11 network, multicast packet
* sent from me is broadcasted from AP.
* It should be silently discarded for
* SIMPLEX interface.
*/
"ieee80211_input(): "
"discard multicast echo\n"));
goto out_with_mutex;
}
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
if (dir != IEEE80211_FC1_DIR_NODS)
goto out_with_mutex;
break;
case IEEE80211_M_HOSTAP:
/* need more work to support HOSTAP */
break;
}
/* copy to listener after decrypt */
"ieee80211_input(): decapsulation failed\n"));
goto out;
}
return;
case IEEE80211_FC0_TYPE_MGT:
if (dir != IEEE80211_FC1_DIR_NODS)
goto out_with_mutex;
goto out_with_mutex;
/* drop uninteresting frames */
if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
goto out_with_mutex;
} else {
goto out_with_mutex;
}
goto out_with_mutex;
case IEEE80211_FC0_TYPE_CTL:
default:
break;
}
out:
}
static void
{
struct ieee80211_node *in;
}
}
/*
* Begin an active scan.
*/
static void
{
"ieee80211_begin_scan(): begin %s scan\n",
/*
* Initialize the active channel set based on the set
* of available channels and the current PHY mode.
*/
sizeof (isc->isc_chan_active));
/*
* Flush any previously seen AP's. Note that this
* assumes we don't act as both an AP and a station,
* otherwise we'll potentially flush state of stations
* associated with us.
*/
}
}
static void
{
struct ieee80211_node *in;
"ieee80211_create_ibss(): creating ibss\n"));
}
}
/*
* The difference between _ieee80211_new_state() and ieee80211_new_state()
* is the former asserts isc_genlock is already held.
* _ieee80211_new_state() is called from Multi-func thread and GLD thread,
* because ic_genlock is already owned at its entry in those 2 types of thread.
* ieee80211_new_state() is just for software interrupt thread in LLD.
*
* Because of the reason to avoid recursive mutex entry, the caller can't hold
* any other LLD mutexs before calling ieee80211_new_state().
*/
int
{
struct ieee80211_node *in;
if (isc->isc_new_state) {
if (error == EINPROGRESS)
return (0);
if (error != 0)
return (error);
}
/* state transition */
switch (nstate) {
case IEEE80211_S_INIT:
switch (ostate) {
case IEEE80211_S_INIT:
break;
case IEEE80211_S_RUN:
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
break;
default:
break;
}
case IEEE80211_S_ASSOC:
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
break;
default:
break;
}
case IEEE80211_S_AUTH:
case IEEE80211_S_SCAN:
break;
}
isc->isc_mgt_timeout = 0;
isc->isc_inact_timeout = 0;
break;
case IEEE80211_S_SCAN:
/* initialize bss for probe request */
in->in_associd = 0;
switch (ostate) {
case IEEE80211_S_INIT:
/*
* AP operation and we already have a channel;
* bypass the scan and startup immediately.
* Same applies to ad-hoc mode.
*/
} else {
}
break;
case IEEE80211_S_SCAN:
/* scan next */
}
break;
case IEEE80211_S_RUN:
/* beacon miss */
"_ieee80211_new_state(): no recent beacons"
" from %s; rescanning\n",
break;
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
/* timeout, restart scan */
}
break;
default:
break;
}
break;
case IEEE80211_S_AUTH:
switch (ostate) {
case IEEE80211_S_INIT:
"_ieee80211_new_state(): invalid transition\n"));
break;
case IEEE80211_S_SCAN:
break;
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
switch (mgt) {
break;
/* ignore and retry scan on timeout */
break;
}
break;
case IEEE80211_S_RUN:
switch (mgt) {
break;
/* try to reauth */
break;
}
break;
}
break;
case IEEE80211_S_ASSOC:
switch (ostate) {
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
case IEEE80211_S_ASSOC:
"_ieee80211_new_state(): invalid transition\n"));
break;
case IEEE80211_S_AUTH:
break;
case IEEE80211_S_RUN:
break;
}
break;
case IEEE80211_S_RUN:
switch (ostate) {
case IEEE80211_S_INIT:
case IEEE80211_S_AUTH:
case IEEE80211_S_RUN:
"_ieee80211_new_state(): invalid transition\n"));
break;
case IEEE80211_S_SCAN: /* adhoc/hostap mode */
case IEEE80211_S_ASSOC: /* infra mode */
"_ieee80211_new_state(): "
"associated with %s\n",
else
"_ieee80211_new_state(): "
"asynchronized with %s\n",
"_ieee80211_new_state(): "
"essid %s, channel %d, start %uMb\n",
isc->isc_mgt_timeout = 0;
break;
}
break;
}
return (0);
}
int
{
int result;
return (result);
}
static void
{
continue;
}
"station %s timed out due to inactivity"
}
}
int
{
struct ieee80211_frame *wh;
return (0);
}
mblk_t *
{
struct ieee80211_frame *wh;
struct ieee80211_llc *llc;
struct ether_header *eh;
/*
* Alloc a new mblk struct for the whole IEEE80211 header.
*/
ath_problem("ath: ieee80211_encap(): can't alloc mblk!\n");
return (NULL);
}
sizeof (struct ieee80211_frame);
/*
* Fill 802.11 field.
*/
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
break;
case IEEE80211_M_HOSTAP:
break;
}
/*
* IV must not duplicate during the lifetime of the key.
* But no mechanism to renew keys is defined in IEEE 802.11
* WEP. And IV may be duplicated between other stations
* because of the session key itself is shared.
* So we use pseudo random IV for now, though it is not the
* right way.
*/
/*
* (B, 255, N) with 3 <= B < 8
*/
if (3 <= B && B < 16)
}
#ifdef ATH_HOST_BIG_ENDIAN
#else
#endif /* ATH_HOST_BIG_ENDIAN */
/* Key ID and pad */
/*
* The ICV length must be included into hdrlen and pktlen.
*/
}
/*
* CRC are added by H/W, not encaped by driver,
* but we must count it in pkt length.
*/
/*
* fill LLC and SNAP fields.
*/
return (mp_header);
}
mblk_t *
{
struct ether_header *eh;
struct ieee80211_frame wh;
struct ieee80211_llc *llc;
return (NULL);
}
}
/*
* we are sure that the size of ieee80211_frame plus llc is
* larger than the size of ether_header,
* so there has enough space to encap ether_header in this mblk.
*/
case IEEE80211_FC1_DIR_NODS:
wh.ifrm_addr1);
wh.ifrm_addr2);
break;
case IEEE80211_FC1_DIR_TODS:
wh.ifrm_addr3);
wh.ifrm_addr2);
break;
case IEEE80211_FC1_DIR_FROMDS:
wh.ifrm_addr1);
wh.ifrm_addr3);
break;
case IEEE80211_FC1_DIR_DSTODS:
/* not yet supported */
"ieee80211_decap(): DS to DS\n"));
return (NULL);
}
return (mp);
}
/*
* This function doesn't need mutex protection.
*/
void
{
struct ieee80211_frame *wh;
int32_t i;
case IEEE80211_FC1_DIR_NODS:
break;
case IEEE80211_FC1_DIR_TODS:
break;
case IEEE80211_FC1_DIR_FROMDS:
break;
case IEEE80211_FC1_DIR_DSTODS:
break;
}
case IEEE80211_FC0_TYPE_DATA:
break;
case IEEE80211_FC0_TYPE_MGT:
break;
default:
break;
}
}
if (rate >= 0) {
}
if (rssi >= 0) {
}
if (len > 0) {
if ((i & 0x03) == 0)
}
buf1));
}
}
/*
* Mark the basic rates for the 11g rate table based on the
* operating mode. For real 11g we mark all the 11b rates
* and 6, 12, and 24 OFDM. For 11b compatibility we mark only
* 11b rates. There's also a pseudo 11a-mode used to mark only
* the basic OFDM rates.
*/
static void
enum ieee80211_phymode mode)
{
static const struct ieee80211_rateset basic[] = {
{ 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */
{ 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11B */
{ 7, { 2, 4, 11, 22, 12, 24, 48 } }, /* IEEE80211_MODE_11G */
{ 0 }, /* IEEE80211_MODE_TURBO */
};
int32_t i, j;
break;
}
}
}
/*
* Set the current phy mode and recalculate the active channel
* set based on the available channels for this mode. Also
* inappropriate for this mode.
*/
static int
{
0, /* IEEE80211_MODE_AUTO */
IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */
IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO */
};
struct ieee80211channel *ch;
int32_t i;
/* validate new mode */
ath_problem("ath: ieee80211_setmode(): mode %u not supported"
return (EINVAL);
}
/*
* Verify at least one channel is present in the available
* channel list before committing to the new mode.
*/
/* isc_channels size is IEEE80211_CHAN_MAX + 1, so no problem */
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
if (mode == IEEE80211_MODE_AUTO) {
/* ignore turbo channels for autoselect */
break;
} else {
break;
}
}
if (i > IEEE80211_CHAN_MAX) {
ath_problem("ath: ieee80211_setmode(): "
"no channel found for mode %u\n", mode);
return (EINVAL);
}
/*
* Calculate the active channel set.
*/
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
if (mode == IEEE80211_MODE_AUTO) {
/* take anything but pure turbo channels */
} else {
}
}
/*
* channel is wrong for the mode then pick the first
* available channel from the active list. This is likely
* not the right one.
*/
for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
break;
}
}
/*
*/
if (mode == IEEE80211_MODE_11G) {
} else {
}
/*
* Setup an initial rate set according to the
* when scanning but must exist now so drivers have
* consistent state of ic_ibss_chan.
*/
return (0);
}
/*
* If (its return value & IEEE80211_RATE_BASIC != 0),
* the rate negotiation or fix rate is failed.
*/
static int
{
uint8_t r;
error = 0;
ignore = 0;
if (flags & IEEE80211_F_DOSORT) {
/*
* Sort rates.
*/
}
}
}
badrate = r;
if (flags & IEEE80211_F_DOFRATE) {
/*
* Apply fixed rate constraint. Note that we do
* not apply the constraint to basic rates as
* otherwise we may not be able to associate if
* the rate set we submit to the AP is invalid
* (e.g. fix rate at 36Mb/s which is not a basic
* rate for 11a operation).
*/
isc->isc_fixed_rate >= 0 &&
[isc->isc_fixed_rate]))
ignore++;
}
if (flags & IEEE80211_F_DONEGO) {
/*
* Check against supported rates.
*/
break;
}
error++;
ignore++;
}
}
if (flags & IEEE80211_F_DODEL) {
/*
* Delete unacceptable rates.
*/
if (ignore) {
continue;
}
}
if (!ignore)
i++;
}
return (badrate | IEEE80211_RATE_BASIC);
else
return (IEEE80211_RV(okrate));
}
/*
* Complete a scan of potential channels.
*/
static void
{
"no scan candidate\n"));
isc->isc_des_esslen != 0) {
return;
}
return;
}
"ieee80211_end_scan(): isc_bss->in_bssid=%s",
/*
* The configuration of the access points may change
* during my scan. So delete the entry for the AP
* and retry to associate if there is another beacon.
*/
continue;
}
fail = 0;
fail |= 0x01;
if (isc->isc_des_chan !=
(struct ieee80211channel *)IEEE80211_CHAN_ANY &&
fail |= 0x01;
fail |= 0x02;
} else {
fail |= 0x02;
}
fail |= 0x04;
} else {
fail |= 0x04;
}
if (rate & IEEE80211_RATE_BASIC)
fail |= 0x08;
if (isc->isc_des_esslen != 0 &&
isc->isc_des_esslen) != 0))
fail |= 0x10;
fail |= 0x20;
if (!fail) {
else if (ieee80211_get_rssi(in) >
}
}
goto notfound;
goto notfound;
}
} else
}
/*
* Switch to the next channel marked for scanning.
* This one is only called by multi-func thread.
*/
static void
{
struct ieee80211channel *chan;
for (;;) {
/*
* Honor channels marked passive-only
* during an active scan.
*/
break;
}
return;
}
}
}
static void
{
/*
* Note we don't enable the inactive timer when acting
* as a station. Nodes created in this mode represent
* AP's identified while scanning. If we time them out
* then several things happen: we can't return the data
* to users to show the list of AP's we encountered, and
* more importantly, we'll incorrectly deauthenticate
* ourself because the inactivity timer will kick us off.
*/
}
static struct ieee80211_node *
{
return (in);
}
static struct ieee80211_node *
{
struct ieee80211_node *in;
return (in);
}
/*
* Find a node state block given the mac address. Note that
* this returns the first node found with the mac address.
*/
struct ieee80211_node *
{
struct ieee80211_node *in;
break;
}
return (in);
}
/*
* Like find but search based on the channel too.
*/
struct ieee80211_node *
struct ieee80211channel *chan)
{
struct ieee80211_node *in;
break;
}
return (in);
}
/*
* Install received rate set information in the node's state block.
* If (its return value & IEEE80211_RATE_BASIC != 0),
* the rate negotiation or fix rate is failed.
*/
static int32_t
{
/*
* Tack on 11g extended supported rate element.
*/
"ieee80211_setup_rates(): extended rate set"
" too large; only using %u of %u rates\n",
}
}
}
/*
* Misc management frame encapsulation functions.
*/
static uint8_t *
{
*frm++ = IEEE80211_ELEMID_RATES;
if (nrates > IEEE80211_RATE_SIZE)
}
static uint8_t *
{
/*
* Add an extended supported rates element if operating in 11g mode.
*/
*frm++ = IEEE80211_ELEMID_XRATES;
}
return (frm);
}
static uint8_t *
{
*frm++ = IEEE80211_ELEMID_SSID;
}
/*
* Following functions are responsible for management frame encapsulation.
*/
static int32_t
{
enum ieee80211_phymode mode;
/*
* prreq frame format
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
*/
pktlen = sizeof (struct ieee80211_frame) +
2 + IEEE80211_RATE_SIZE +
ath_problem("ath: ieee80211_send_prreq(): no space\n");
return (ENOMEM);
}
return (ret);
}
static int32_t
{
/*
* probe response frame format
* [8] time stamp
* [2] beacon interval
* [2] cabability information
* [tlv] ssid
* [tlv] supported rates
* [tlv] parameter set (IBSS)
* [tlv] extended supported rates
*/
pktlen = sizeof (struct ieee80211_frame) +
8 + 2 + 2 + 2 +
2 + IEEE80211_RATE_SIZE +
6 +
ath_problem("ath: ieee80211_send_prresp(): alloc failed.\n");
return (ENOMEM);
}
frm += 8;
frm += 2;
else
frm += 2;
*frm++ = 2;
} else { /* IEEE80211_M_HOSTAP */
/* TIM */
*frm++ = IEEE80211_ELEMID_TIM;
*frm++ = 0; /* DTIM count */
*frm++ = 0; /* bitmap control */
*frm++ = 0; /* Partial Virtual Bitmap (variable length) */
}
}
static int32_t
{
ath_problem("ath: ieee80211_send_auth(): allocb failed\n");
return (ENOMEM);
}
/* shared key auth */
return (ret);
}
static int32_t
{
"ieee80211_send_deauth(): station %s deauthenticate",
ath_problem("ath: ieee80211_send_deauth(): allocb failed\n");
return (ENOMEM);
}
}
static int32_t
{
/*
* asreq frame format
* [2] capability information
* [2] listen interval
* [6*] current AP address (reassoc only)
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
*/
pktlen = sizeof (struct ieee80211_frame) +
ath_problem("ath: ieee80211_send_asreq: allocb failed\n");
return (ENOMEM);
}
capinfo = 0;
else /* IEEE80211_M_STA */
capinfo |= 0x0020;
frm += 2;
frm += 2;
if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
}
return (ret);
}
static int32_t
{
/*
* asreq frame format
* [2] capability information
* [2] status
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
*/
pktlen = sizeof (struct ieee80211_frame) +
sizeof (capinfo) +
sizeof (uint16_t) +
sizeof (uint16_t) +
2 + IEEE80211_RATE_SIZE +
ath_problem("ath: ieee80211_send_asresp: allocb failed\n");
return (ENOMEM);
}
frm += 2;
frm += 2;
else
frm += 2;
} else {
}
}
static int32_t
{
"ieee80211_send_disassoc(): station %s disassociate",
ath_problem("ath: ieee80211_send_asresp: allocb failed\n");
return (ENOMEM);
}
}
/*
* This handles both beacon and probe response frames.
*/
static void
{
struct ieee80211_frame *wh;
struct ieee80211_node *in;
return;
}
/*
* beacon frame format
* [8] time stamp
* [2] beacon interval
* [2] cabability information
* [tlv] ssid
* [tlv] supported rates
* [tlv] country information
* [tlv] erp information
* [tlv] extended supported rates
*/
frm += 8;
frm += 2;
frm += 2;
fhdwell = 0;
fhindex = 0;
erp = 0;
switch (*frm) {
case IEEE80211_ELEMID_SSID:
break;
case IEEE80211_ELEMID_RATES:
break;
case IEEE80211_ELEMID_COUNTRY:
/*
* don't care 'country', otherwise,
* just do:
* country = frm;
*/
break;
case IEEE80211_ELEMID_FHPARMS:
}
break;
case IEEE80211_ELEMID_DSPARMS:
/*
* hack this since depending on phytype
* is problematic for multi-mode devices.
*/
break;
case IEEE80211_ELEMID_TIM:
break;
case IEEE80211_ELEMID_XRATES:
break;
case IEEE80211_ELEMID_ERP:
"ieee80211_recv_beacon(): "
"%s: invalid ERP element; "
"length %u, expecting 1\n",
break;
}
break;
default:
break;
}
}
"ignore %s with invalid channel %u\n",
return;
}
/*
* Frame was received on a channel different from the
* discard it.
*
* NB: this can happen due to signal leakage.
*/
"ignore %s phytype %u on channel %u marked for %u\n",
return;
}
/*
* Use mac and channel for lookup so we collect all
* potential AP's when scanning. Otherwise we may
* see the same AP on multiple channels and will only
* record the last one. We could filter APs here based
* on rssi, etc. but leave that to the end of the scan
* so we can keep the selection criteria in one spot.
* This may result in a bloat of the scanned AP list but
* it shouldn't be too much.
*/
"ieee80211_recv_beacon(): essid = %s\n",
"ieee80211_recv_beacon(): to %s\n",
"caps 0x%x bintval %u erp 0x%x\n",
}
return;
/*
* Update ESSID at probe response to adopt hidden AP by
*/
}
/* in_chan must have been setup */
}
static void
{
struct ieee80211_frame *wh;
struct ieee80211_node *in;
return;
return;
/*
* prreq frame format
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
*/
switch (*frm) {
case IEEE80211_ELEMID_SSID:
break;
case IEEE80211_ELEMID_RATES:
break;
case IEEE80211_ELEMID_XRATES:
break;
}
}
if (ssid[1] != 0 &&
"ssid unmatch %s from %s",
return;
}
ath_problem("ath: ieee80211_recv_prreq(): "
"dup bss failed\n");
return;
}
"ieee80211_recv_prreq: new req from %s\n",
allocbs = 1;
} else
allocbs = 0;
if (rate & IEEE80211_RATE_BASIC) {
"rate negotiation fail: %s\n",
} else {
0);
}
else
}
static void
{
struct ieee80211_frame *wh;
struct ieee80211_node *in;
/*
* auth frame format
* [2] algorithm
* [2] sequence
* [2] status
* [tlv*] challenge
*/
"ieee80211_recv_auth: too short from %s\n",
return;
}
if (algo != IEEE80211_AUTH_ALG_OPEN) {
/* shared key auth */
ath_problem("ath: ieee80211_recv_auth(): "
"unsupported auth %d from %s\n",
return;
}
switch (isc->isc_opmode) {
case IEEE80211_M_IBSS:
return;
break;
case IEEE80211_M_AHDEMO:
break;
case IEEE80211_M_HOSTAP:
break;
case IEEE80211_M_STA:
return;
if (status != 0) {
ath_log("ath: ieee80211_recv_auth(): "
"authentication failed (reason %d) for %s\n",
return;
}
break;
}
}
static void
{
/* doesn't support HOST-AP mode yet */
}
static void
{
struct ieee80211_frame *wh;
struct ieee80211_node *in;
return;
/*
* asresp frame format
* [2] capability information
* [2] status
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
*/
ath_log("ath: ieee80211_recv_asresp(): too short from %s\n",
return;
}
frm += 2;
frm += 2;
if (status != 0) {
"association failed (reason %d)\n", status));
}
return;
}
frm += 2;
switch (*frm) {
case IEEE80211_ELEMID_RATES:
break;
case IEEE80211_ELEMID_XRATES:
break;
}
}
}
static void
{
struct ieee80211_frame *wh;
/*
* disassoc frame format
* [2] reason
*/
ath_log("ath: ieee80211_recv_disassoc(): too short from %s\n",
return;
}
"disassociation packet, reason:0x%x\n", reason));
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
break;
case IEEE80211_M_HOSTAP:
/* don't support HOSTAP */
break;
default:
break;
}
}
static void
{
struct ieee80211_frame *wh;
/*
* dauth frame format
* [2] reason
*/
ath_log("ath: ieee80211_recv_deauth(): too short from %s\n",
return;
}
"deauthentication packet, reason: 0x%x\n", reason));
switch (isc->isc_opmode) {
case IEEE80211_M_STA:
break;
case IEEE80211_M_HOSTAP:
break;
default:
break;
}
}
/*
* Return the phy mode for with the specified channel so the
* caller can select a rate set. This is problematic and the
* work here assumes how things work elsewhere in this code.
*/
enum ieee80211_phymode
{
/*
* NB: this assumes the channel would not be supplied to us
* unless it was already compatible with the current mode.
*/
return (isc->isc_curmode);
/*
* In autoselect mode; deduce a mode based on the channel
* characteristics. We assume that turbo-only channels
* are not considered when the channel set is constructed.
*/
if (IEEE80211_IS_CHAN_5GHZ(chan))
return (IEEE80211_MODE_11A);
return (IEEE80211_MODE_11G);
else
return (IEEE80211_MODE_11B);
}
/*
* Format an Ethernet MAC for printing,
* and this function adds NULL byte at the end of string.
*/
const char *
{
static char etherbuf[18];
"%02x:%02x:%02x:%02x:%02x:%02x",
return (etherbuf);
}
/*
* Format an essid for printing,
* and this function adds NULL byte at the end of string.
*/
const int8_t *
{
uint32_t i;
uint8_t *p;
if (len > IEEE80211_NWID_LEN)
/* determine printable or not */
if (*p < ' ' || *p > 0x7e)
break;
}
if (i == len) {
for (i = 0; i < len; i++)
essidbuf[i] = '\0';
} else {
for (i = 0; i < len; i++) {
}
}
return (essidbuf);
}
/*
* Following fucntions are registerd to GLD and intercepting
* the function calls from GLD to LLD to add appropriate ic_genlock protection.
*
* We have to protect ieee80211_gld_send() by isc_genlock,
* because there have many references to isc struct on this transimit path,
* and this may affect performace.
*/
static int32_t
{
return (result);
}
static int32_t
{
return (result);
}
/*
* Following function is for Multi_func thread.
* The smallest timing unit is 100ms.
* It is created in ieee80211_gld_start(),
* and destroy in ieee80211_gld_stop().
*/
void
{
struct ieee80211_node *in;
uint32_t scan_ticks = 0;
uint32_t ratectl_ticks = 0;
uint32_t cali_ticks = 0;
while (isc->isc_mfthread_switch) {
scan_ticks = 0;
scan_ticks = 0;
} else
scan_ticks++;
}
else {
}
}
ratectl_ticks = 0;
} else
cali_ticks = 0;
} else
cali_ticks++;
if (isc->isc_mgt_timeout &&
--isc->isc_mgt_timeout == 0) {
(void) _ieee80211_new_state(isc,
IEEE80211_S_SCAN, -1);
}
if (isc->isc_inact_timeout &&
--isc->isc_inact_timeout == 0)
mgt_ticks = 0;
} else
mgt_ticks++;
}
thread_exit();
}
/*
* This function is responsible for creating multi-func thread.
*/
static int32_t
{
return (result);
}
/*
* This function is responsible for destory multi-func thread.
*/
static int32_t
{
isc->isc_mfthread_switch = 0;
break;
}
return (result);
}
static int32_t
{
return (result);
}
static int
{
int result;
return (result);
}
static int32_t
{
return (result);
}
static int32_t
{
return (result);
}
static int
{
int result;
return (result);
}
static uint32_t
{
}
{
struct ieee80211channel *ch;
int32_t i;
/*
* Fill in 802.11 available channel set, mark
* all available channels as active, and pick
* a default channel if not already specified.
*/
for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
/*
* Identify mode capabilities.
*/
if (IEEE80211_IS_CHAN_A(ch))
if (IEEE80211_IS_CHAN_B(ch))
if (IEEE80211_IS_CHAN_PUREG(ch))
if (IEEE80211_IS_CHAN_T(ch))
}
}
/* Start from auto mode */
/* Initialize WEP related variable */
isc->isc_wep_txkey = 0;
/* Initialize some config variables */
if (isc->isc_lintval == 0)
sizeof (struct ieee80211_node),
for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++)
sizeof (struct ieee80211_node),
/* Initialize management frame handlers */
isc->isc_mfthread_switch = 0;
return (0);
}
void
{
}