/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002-2008 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
* 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.
*/
/*
* Process received frame
*/
#include <sys/mac_provider.h>
#include <sys/byteorder.h>
#include "net80211_impl.h"
mblk_t *, int);
/*
* Process a received frame. The node associated with the sender
* should be supplied. If nothing was found in the node table then
* the caller is assumed to supply a reference to ic_bss instead.
* The RSSI and a timestamp are also supplied. The RSSI data is used
* during AP scanning to select a AP to associate with; it can have
* any units so long as values have consistent units and higher values
* mean ``better signal''. The receive timestamp is currently not used
* by the 802.11 layer.
*/
int
{
int hdrspace;
int len;
/*
* Fastpath for A-MPDU reorder q resubmission. Frames
* w/ M_AMPDU marked have already passed through here
* but were received out of order and been held on the
* reorder queue. When resubmitted they are marked
* with the M_AMPDU flag and we can bypass most of the
* normal processing.
*/
goto resubmit_ampdu;
}
if (len < sizeof (struct ieee80211_frame_min)) {
"too short (1): len %u", len);
goto out;
}
/*
* Bit of a cheat here, we use a pointer for a 3-address
* frame format but don't reference fields past outside
* ieee80211_frame_min w/o first validating the data is
* present.
*/
goto out;
}
case IEEE80211_M_STA:
goto out_exit_mutex;
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
if (dir != IEEE80211_FC1_DIR_NODS) {
} else if (type == IEEE80211_FC0_TYPE_CTL) {
} else {
if (len < sizeof (struct ieee80211_frame)) {
"ieee80211_input: too short(2):"
"len %u\n", len);
goto out_exit_mutex;
}
}
if (type != IEEE80211_FC0_TYPE_DATA)
break;
/*
* Data frame, validate the bssid.
*/
/* not interested in */
"ieee80211_input: not to bss %s\n",
goto out_exit_mutex;
}
/*
* For adhoc mode we cons up a node when it doesn't
* exist. This should probably done after an ACL check.
*/
/*
* Fake up a node for this newly
* discovered member of the IBSS.
*/
/* NB: stat kept for alloc failure */
goto out_exit_mutex;
}
}
break;
default:
goto out_exit_mutex;
}
if (!(type & IEEE80211_FC0_TYPE_CTL)) {
if (IEEE80211_QOS_HAS_SEQ(wh)) {
i_qos[0] & IEEE80211_QOS_TID;
tid++;
} else {
}
/* duplicate, discard */
"ieee80211_input: duplicate",
"seqno <%u,%u> fragno <%u,%u> tid %u",
tid);
goto out_exit_mutex;
}
}
}
switch (type) {
case IEEE80211_FC0_TYPE_DATA:
"data too short: expecting %u", hdrspace);
goto out_exit_mutex;
}
case IEEE80211_M_STA:
if (dir != IEEE80211_FC1_DIR_FROMDS) {
"ieee80211_input: data ",
"unknown dir 0x%x", dir);
goto out_exit_mutex;
}
/*
* In IEEE802.11 network, multicast packet
* sent from me is broadcasted from AP.
* It should be silently discarded for
* SIMPLEX interface.
*/
"ieee80211_input: multicast echo\n");
goto out_exit_mutex;
}
break;
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
if (dir != IEEE80211_FC1_DIR_NODS) {
"ieee80211_input: unknown dir 0x%x",
dir);
goto out_exit_mutex;
}
break;
default:
ieee80211_err("ieee80211_input: "
"receive data, unknown opmode %u, skip\n",
goto out_exit_mutex;
}
/*
* Handle A-MPDU re-ordering. The station must be
* associated and negotiated HT. The frame must be
* a QoS frame (not QoS null data) and not previously
* processed for A-MPDU re-ordering. If the frame is
* to be processed directly then ieee80211_ampdu_reorder
* will return 0; otherwise it has consumed the mbuf
* and we should do nothing more with it.
*/
(subtype == IEEE80211_FC0_SUBTYPE_QOS)) {
goto out;
}
}
/*
* Handle privacy requirements.
*/
/*
* Discard encrypted frames when privacy off.
*/
"ieee80211_input: ""WEP PRIVACY off");
goto out_exit_mutex;
}
/* NB: stats+msgs handled in crypto_decap */
goto out_exit_mutex;
}
} else {
}
/*
* Save QoS bits for use below--before we strip the header.
*/
if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
} else {
qos = 0;
}
/*
* Next up, any fragmentation
*/
/* Fragment dropped or frame not complete yet */
goto out_exit_mutex;
}
}
/*
* Next strip any MSDU crypto bits.
*/
"data demic error\n");
goto out_exit_mutex;
}
if (qos & IEEE80211_QOS_AMSDU) {
"ieee80211_input: QOS_AMSDU (%x)\n", qos);
goto out_exit_mutex;
}
return (IEEE80211_FC0_TYPE_DATA);
case IEEE80211_FC0_TYPE_MGT:
if (dir != IEEE80211_FC1_DIR_NODS)
goto out_exit_mutex;
if (len < sizeof (struct ieee80211_frame))
goto out_exit_mutex;
if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
/*
* Only shared key auth frames with a challenge
* should be encrypted, discard all others.
*/
"ieee80211_input: "
"%s WEP set but not permitted",
goto out_exit_mutex;
}
/*
* Discard encrypted frames when privacy off.
*/
"ieee80211_input: "
"mgt WEP set but PRIVACY off");
goto out_exit_mutex;
}
/* NB: stats+msgs handled in crypto_decap */
goto out_exit_mutex;
}
}
goto out;
case IEEE80211_FC0_TYPE_CTL:
switch (subtype) {
break;
}
}
break;
default:
"bad frame type 0x%x", type);
/* should not come here */
break;
}
out:
return (type);
}
/*
* This function reassemble fragments.
* More fragments bit in the frame control means the packet is fragmented.
* While the sequence control field consists of 4-bit fragment number
* field and a 12-bit sequence number field.
*/
/* ARGSUSED */
static mblk_t *
int hdrspace)
{
/* Quick way out, if there's nothing to defragment */
return (mp);
/*
* Remove frag to insure it doesn't get reaped by timer.
*/
/*
* Should never happen. If the node is orphaned (not in
* the table) then input packets should not reach here.
* Otherwise, a concurrent request that yanks the table
* shutting the driver down. Regardless, be defensive
* here and just bail
*/
return (NULL);
}
/*
* Validate new fragment is in order and
* related to the previous ones.
*/
/*
* Sequence control field contains 12-bit sequence no
* and 4-bit fragment number. For fragemnts, the
* sequence no is not changed.
* NB: check seq # and frag together
*/
/*
* Unrelated fragment or no space for it,
* clear current fragments.
*/
}
}
if (fragno != 0) { /* !first fragment, discard */
return (NULL);
}
} else { /* concatenate */
/* track last seqnum and fragno */
}
if (more_frag != 0) { /* more to come, save */
}
return (mfrag);
}
/*
* Install received rate set information in the node's state block.
*/
int
{
/* skip 1 byte element ID and 1 byte length */
/*
* Tack on 11g extended supported rate element.
*/
"ieee80211_setup_rates: %s",
"[%s] extended rate set too large;"
" only using %u of %u rates\n",
}
}
}
/*
* Process open-system authentication response frame and start
* association if the authentication request is accepted.
*/
static void
{
return;
}
return;
}
if (status != 0) {
"open auth failed (reason %d)\n", status);
} else {
/* i_fc[0] - frame control's type & subtype field */
}
} else {
}
}
/*
* Allocate challenge text for use by shared-key authentication
* Return B_TRUE on success, B_FALST otherwise.
*/
static boolean_t
{
}
"[%s] shared key challenge alloc failed\n",
}
}
/*
* Process shared-key authentication response frames. If authentication
* succeeds, start association; otherwise, restart scan.
*/
static void
{
/*
* Pre-shared key authentication is evil; accept
* it only if explicitly configured (it is supported
* mainly for compatibility with clients like OS X).
*/
goto bad;
}
/*
* Challenge text information element
* frm[0] - element ID
* frm[1] - length
* frm[2]... - challenge text
*/
"ieee80211_auth_shared: ie %d%d too long\n",
goto bad;
}
if (*frm == IEEE80211_ELEMID_CHALLENGE)
}
switch (seq) {
"ieee80211_auth_shared: no challenge\n");
goto bad;
}
"ieee80211_auth_shared: bad challenge len %d\n",
challenge[1]);
goto bad;
}
default:
break;
}
case IEEE80211_M_STA:
return;
switch (seq) {
}
if (status != 0) {
"shared key auth failed (reason %d)\n",
status);
return;
}
break;
if (!ieee80211_alloc_challenge(in))
return;
seq + 1);
break;
default:
"shared key auth: bad seq %d", seq);
return;
}
break;
default:
"ieee80211_auth_shared: bad opmode %u\n",
break;
}
return;
bad:
/*
* Kick the state machine. This short-circuits
* using the mgt frame timeout to trigger the
* state transition.
*/
}
}
}
static int
{
uint32_t c;
}
#define LE_READ_4(p) \
((uint32_t) \
#define LE_READ_2(p) \
((uint16_t) \
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
/* ARGSUSED */
static int
const struct ieee80211_frame *wh)
{
int i;
"WME too short, len %u", len);
return (-1);
}
/* do proper check for wraparound */
return (0);
for (i = 0; i < WME_NUM_AC; i++) {
/* NB: ACI not used */
frm += 4;
}
return (1);
}
/*
* When the device is in station mode, create a node and add it
* to the node database for a new ESS or update node info if it's
* already there.
*/
static void
{
/*
* o when scanning, or
* o station mode when associated (to collect state
* updates such as 802.11g slot time), or
* o adhoc mode (to discover neighbors)
* Frames otherwise received are discarded.
*/
return;
}
/*
* [8] time stamp
* [2] beacon interval
* [2] capability information
* [tlv] ssid
* [tlv] supported rates
* [tlv] country information
* [tlv] erp information
* [tlv] extended supported rates
* [tlv] WME
* [tlv] WPA or RSN
* [tlv] HT capabilities
* [tlv] HT information
*/
IEEE80211_BEACON_ELEM_MIN, return);
frm += 8;
frm += 2;
frm += 2;
/* Agere element in beacon */
if ((*frm == IEEE80211_ELEMID_AGERE1) ||
(*frm == IEEE80211_ELEMID_AGERE2)) {
break;
}
switch (*frm) {
case IEEE80211_ELEMID_SSID:
break;
case IEEE80211_ELEMID_RATES:
break;
case IEEE80211_ELEMID_COUNTRY:
break;
case IEEE80211_ELEMID_FHPARMS:
}
break;
case IEEE80211_ELEMID_DSPARMS:
}
break;
case IEEE80211_ELEMID_TIM:
break;
break;
case IEEE80211_ELEMID_XRATES:
break;
case IEEE80211_ELEMID_ERP:
"ieee80211_recv_mgmt: ignore %s, "
"invalid ERP element; "
"length %u, expecting 1\n",
frm[1]);
break;
}
break;
case IEEE80211_ELEMID_HTCAP:
break;
case IEEE80211_ELEMID_RSN:
break;
case IEEE80211_ELEMID_HTINFO:
break;
case IEEE80211_ELEMID_VENDOR:
/*
* Accept pre-draft HT ie's if the
* standard ones have not been seen.
*/
if (ishtcapoui(frm)) {
} else if (ishtinfooui(frm)) {
}
}
break;
default:
"ieee80211_recv_mgmt: ignore %s,"
"unhandled id %u, len %u, totallen %u",
break;
}
/* frm[1] - component length */
}
"ieee80211_recv_mgmt: ignore %s ,"
"invalid channel %u\n",
return;
}
/*
* Frame was received on a channel different from the
* one indicated in the DS params element id;
* silently discard it.
*
* NB: this can happen due to signal leakage.
* But we should take it for FH phy because
* the rssi value should be correct even for
* different hop pattern in FH.
*/
"ieee80211_recv_mgmt: ignore %s ,"
"phytype %u channel %u marked for %u\n",
return;
}
"ieee80211_recv_mgmt: ignore %s ,"
"bogus beacon interval %u\n",
return;
}
/*
* Process HT ie's. This is complicated by our
* accepting both the standard ie's and the pre-draft
*/
sizeof (struct ieee80211_ie_htcap) - 2,
}
sizeof (struct ieee80211_ie_htinfo) - 2,
}
/*
* When operating in station mode, check for state updates.
* Be careful to ignore beacons received while doing a
*/
in->in_associd != 0 &&
/* record tsf of last beacon */
/* count beacon frame for s/w bmiss handling */
im->im_swbmiss_count++;
im->im_bmiss_count = 0;
"ieee80211_recv_mgmt: "
"[%s] cap change: before 0x%x, now 0x%x\n",
/*
* NB: we assume short preamble doesn't
* change dynamically
*/
}
}
/*
* Channel has been adjusted based on
* negotiated HT parameters; force the
* channel state to follow.
*/
}
}
}
rstamp);
}
return;
}
/*
* If scanning, just pass information to the scan module.
*/
return;
}
/*
* Create a new entry in the neighbor table.
*/
} else {
/*
* Copy data from beacon to neighbor table.
* Some of this information might change after
* ieee80211_add_neighbor(), so we just copy
* everything over to be safe.
*/
}
}
}
}
/*
* Perform input processing for 802.11 management frames.
* It's the default ic_recv_mgmt callback function for the interface
* softc, ic. Tipically ic_recv_mgmt is called within ieee80211_input()
*/
void
{
switch (subtype) {
break;
break;
}
/*
* 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;
}
}
}
"ieee80211_recv_mgmt: ignore %s, "
"no ssid with ssid suppression enabled",
break;
}
}
in->in_macaddr)) {
/*
* Cannot tell if the sender is operating
* in ibss mode. But we need a new node to
* send the response so blindly add them to the
* neighbor table.
*/
}
break;
}
"[%s] recv probe req\n",
/*
* Adjust and check station's rate list with device's
* supported rate. Send back response if there is at
* least one rate or the fixed rate(if being set) is
* supported by both station and the device
*/
if (rate & IEEE80211_RATE_BASIC) {
"%s recv'd rate set invalid",
} else {
}
if (allocbs) {
/*
* Temporary node created just to send a
* response, reclaim immediately.
*/
}
break;
/*
* auth frame format
* [2] algorithm
* [2] sequence
* [2] status
* [tlv*] challenge
*/
IEEE80211_AUTH_ELEM_MIN, break);
"[%s] recv auth frame with algorithm %d seq %d\n",
"ieee80211_recv_mgmt: ignore auth, %s\n",
"TKIP countermeasures enabled");
break;
}
switch (algo) {
break;
case IEEE80211_AUTH_ALG_OPEN:
break;
default:
"ignore auth, unsupported alg %d", algo);
break;
}
break;
break;
/*
* asresp frame format
* [2] capability information
* [2] status
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WME
* [tlv] HT capabilities
* [tlv] HT info
*/
IEEE80211_ASSOC_RESP_ELEM_MIN, break);
frm += 2;
frm += 2;
if (status != 0) {
"assoc failed (reason %d)\n", status);
}
break;
}
frm += 2;
/*
* Do not discard frames containing proprietary Agere
* elements 128 and 129, as the reported element length
* is often wrong. Skip rest of the frame, since we can
* not rely on the given element length making it
* impossible to know where the next element starts
*/
if ((*frm == IEEE80211_ELEMID_AGERE1) ||
(*frm == IEEE80211_ELEMID_AGERE2)) {
break;
}
switch (*frm) {
case IEEE80211_ELEMID_RATES:
break;
case IEEE80211_ELEMID_XRATES:
break;
case IEEE80211_ELEMID_HTCAP:
break;
case IEEE80211_ELEMID_HTINFO:
break;
case IEEE80211_ELEMID_VENDOR:
else if (ic->ic_flags_ext &
/*
* Accept pre-draft HT ie's if the
* standard ones have not been seen.
*/
if (ishtcapoui(frm)) {
} else if (ishtinfooui(frm)) {
}
}
break;
}
}
/*
* Adjust and check AP's rate list with device's
* supported rate. Re-start scan if no rate is or the
* fixed rate(if being set) cannot be supported by
* either AP or the device.
*/
if (rate & IEEE80211_RATE_BASIC) {
"assoc failed (rate set mismatch)\n");
return;
}
} else {
}
/*
* Setup HT state according to the negotiation.
*/
(void) ieee80211_setup_htrates(in,
/*
* Channel has been adjusted based on
* negotiated HT parameters; force the
* channel state to follow.
*/
}
}
/*
* Configure state now that we are associated.
*/
} else {
}
/*
* Honor ERP protection.
*
* NB: in_erp should zero for non-11g operation.
* check ic_curmode anyway
*/
else
"assoc success: %s preamble, %s slot time%s%s\n",
return;
break;
/*
* deauth frame format
* [2] reason
*/
"recv deauthenticate (reason %d)\n", status);
case IEEE80211_M_STA:
return;
default:
break;
}
break;
break;
/*
* disassoc frame format
* [2] reason
*/
"recv disassociate (reason %d)\n", status);
case IEEE80211_M_STA:
return;
default:
break;
}
break;
break;
/*
* action frame format:
* [1] category
* [1] action
* [tlv] parameters
*/
sizeof (struct ieee80211_action), break);
/* verify frame payloads but defer processing */
/* maybe push this to method */
switch (ia->ia_category) {
case IEEE80211_ACTION_CAT_BA:
sizeof (struct ieee80211_action_ba_addbarequest),
break);
break;
sizeof (struct ieee80211_action_ba_addbaresponse),
break);
break;
sizeof (struct ieee80211_action_ba_delba),
break);
break;
}
break;
case IEEE80211_ACTION_CAT_HT:
sizeof (struct ieee80211_action_ht_txchwidth),
break);
break;
}
break;
}
break;
default:
"subtype 0x%x not handled\n", subtype);
break;
} /* switch subtype */
out:
}