/*
* 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.
*/
/*
* Node management routines
*/
#include "net80211_impl.h"
static void ieee80211_node_cleanup(ieee80211_node_t *);
static void ieee80211_node_free(ieee80211_node_t *);
ieee80211_node_t *, const uint8_t *);
static void ieee80211_node_reclaim(ieee80211_node_table_t *,
ieee80211_node_t *);
static void ieee80211_free_node_locked(ieee80211_node_t *);
static void ieee80211_free_allnodes(ieee80211_node_table_t *);
static void ieee80211_timeout_scan_candidates(ieee80211_node_table_t *);
static void ieee80211_timeout_stations(ieee80211_node_table_t *);
static void ieee80211_node_table_init(ieee80211com_t *,
ieee80211_node_table_t *, const char *, int, int,
void (*timeout)(ieee80211_node_table_t *));
static void ieee80211_node_table_cleanup(ieee80211_node_table_t *);
/*
* association failures before ignored
* The failure may be caused by the response frame is lost for
* environmental reason. So Try associate more than once before
* ignore the node
*/
/*
* Initialize node database management callbacks for the interface.
* This function is called by ieee80211_attach(). These callback
* functions may be overridden in special circumstances, as long as
* as this is done after calling ieee80211_attach() and prior to any
* other call which may allocate a node
*/
void
{
/* default station inactivity timer setings */
}
/*
* Initialize node databases and the ic_bss node element.
*/
void
{
/*
* Calculate ic_tim_bitmap size in bytes
* IEEE80211_AID_MAX defines maximum bits in ic_tim_bitmap
*/
}
/*
* Destroy all node databases and is usually called during device detach
*/
void
{
/* Node Detach */
}
}
/*
* Increase a node's reference count
*
* Return pointer to the node
*/
{
return (in);
}
/*
* Dexrease a node's reference count
*/
void
{
}
/*
* Mark ports authorized for data traffic. This function is usually
* used by 802.1x authenticator.
*/
void
{
}
/*
* Mark ports unauthorized for data traffic. This function is usually
* used by 802.1x authenticator.
*/
void
{
}
/*
* to insure a consistent view by drivers.
*/
static void
struct ieee80211_channel *chan)
{
if (chan == IEEE80211_CHAN_ANYC)
if (IEEE80211_IS_CHAN_HT(chan)) {
/*
* Gotta be careful here; the rate set returned by
* ieee80211_get_suprates is actually any HT rate
* set so blindly copying it will be bad. We must
* install the legacy rate est in ni_rates and the
* HT rate set in ni_htrates.
*/
}
/* in->in_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; */
}
/*
* Initialize the channel set to scan based on the available channels
* and the current PHY mode.
*/
static void
{
} else {
sizeof (ic->ic_chan_active));
}
}
/*
* Begin an active scan. Initialize the node cache. The scan
* begins on the next radio channel by calling ieee80211_next_scan().
* The actual scanning is not automated. The driver itself
* only handles setting the radio frequency and stepping through
* the channels.
*/
void
{
"begin %s scan in %s mode on channel %u\n",
/*
* Clear scan state and flush any previously seen AP's.
*/
if (reset)
/* Scan the next channel. */
}
/*
* Switch to the next channel marked for scanning.
* A driver is expected to first call ieee80211_begin_scan(),
* to initialize the node cache, then set the radio channel
* on the device. And then after a certain time has elapsed,
* call ieee80211_next_scan() to move to the next channel.
* Typically, a timeout routine is used to automate this process.
*/
void
{
/*
* Insure any previous mgt frame timeouts don't fire.
* This assumes the driver does the right thing in
* flushing anything queued in the driver and below.
*/
im->im_mgt_timer = 0;
do {
"ieee80211_next_scan: chan %d->%d\n",
/*
* drivers should do this as needed,
* for now maintain compatibility
*/
return;
}
}
/*
* Copy useful state from node obss into nbss.
*/
static void
{
/* propagate useful state */
}
/*
* Setup the net80211 specific portion of an interface's softc, ic,
* for use in IBSS mode
*/
void
{
"creating ibss\n");
/*
* mode we make the initial inactivity timer longer since
* we create nodes only through discovery and they typically
* are long-lived associations.
*/
ieee80211_err("ieee80211_create_ibss(): alloc node failed\n");
return;
}
}
case IEEE80211_M_IBSS:
else
break;
case IEEE80211_M_AHDEMO:
else
break;
default:
ieee80211_err("ieee80211_create_ibss(): "
"wrong opmode %u to creat IBSS, abort\n",
return;
}
/*
* Fix the channel and related attributes.
*/
/*
* Do mode-specific rate setup.
*/
}
void
{
}
}
static int
{
int fail;
fail = 0;
}
}
} else {
}
} else {
}
if (rate & IEEE80211_RATE_BASIC)
if (ic->ic_des_esslen != 0 &&
}
}
return (fail);
}
/*
* Compare the capabilities of node a with node b and decide which is
* more desirable (return b if b is considered better than a). Note
* that we assume compatibility/usability has already been checked
* so we don't need to (e.g. validate whether privacy is supported).
* Used to select the best scan candidate for association in a BSS.
*
* Return desired node
*/
static ieee80211_node_t *
ieee80211_node_t *b)
{
/* privacy support preferred */
if ((a->in_capinfo & IEEE80211_CAPINFO_PRIVACY) &&
!(b->in_capinfo & IEEE80211_CAPINFO_PRIVACY)) {
return (a);
}
if (!(a->in_capinfo & IEEE80211_CAPINFO_PRIVACY) &&
(b->in_capinfo & IEEE80211_CAPINFO_PRIVACY)) {
return (b);
}
/* compare count of previous failures */
/* for now just prefer 5Ghz band to all other bands */
if (IEEE80211_IS_CHAN_5GHZ(a->in_chan) &&
!IEEE80211_IS_CHAN_5GHZ(b->in_chan)) {
return (a);
}
if (!IEEE80211_IS_CHAN_5GHZ(a->in_chan) &&
IEEE80211_IS_CHAN_5GHZ(b->in_chan)) {
return (b);
}
}
/* all things being equal, compare signal level */
}
/*
* Mark an ongoing scan stopped.
*/
void
{
"end %s scan\n",
}
/*
* Complete a scan of potential channels. It is called by
* ieee80211_next_scan() when the state machine has performed
* a full cycle of scaning on all available radio channels.
* ieee80211_end_scan() will inspect the node cache for suitable
* APs found during scaning, and associate with one, should
* the parameters of the node match those of the configuration
* requested from userland.
*/
void
{
/* notify SCAN done */
/*
* Automatic sequencing; look for a candidate and
* if found join the network.
*/
/* NB: unlocked read should be ok */
"no scan candidate\n");
ic->ic_des_esslen != 0) {
return;
}
/*
* Reset the list of channels to scan and start again.
*/
return;
}
return;
}
/*
* 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;
}
/*
* It's possible at some special moments, the in_chan will
* be none. Need to skip the null node.
*/
continue;
}
else
}
}
(void) ieee80211_ref_node(selbs);
goto notfound;
}
/*
* Handle 802.11 ad hoc network merge. The convention, set by the
* Wireless Ethernet Compatibility Alliance (WECA), is that an 802.11
* station will change its BSSID to match the "oldest" 802.11 ad hoc
* network, on the same channel, that has the station's desired SSID.
* The "oldest" 802.11 network sends beacons with the greatest TSF
* timestamp.
* The caller is assumed to validate TSF's before attempting a merge.
*
* Return B_TRUE if the BSSID changed, B_FALSE otherwise.
*/
{
/* unchanged, nothing to do */
return (B_FALSE);
}
" merge failed, capabilities mismatch\n");
return (B_FALSE);
}
"new bssid %s: %s preamble, %s slot time%s\n",
return (B_TRUE);
}
/*
* Change the bss channel.
*/
void
{
ic->ic_curchan = c;
}
/*
* be passed in with a held reference.
*/
void
{
/*
* Delete unusable rates; we've already checked
* that the negotiated rate set is acceptable.
*/
/*
* Fillin the neighbor table
*/
}
/*
* Committed to selbs, setup state.
*/
}
/*
* Set the erp state (mostly the slot time) to deal with
* the auto-select case; this should be redundant if the
* mode is locked.
*/
else
}
/*
* be passed in with a held reference.
*/
void
{
}
/*
* Allocate a node. This is the default callback function for
* ic_node_alloc. This function may be overridden by the driver
* to allocate device specific node structure.
*/
/* ARGSUSED */
static ieee80211_node_t *
{
}
/*
* Cleanup a node, free any memory associated with the node.
* This is the default callback function for ic_node_cleanup
* and may be overridden by the driver.
*/
static void
{
in->in_associd = 0;
}
}
}
/*
* Free a node. This is the default callback function for ic_node_free
* and may be overridden by the driver to free memory used by device
* specific node structure
*/
static void
{
}
/*
* Get a node current RSSI value. This is the default callback function
* for ic_node_getrssi and may be overridden by the driver to provide
* device specific RSSI calculation algorithm.
*/
static uint8_t
{
}
/* Free fragment if not needed anymore */
static void
{
ticks = ddi_get_lbolt();
}
}
/*
* Setup a node. Initialize the node with specified macaddr. Associate
* with the interface softc, ic, and add it to the specified node
* database.
*/
static void
{
"%p<%s> in %s table\n", in,
}
}
/*
* Allocates and initialize a node with specified MAC address.
* Associate the node with the interface ic. If the allocation
* is successful, the node structure is initialized by
* ieee80211_setup_node(); otherwise, NULL is returned
*/
{
return (in);
}
/*
* Craft a temporary node suitable for sending a management frame
* to the specified station. We craft only as much state as we
* need to do the work since the node will be immediately reclaimed
* once the send completes.
*/
{
/* NB: required by ieee80211_fix_rate */
}
return (in);
}
/*
* ieee80211_dup_bss() is similar to ieee80211_alloc_node(),
* but is instead used to create a node database entry for
* the specified BSSID. If the allocation is successful, the
* node is initialized, otherwise, NULL is returned.
*/
{
/*
* Inherit from ic_bss.
*/
}
return (in);
}
/*
* Iterate through the node table, searching for a node entry which
* matches macaddr. If the entry is found, its reference count is
* incremented, and a pointer to the node is returned; otherwise,
* NULL will be returned.
* The node table lock is acquired by the caller.
*/
static ieee80211_node_t *
{
int hash;
}
return (NULL);
}
/*
* Iterate through the node table, searching for a node entry
* which match specified mac address.
* Return NULL if no matching node found.
*/
{
return (in);
}
/*
* Like find but search based on the ssid too.
*/
{
int hash;
break;
}
}
return (in);
}
/*
* Fake up a node; this handles node discovery in adhoc mode.
* Note that for the driver's benefit we treat this like an
* association so the driver has an opportunity to setup it's
* private state.
*/
{
/* no rate negotiation; just dup */
}
return (in);
}
static void
{
/*
* Record information element for later use.
*/
}
}
static void
{
}
else
}
/*
* Process a beacon or probe response frame.
*/
void
{
/*
* Create a new entry.
*/
"alloc node failed\n");
return;
}
/*
* inherit from ic_bss.
*/
}
/* ap beaconing multiple ssid w/ same bssid */
/*
* sp->ssid[0] - element ID
* sp->ssid[1] - length
* sp->ssid[2]... - ssid
*/
}
}
/*
* Record the byte offset from the mac header to
* the start of the TIM information element for
* processing of beacon frames.
*/
/*
* Record optional information elements that might be
* used by applications or drivers.
*/
/* parsed in ieee80211_sta_join() */
/* NB: must be after in_chan is setup */
if (!newnode)
}
/*
* Initialize/update an ad-hoc node with contents from a received
* beacon frame.
*/
void
const struct ieee80211_scanparams *sp)
{
/* NB: must be after in_chan is setup */
}
/*
* Do node discovery in adhoc mode on receipt of a beacon
* or probe response frame. Note that for the driver's
* benefit we we treat this like an association so the
* driver has an opportuinty to setup it's private state.
*/
const struct ieee80211_scanparams *sp)
{
}
return (in);
}
/*
* Locate the node for sender, track state, and then pass the
* (referenced) node up to the 802.11 layer for its use. We
* are required to pass some node so we fall back to ic_bss
* when this frame is from an unknown sender. The 802.11 layer
* knows this means the sender wasn't in the node table and
* acts accordingly.
*/
{
/* may want scanned nodes in the neighbor table for adhoc */
} else {
}
if (IEEE80211_IS_CTL(wh) &&
else
return (in);
}
/*
* Return a reference to the appropriate node for sending
* a data frame. This handles node discovery in adhoc networks.
*/
{
/*
* The destination address should be in the node table
* also optimize station mode operation, all frames go
* to the bss node.
*/
else
/*
* In adhoc mode cons up a node for the destination.
* Note that we need an additional reference for the
* caller to be consistent with
* ieee80211_find_node_locked
* can't hold lock across ieee80211_dup_bss 'cuz of
* recursive locking
*/
(void) ieee80211_ref_node(in);
} else {
"ieee80211_find_txnode: "
"[%s] no node, discard frame\n",
}
}
return (in);
}
/*
* Remove a node from the node database entries and free memory
* associated with the node. The node table lock is acquired by
* the caller.
*/
static void
{
}
}
/*
* Remove a node from the node database entries and free any
* memory associated with the node.
* This method can be overridden in ieee80211_attach()
*/
void
{
if (ieee80211_node_decref_nv(in) == 0)
}
/*
* Reclaim a node. If this is the last reference count then
* do the normal free work. Otherwise remove it from the node
* table and mark it gone by clearing the back-reference.
*/
static void
{
" remove %p<%s> from %s table, refcnt %d\n",
if (ieee80211_node_decref_nv(in) != 0) {
/*
* Clear any entry in the unicast key mapping table.
* We need to do it here so rx lookups don't find it
* in the mapping table even if it's not in the hash
* table. We cannot depend on the mapping table entry
* being cleared because the node may not be free'd.
*/
} else {
}
}
/*
* Iterate through the node list and reclaim all node in the node table.
* The node table lock is acquired by the caller
*/
static void
{
}
}
/*
* Iterate through the node list, calling ieee80211_node_reclaim() for
* all nodes associated with the interface.
*/
static void
{
}
/*
* Timeout entries in the scan cache. This is the timeout callback
* function of node table ic_scan which is called when the inactivity
* timer expires.
*/
static void
{
}
/*
* Timeout inactive stations and do related housekeeping.
* Note that we cannot hold the node lock while sending a
* frame as this would lead to a LOR. Instead we use a
* generation number to mark nodes that we've scanned and
* drop the lock and restart a scan if we have to time out
* a node. Since we are single-threaded by virtue of
* controlling the inactivity timer we can be sure this will
* process each node only once.
*/
static void
{
continue;
/*
* Special case ourself; we may be idle for extended periods
* of time and regardless reclaiming our state is wrong.
*/
continue;
/*
* Probe the station before time it out. We
* send a null data frame which may not be
* uinversally supported by drivers (need it
* for ps-poll support so it should be...).
*/
"probe station due to inactivity\n");
(void) ieee80211_send_nulldata(in);
goto restart;
}
}
"station timed out due to inact (refcnt %u)\n",
/*
* Send a deauthenticate frame and drop the station.
* This is somewhat complicated due to reference counts
* and locking. At this point a station will typically
* have a reference count of 1. ieee80211_node_leave
* will do a "free" of the node which will drop the
* reference count. But in the meantime a reference
* wil be held by the deauth frame. The actual reclaim
* of the node will happen either after the tx is
* completed or by ieee80211_node_leave.
*
* Separately we must drop the node lock before sending
* in case the driver takes a lock, as this will result
* in LOR between the node lock and the driver lock.
*/
if (in->in_associd != 0) {
}
goto restart;
}
}
}
/*
* Call the user-defined call back function for all nodes in
* the node cache. The callback is invoked with the user-supplied
* value and a pointer to the current node.
*/
void
void *arg)
{
continue;
}
(void) ieee80211_ref_node(in);
}
}
/*
* Handle bookkeeping for station deauthentication/disassociation
* when operating as an ap.
*/
static void
{
/*
* Remove the node from any table it's recorded in and
* drop the caller's reference. Removal from the table
* is important to insure the node is not reprocessed
* for inactivity.
*/
} else {
}
}
/*
* Initialize a node table with specified name, inactivity timer value
* and callback inactivity timeout function. Associate the node table
* with interface softc, ic.
*/
static void
void (*timeout)(ieee80211_node_table_t *))
{
int i;
nt->nt_inact_timer = 0;
for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) {
}
}
/*
* Reset a node table. Clean its inactivity timer and call
* ieee80211_free_allnodes_locked() to free all nodes in the
* node table.
*/
void
{
nt->nt_inact_timer = 0;
}
/*
* Destroy a node table. Free all nodes in the node table.
* This function is usually called by node detach function.
*/
static void
{
}