* Importing r401 of ported wlan stack from osdrawer.net/haiku-wifi.

This port is based on FreeBSD RELEASE_8_0_0 (r199625) found in Haiku's
  freebsd vendor branch.
* Added freebsd_wlan directory to the Jamfile in HAIKU_TOP/src/libs/compat/
  so that the stack can be built with jam libfreebsd_wlan.a.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34343 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Colin Günther
2009-11-29 11:22:01 +00:00
parent ea77094b24
commit a315b76dc8
71 changed files with 46248 additions and 0 deletions
+1
View File
@@ -1,3 +1,4 @@
SubDir HAIKU_TOP src libs compat ;
SubInclude HAIKU_TOP src libs compat freebsd_network ;
SubInclude HAIKU_TOP src libs compat freebsd_wlan ;
+4
View File
@@ -0,0 +1,4 @@
SubDir HAIKU_TOP src libs compat freebsd_wlan ;
SubInclude HAIKU_TOP src libs compat freebsd_wlan net80211 ;
@@ -0,0 +1,54 @@
SubDir HAIKU_TOP src libs compat freebsd_wlan net80211 ;
UseHeaders [ FDirName $(SUBDIR) .. .. freebsd_network ] : true ;
UseHeaders [ FDirName $(SUBDIR) .. .. freebsd_network compat ] : true ;
UseHeaders [ FDirName $(SUBDIR) .. ] : true ;
UseHeaders [ FDirName $(SUBDIR) ] : true ;
UsePrivateHeaders net system ;
UsePrivateKernelHeaders ;
SubDirCcFlags [ FDefines _KERNEL=1 FBSD_DRIVER=1 ]
-Wno-format
-Wno-unused
-Wno-uninitialized ;
KernelStaticLibrary libfreebsd_wlan.a :
ieee80211.c
ieee80211_action.c
ieee80211_adhoc.c
ieee80211_ageq.c
ieee80211_crypto_none.c
ieee80211_crypto.c
ieee80211_dfs.c
ieee80211_haiku.c
ieee80211_ht.c
ieee80211_input.c
ieee80211_ioctl.c
ieee80211_hostap.c
ieee80211_monitor.c
ieee80211_node.c
ieee80211_output.c
ieee80211_phy.c
ieee80211_power.c
ieee80211_proto.c
ieee80211_radiotap.c
ieee80211_regdomain.c
ieee80211_scan_sta.c
ieee80211_scan.c
ieee80211_sta.c
ieee80211_wds.c
# NOT SUPPORTED YET ieee80211_acl.c
# NOT SUPPORTED YET ieee80211_amrr.c
# NOT SUPPORTED YET ieee80211_crypto_ccmp.c
# NOT SUPPORTED YET ieee80211_crypto_tkip.c
# NOT SUPPORTED YET ieee80211_crypto_wep.c
# NOT SUPPORTED YET ieee80211_ddb.c
# NOT SUPPORTED YET ieee80211_hwmp.c
# NOT SUPPORTED YET ieee80211_mesh.c
# NOT SUPPORTED YET ieee80211_rssadapt.c
# NOT SUPPORTED YET ieee80211_superg.c
# NOT SUPPORTED YET ieee80211_tdma.c
# NOT SUPPORTED YET ieee80211_xauth.c
;
@@ -0,0 +1,396 @@
/*-
* 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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211__IEEE80211_H_
#define _FBSD_COMPAT_NET80211__IEEE80211_H_
/*
* 802.11 implementation definitions.
*
* NB: this file is used by applications.
*/
/*
* PHY type; mostly used to identify FH phys.
*/
enum ieee80211_phytype {
IEEE80211_T_DS, /* direct sequence spread spectrum */
IEEE80211_T_FH, /* frequency hopping */
IEEE80211_T_OFDM, /* frequency division multiplexing */
IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */
IEEE80211_T_HT, /* high throughput */
IEEE80211_T_OFDM_HALF, /* 1/2 rate OFDM */
IEEE80211_T_OFDM_QUARTER, /* 1/4 rate OFDM */
};
#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
/*
* PHY mode; this is not really a mode as multi-mode devices
* have multiple PHY's. Mode is mostly used as a shorthand
* for constraining which channels to consider in setting up
* operation. Modes used to be used more extensively when
* channels were identified as IEEE channel numbers.
*/
enum ieee80211_phymode {
IEEE80211_MODE_AUTO = 0, /* autoselect */
IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */
IEEE80211_MODE_11B = 2, /* 2GHz, CCK */
IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */
IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */
IEEE80211_MODE_TURBO_A = 5, /* 5GHz, OFDM, 2x clock */
IEEE80211_MODE_TURBO_G = 6, /* 2GHz, OFDM, 2x clock */
IEEE80211_MODE_STURBO_A = 7, /* 5GHz, OFDM, 2x clock, static */
IEEE80211_MODE_11NA = 8, /* 5GHz, w/ HT */
IEEE80211_MODE_11NG = 9, /* 2GHz, w/ HT */
IEEE80211_MODE_HALF = 10, /* OFDM, 1/2x clock */
IEEE80211_MODE_QUARTER = 11, /* OFDM, 1/4x clock */
};
#define IEEE80211_MODE_MAX (IEEE80211_MODE_QUARTER+1)
/*
* Operating mode. Devices do not necessarily support
* all modes; they indicate which are supported in their
* capabilities.
*/
enum ieee80211_opmode {
IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */
IEEE80211_M_STA = 1, /* infrastructure station */
IEEE80211_M_WDS = 2, /* WDS link */
IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */
IEEE80211_M_HOSTAP = 4, /* Software Access Point */
IEEE80211_M_MONITOR = 5, /* Monitor mode */
IEEE80211_M_MBSS = 6, /* MBSS (Mesh Point) link */
};
#define IEEE80211_OPMODE_MAX (IEEE80211_M_MBSS+1)
/*
* 802.11g/802.11n protection mode.
*/
enum ieee80211_protmode {
IEEE80211_PROT_NONE = 0, /* no protection */
IEEE80211_PROT_CTSONLY = 1, /* CTS to self */
IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */
};
/*
* Authentication mode. The open and shared key authentication
* modes are implemented within the 802.11 layer. 802.1x and
* WPA/802.11i are implemented in user mode by setting the
* 802.11 layer into IEEE80211_AUTH_8021X and deferring
* authentication to user space programs.
*/
enum ieee80211_authmode {
IEEE80211_AUTH_NONE = 0,
IEEE80211_AUTH_OPEN = 1, /* open */
IEEE80211_AUTH_SHARED = 2, /* shared-key */
IEEE80211_AUTH_8021X = 3, /* 802.1x */
IEEE80211_AUTH_AUTO = 4, /* auto-select/accept */
/* NB: these are used only for ioctls */
IEEE80211_AUTH_WPA = 5, /* WPA/RSN w/ 802.1x/PSK */
};
/*
* Roaming mode is effectively who controls the operation
* of the 802.11 state machine when operating as a station.
* State transitions are controlled either by the driver
* (typically when management frames are processed by the
* hardware/firmware), the host (auto/normal operation of
* the 802.11 layer), or explicitly through ioctl requests
* when applications like wpa_supplicant want control.
*/
enum ieee80211_roamingmode {
IEEE80211_ROAMING_DEVICE= 0, /* driver/hardware control */
IEEE80211_ROAMING_AUTO = 1, /* 802.11 layer control */
IEEE80211_ROAMING_MANUAL= 2, /* application control */
};
/*
* Channels are specified by frequency and attributes.
*/
struct ieee80211_channel {
uint32_t ic_flags; /* see below */
uint16_t ic_freq; /* setting in Mhz */
uint8_t ic_ieee; /* IEEE channel number */
int8_t ic_maxregpower; /* maximum regulatory tx power in dBm */
int8_t ic_maxpower; /* maximum tx power in .5 dBm */
int8_t ic_minpower; /* minimum tx power in .5 dBm */
uint8_t ic_state; /* dynamic state */
uint8_t ic_extieee; /* HT40 extension channel number */
int8_t ic_maxantgain; /* maximum antenna gain in .5 dBm */
uint8_t ic_pad;
uint16_t ic_devdata; /* opaque device/driver data */
};
#define IEEE80211_CHAN_MAX 256
#define IEEE80211_CHAN_BYTES 32 /* howmany(IEEE80211_CHAN_MAX, NBBY) */
#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */
#define IEEE80211_CHAN_ANYC \
((struct ieee80211_channel *) IEEE80211_CHAN_ANY)
/* channel attributes */
#define IEEE80211_CHAN_PRIV0 0x00000001 /* driver private bit 0 */
#define IEEE80211_CHAN_PRIV1 0x00000002 /* driver private bit 1 */
#define IEEE80211_CHAN_PRIV2 0x00000004 /* driver private bit 2 */
#define IEEE80211_CHAN_PRIV3 0x00000008 /* driver private bit 3 */
#define IEEE80211_CHAN_TURBO 0x00000010 /* Turbo channel */
#define IEEE80211_CHAN_CCK 0x00000020 /* CCK channel */
#define IEEE80211_CHAN_OFDM 0x00000040 /* OFDM channel */
#define IEEE80211_CHAN_2GHZ 0x00000080 /* 2 GHz spectrum channel. */
#define IEEE80211_CHAN_5GHZ 0x00000100 /* 5 GHz spectrum channel */
#define IEEE80211_CHAN_PASSIVE 0x00000200 /* Only passive scan allowed */
#define IEEE80211_CHAN_DYN 0x00000400 /* Dynamic CCK-OFDM channel */
#define IEEE80211_CHAN_GFSK 0x00000800 /* GFSK channel (FHSS PHY) */
#define IEEE80211_CHAN_GSM 0x00001000 /* 900 MHz spectrum channel */
#define IEEE80211_CHAN_STURBO 0x00002000 /* 11a static turbo channel only */
#define IEEE80211_CHAN_HALF 0x00004000 /* Half rate channel */
#define IEEE80211_CHAN_QUARTER 0x00008000 /* Quarter rate channel */
#define IEEE80211_CHAN_HT20 0x00010000 /* HT 20 channel */
#define IEEE80211_CHAN_HT40U 0x00020000 /* HT 40 channel w/ ext above */
#define IEEE80211_CHAN_HT40D 0x00040000 /* HT 40 channel w/ ext below */
#define IEEE80211_CHAN_DFS 0x00080000 /* DFS required */
#define IEEE80211_CHAN_4MSXMIT 0x00100000 /* 4ms limit on frame length */
#define IEEE80211_CHAN_NOADHOC 0x00200000 /* adhoc mode not allowed */
#define IEEE80211_CHAN_NOHOSTAP 0x00400000 /* hostap mode not allowed */
#define IEEE80211_CHAN_11D 0x00800000 /* 802.11d required */
#define IEEE80211_CHAN_HT40 (IEEE80211_CHAN_HT40U | IEEE80211_CHAN_HT40D)
#define IEEE80211_CHAN_HT (IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40)
#define IEEE80211_CHAN_BITS \
"\20\1PRIV0\2PRIV2\3PRIV3\4PRIV4\5TURBO\6CCK\7OFDM\0102GHZ\0115GHZ" \
"\12PASSIVE\13DYN\14GFSK\15GSM\16STURBO\17HALF\20QUARTER\21HT20" \
"\22HT40U\23HT40D\24DFS\0254MSXMIT\26NOADHOC\27NOHOSTAP\03011D"
/*
* Useful combinations of channel characteristics.
*/
#define IEEE80211_CHAN_FHSS \
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
#define IEEE80211_CHAN_A \
(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
#define IEEE80211_CHAN_B \
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
#define IEEE80211_CHAN_PUREG \
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
#define IEEE80211_CHAN_G \
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
#define IEEE80211_CHAN_108A \
(IEEE80211_CHAN_A | IEEE80211_CHAN_TURBO)
#define IEEE80211_CHAN_108G \
(IEEE80211_CHAN_PUREG | IEEE80211_CHAN_TURBO)
#define IEEE80211_CHAN_ST \
(IEEE80211_CHAN_108A | IEEE80211_CHAN_STURBO)
#define IEEE80211_CHAN_ALL \
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_GFSK | \
IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | \
IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER | \
IEEE80211_CHAN_HT)
#define IEEE80211_CHAN_ALLTURBO \
(IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)
#define IEEE80211_IS_CHAN_FHSS(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
#define IEEE80211_IS_CHAN_A(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
#define IEEE80211_IS_CHAN_B(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
#define IEEE80211_IS_CHAN_PUREG(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
#define IEEE80211_IS_CHAN_G(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
#define IEEE80211_IS_CHAN_ANYG(_c) \
(IEEE80211_IS_CHAN_PUREG(_c) || IEEE80211_IS_CHAN_G(_c))
#define IEEE80211_IS_CHAN_ST(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)
#define IEEE80211_IS_CHAN_108A(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A)
#define IEEE80211_IS_CHAN_108G(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)
#define IEEE80211_IS_CHAN_2GHZ(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
#define IEEE80211_IS_CHAN_5GHZ(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
#define IEEE80211_IS_CHAN_PASSIVE(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_PASSIVE) != 0)
#define IEEE80211_IS_CHAN_OFDM(_c) \
(((_c)->ic_flags & (IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN)) != 0)
#define IEEE80211_IS_CHAN_CCK(_c) \
(((_c)->ic_flags & (IEEE80211_CHAN_CCK | IEEE80211_CHAN_DYN)) != 0)
#define IEEE80211_IS_CHAN_GFSK(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0)
#define IEEE80211_IS_CHAN_TURBO(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_TURBO) != 0)
#define IEEE80211_IS_CHAN_STURBO(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_STURBO) != 0)
#define IEEE80211_IS_CHAN_DTURBO(_c) \
(((_c)->ic_flags & \
(IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)) == IEEE80211_CHAN_TURBO)
#define IEEE80211_IS_CHAN_HALF(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_HALF) != 0)
#define IEEE80211_IS_CHAN_QUARTER(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_QUARTER) != 0)
#define IEEE80211_IS_CHAN_FULL(_c) \
(((_c)->ic_flags & (IEEE80211_CHAN_QUARTER | IEEE80211_CHAN_HALF)) == 0)
#define IEEE80211_IS_CHAN_GSM(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_GSM) != 0)
#define IEEE80211_IS_CHAN_HT(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
#define IEEE80211_IS_CHAN_HT20(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_HT20) != 0)
#define IEEE80211_IS_CHAN_HT40(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_HT40) != 0)
#define IEEE80211_IS_CHAN_HT40U(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_HT40U) != 0)
#define IEEE80211_IS_CHAN_HT40D(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_HT40D) != 0)
#define IEEE80211_IS_CHAN_HTA(_c) \
(IEEE80211_IS_CHAN_5GHZ(_c) && \
((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
#define IEEE80211_IS_CHAN_HTG(_c) \
(IEEE80211_IS_CHAN_2GHZ(_c) && \
((_c)->ic_flags & IEEE80211_CHAN_HT) != 0)
#define IEEE80211_IS_CHAN_DFS(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_DFS) != 0)
#define IEEE80211_IS_CHAN_NOADHOC(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_NOADHOC) != 0)
#define IEEE80211_IS_CHAN_NOHOSTAP(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_NOHOSTAP) != 0)
#define IEEE80211_IS_CHAN_11D(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_11D) != 0)
#define IEEE80211_CHAN2IEEE(_c) (_c)->ic_ieee
/* dynamic state */
#define IEEE80211_CHANSTATE_RADAR 0x01 /* radar detected */
#define IEEE80211_CHANSTATE_CACDONE 0x02 /* CAC completed */
#define IEEE80211_CHANSTATE_CWINT 0x04 /* interference detected */
#define IEEE80211_CHANSTATE_NORADAR 0x10 /* post notify on radar clear */
#define IEEE80211_IS_CHAN_RADAR(_c) \
(((_c)->ic_state & IEEE80211_CHANSTATE_RADAR) != 0)
#define IEEE80211_IS_CHAN_CACDONE(_c) \
(((_c)->ic_state & IEEE80211_CHANSTATE_CACDONE) != 0)
#define IEEE80211_IS_CHAN_CWINT(_c) \
(((_c)->ic_state & IEEE80211_CHANSTATE_CWINT) != 0)
/* ni_chan encoding for FH phy */
#define IEEE80211_FH_CHANMOD 80
#define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat))
#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1)
#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD)
#define IEEE80211_TID_SIZE (WME_NUM_TID+1) /* WME TID's +1 for non-QoS */
#define IEEE80211_NONQOS_TID WME_NUM_TID /* index for non-QoS sta */
/*
* The 802.11 spec says at most 2007 stations may be
* associated at once. For most AP's this is way more
* than is feasible so we use a default of 128. This
* number may be overridden by the driver and/or by
* user configuration but may not be less than IEEE80211_AID_MIN.
*/
#define IEEE80211_AID_DEF 128
#define IEEE80211_AID_MIN 16
/*
* 802.11 rate set.
*/
#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */
#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */
struct ieee80211_rateset {
uint8_t rs_nrates;
uint8_t rs_rates[IEEE80211_RATE_MAXSIZE];
};
/*
* 802.11n variant of ieee80211_rateset. Instead of
* legacy rates the entries are MCS rates. We define
* the structure such that it can be used interchangeably
* with an ieee80211_rateset (modulo structure size).
*/
#define IEEE80211_HTRATE_MAXSIZE 127
struct ieee80211_htrateset {
uint8_t rs_nrates;
uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE];
};
#define IEEE80211_RATE_MCS 0x80
/*
* Per-mode transmit parameters/controls visible to user space.
* These can be used to set fixed transmit rate for all operating
* modes or on a per-client basis according to the capabilities
* of the client (e.g. an 11b client associated to an 11g ap).
*
* MCS are distinguished from legacy rates by or'ing in 0x80.
*/
struct ieee80211_txparam {
uint8_t ucastrate; /* ucast data rate (legacy/MCS|0x80) */
uint8_t mgmtrate; /* mgmt frame rate (legacy/MCS|0x80) */
uint8_t mcastrate; /* multicast rate (legacy/MCS|0x80) */
uint8_t maxretry; /* max unicast data retry count */
};
/*
* Per-mode roaming state visible to user space. There are two
* thresholds that control whether roaming is considered; when
* either is exceeded the 802.11 layer will check the scan cache
* for another AP. If the cache is stale then a scan may be
* triggered.
*/
struct ieee80211_roamparam {
int8_t rssi; /* rssi thresh (.5 dBm) */
uint8_t rate; /* tx rate thresh (.5 Mb/s or MCS) */
uint16_t pad; /* reserve */
};
/*
* Regulatory Information.
*/
struct ieee80211_regdomain {
uint16_t regdomain; /* SKU */
uint16_t country; /* ISO country code */
uint8_t location; /* I (indoor), O (outdoor), other */
uint8_t ecm; /* Extended Channel Mode */
char isocc[2]; /* country code string */
short pad[2];
};
/*
* MIMO antenna/radio state.
*/
struct ieee80211_mimo_info {
int8_t rssi[3]; /* per-antenna rssi */
int8_t noise[3]; /* per-antenna noise floor */
uint8_t pad[2];
uint32_t evm[3]; /* EVM data */
};
#endif /* _FBSD_COMPAT_NET80211__IEEE80211_H_ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,339 @@
/*-
* Copyright (c) 2004-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11 MAC ACL support.
*
* When this module is loaded the sender address of each auth mgt
* frame is passed to the iac_check method and the module indicates
* if the frame should be accepted or rejected. If the policy is
* set to ACL_POLICY_OPEN then all frames are accepted w/o checking
* the address. Otherwise, the address is looked up in the database
* and if found the frame is either accepted (ACL_POLICY_ALLOW)
* or rejected (ACL_POLICY_DENT).
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net/route.h>
#include <net80211/ieee80211_var.h>
enum {
ACL_POLICY_OPEN = 0, /* open, don't check ACL's */
ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */
ACL_POLICY_DENY = 2, /* deny traffic from MAC */
/*
* NB: ACL_POLICY_RADIUS must be the same value as
* IEEE80211_MACCMD_POLICY_RADIUS because of the way
* acl_getpolicy() works.
*/
ACL_POLICY_RADIUS = 7, /* defer to RADIUS ACL server */
};
#define ACL_HASHSIZE 32
struct acl {
TAILQ_ENTRY(acl) acl_list;
LIST_ENTRY(acl) acl_hash;
uint8_t acl_macaddr[IEEE80211_ADDR_LEN];
};
struct aclstate {
acl_lock_t as_lock;
int as_policy;
int as_nacls;
TAILQ_HEAD(, acl) as_list; /* list of all ACL's */
LIST_HEAD(, acl) as_hash[ACL_HASHSIZE];
struct ieee80211vap *as_vap;
};
/* simple hash is enough for variation of macaddr */
#define ACL_HASH(addr) \
(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
static int acl_free_all(struct ieee80211vap *);
/* number of references from net80211 layer */
static int nrefs = 0;
static int
acl_attach(struct ieee80211vap *vap)
{
struct aclstate *as;
as = (struct aclstate *) malloc(sizeof(struct aclstate),
M_80211_ACL, M_NOWAIT | M_ZERO);
if (as == NULL)
return 0;
ACL_LOCK_INIT(as, "acl");
TAILQ_INIT(&as->as_list);
as->as_policy = ACL_POLICY_OPEN;
as->as_vap = vap;
vap->iv_as = as;
nrefs++; /* NB: we assume caller locking */
return 1;
}
static void
acl_detach(struct ieee80211vap *vap)
{
struct aclstate *as = vap->iv_as;
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
acl_free_all(vap);
vap->iv_as = NULL;
ACL_LOCK_DESTROY(as);
free(as, M_80211_ACL);
}
static __inline struct acl *
_find_acl(struct aclstate *as, const uint8_t *macaddr)
{
struct acl *acl;
int hash;
hash = ACL_HASH(macaddr);
LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
return acl;
}
return NULL;
}
static void
_acl_free(struct aclstate *as, struct acl *acl)
{
ACL_LOCK_ASSERT(as);
TAILQ_REMOVE(&as->as_list, acl, acl_list);
LIST_REMOVE(acl, acl_hash);
free(acl, M_80211_ACL);
as->as_nacls--;
}
static int
acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct aclstate *as = vap->iv_as;
switch (as->as_policy) {
case ACL_POLICY_OPEN:
case ACL_POLICY_RADIUS:
return 1;
case ACL_POLICY_ALLOW:
return _find_acl(as, mac) != NULL;
case ACL_POLICY_DENY:
return _find_acl(as, mac) == NULL;
}
return 0; /* should not happen */
}
static int
acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct aclstate *as = vap->iv_as;
struct acl *acl, *new;
int hash;
new = (struct acl *) malloc(sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
if (new == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s failed, no memory\n", ether_sprintf(mac));
/* XXX statistic */
return ENOMEM;
}
ACL_LOCK(as);
hash = ACL_HASH(mac);
LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
ACL_UNLOCK(as);
free(new, M_80211_ACL);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s failed, already present\n",
ether_sprintf(mac));
return EEXIST;
}
}
IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
as->as_nacls++;
ACL_UNLOCK(as);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s\n", ether_sprintf(mac));
return 0;
}
static int
acl_remove(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct aclstate *as = vap->iv_as;
struct acl *acl;
ACL_LOCK(as);
acl = _find_acl(as, mac);
if (acl != NULL)
_acl_free(as, acl);
ACL_UNLOCK(as);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: remove %s%s\n", ether_sprintf(mac),
acl == NULL ? ", not present" : "");
return (acl == NULL ? ENOENT : 0);
}
static int
acl_free_all(struct ieee80211vap *vap)
{
struct aclstate *as = vap->iv_as;
struct acl *acl;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
ACL_LOCK(as);
while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
_acl_free(as, acl);
ACL_UNLOCK(as);
return 0;
}
static int
acl_setpolicy(struct ieee80211vap *vap, int policy)
{
struct aclstate *as = vap->iv_as;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: set policy to %u\n", policy);
switch (policy) {
case IEEE80211_MACCMD_POLICY_OPEN:
as->as_policy = ACL_POLICY_OPEN;
break;
case IEEE80211_MACCMD_POLICY_ALLOW:
as->as_policy = ACL_POLICY_ALLOW;
break;
case IEEE80211_MACCMD_POLICY_DENY:
as->as_policy = ACL_POLICY_DENY;
break;
case IEEE80211_MACCMD_POLICY_RADIUS:
as->as_policy = ACL_POLICY_RADIUS;
break;
default:
return EINVAL;
}
return 0;
}
static int
acl_getpolicy(struct ieee80211vap *vap)
{
struct aclstate *as = vap->iv_as;
return as->as_policy;
}
static int
acl_setioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
return EINVAL;
}
static int
acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct aclstate *as = vap->iv_as;
struct acl *acl;
struct ieee80211req_maclist *ap;
int error, space, i;
switch (ireq->i_val) {
case IEEE80211_MACCMD_POLICY:
ireq->i_val = as->as_policy;
return 0;
case IEEE80211_MACCMD_LIST:
space = as->as_nacls * IEEE80211_ADDR_LEN;
if (ireq->i_len == 0) {
ireq->i_len = space; /* return required space */
return 0; /* NB: must not error */
}
ap = (struct ieee80211req_maclist *) malloc(space,
M_TEMP, M_NOWAIT);
if (ap == NULL)
return ENOMEM;
i = 0;
ACL_LOCK(as);
TAILQ_FOREACH(acl, &as->as_list, acl_list) {
IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr);
i++;
}
ACL_UNLOCK(as);
if (ireq->i_len >= space) {
error = copyout(ap, ireq->i_data, space);
ireq->i_len = space;
} else
error = copyout(ap, ireq->i_data, ireq->i_len);
free(ap, M_TEMP);
return error;
}
return EINVAL;
}
static const struct ieee80211_aclator mac = {
.iac_name = "mac",
.iac_attach = acl_attach,
.iac_detach = acl_detach,
.iac_check = acl_check,
.iac_add = acl_add,
.iac_remove = acl_remove,
.iac_flush = acl_free_all,
.iac_setpolicy = acl_setpolicy,
.iac_getpolicy = acl_getpolicy,
.iac_setioctl = acl_setioctl,
.iac_getioctl = acl_getioctl,
};
IEEE80211_ACL_MODULE(wlan_acl, mac, 1);
@@ -0,0 +1,279 @@
/*-
* Copyright (c) 2009 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.
*
* 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.
*/
#include <sys/cdefs.h>
#ifdef __FreeBSD__
__FBSDID("$FreeBSD$");
#endif
/*
* IEEE 802.11 send/recv action frame support.
*/
#include "opt_inet.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_action.h>
#include <net80211/ieee80211_mesh.h>
static int
send_inval(struct ieee80211_node *ni, int cat, int act, void *sa)
{
return EINVAL;
}
static ieee80211_send_action_func *ba_send_action[8] = {
send_inval, send_inval, send_inval, send_inval,
send_inval, send_inval, send_inval, send_inval,
};
static ieee80211_send_action_func *ht_send_action[8] = {
send_inval, send_inval, send_inval, send_inval,
send_inval, send_inval, send_inval, send_inval,
};
static ieee80211_send_action_func *meshpl_send_action[8] = {
send_inval, send_inval, send_inval, send_inval,
send_inval, send_inval, send_inval, send_inval,
};
static ieee80211_send_action_func *meshlm_send_action[4] = {
send_inval, send_inval, send_inval, send_inval,
};
static ieee80211_send_action_func *hwmp_send_action[8] = {
send_inval, send_inval, send_inval, send_inval,
send_inval, send_inval, send_inval, send_inval,
};
static ieee80211_send_action_func *vendor_send_action[8] = {
send_inval, send_inval, send_inval, send_inval,
send_inval, send_inval, send_inval, send_inval,
};
int
ieee80211_send_action_register(int cat, int act, ieee80211_send_action_func *f)
{
#define N(a) (sizeof(a) / sizeof(a[0]))
switch (cat) {
case IEEE80211_ACTION_CAT_BA:
if (act >= N(ba_send_action))
break;
ba_send_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_HT:
if (act >= N(ht_send_action))
break;
ht_send_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_MESHPEERING:
if (act >= N(meshpl_send_action))
break;
meshpl_send_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_MESHLMETRIC:
if (act >= N(meshlm_send_action))
break;
meshlm_send_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_MESHPATH:
if (act > N(hwmp_send_action))
break;
hwmp_send_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_VENDOR:
if (act >= N(vendor_send_action))
break;
vendor_send_action[act] = f;
return 0;
}
return EINVAL;
#undef N
}
void
ieee80211_send_action_unregister(int cat, int act)
{
ieee80211_send_action_register(cat, act, send_inval);
}
int
ieee80211_send_action(struct ieee80211_node *ni, int cat, int act, void *sa)
{
#define N(a) (sizeof(a) / sizeof(a[0]))
ieee80211_send_action_func *f = send_inval;
switch (cat) {
case IEEE80211_ACTION_CAT_BA:
if (act < N(ba_send_action))
f = ba_send_action[act];
break;
case IEEE80211_ACTION_CAT_HT:
if (act < N(ht_send_action))
f = ht_send_action[act];
break;
case IEEE80211_ACTION_CAT_MESHPEERING:
if (act < N(meshpl_send_action))
f = meshpl_send_action[act];
break;
case IEEE80211_ACTION_CAT_MESHLMETRIC:
if (act < N(meshlm_send_action))
f = meshlm_send_action[act];
break;
case IEEE80211_ACTION_CAT_MESHPATH:
if (act < N(hwmp_send_action))
f = hwmp_send_action[act];
break;
case IEEE80211_ACTION_CAT_VENDOR:
if (act < N(vendor_send_action))
f = vendor_send_action[act];
break;
}
return f(ni, cat, act, sa);
#undef N
}
static int
recv_inval(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
const uint8_t *frm, const uint8_t *efrm)
{
return EINVAL;
}
static ieee80211_recv_action_func *ba_recv_action[8] = {
recv_inval, recv_inval, recv_inval, recv_inval,
recv_inval, recv_inval, recv_inval, recv_inval,
};
static ieee80211_recv_action_func *ht_recv_action[8] = {
recv_inval, recv_inval, recv_inval, recv_inval,
recv_inval, recv_inval, recv_inval, recv_inval,
};
static ieee80211_recv_action_func *meshpl_recv_action[8] = {
recv_inval, recv_inval, recv_inval, recv_inval,
recv_inval, recv_inval, recv_inval, recv_inval,
};
static ieee80211_recv_action_func *meshlm_recv_action[4] = {
recv_inval, recv_inval, recv_inval, recv_inval,
};
static ieee80211_recv_action_func *hwmp_recv_action[8] = {
recv_inval, recv_inval, recv_inval, recv_inval,
recv_inval, recv_inval, recv_inval, recv_inval,
};
static ieee80211_recv_action_func *vendor_recv_action[8] = {
recv_inval, recv_inval, recv_inval, recv_inval,
recv_inval, recv_inval, recv_inval, recv_inval,
};
int
ieee80211_recv_action_register(int cat, int act, ieee80211_recv_action_func *f)
{
#define N(a) (sizeof(a) / sizeof(a[0]))
switch (cat) {
case IEEE80211_ACTION_CAT_BA:
if (act >= N(ba_recv_action))
break;
ba_recv_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_HT:
if (act >= N(ht_recv_action))
break;
ht_recv_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_MESHPEERING:
if (act >= N(meshpl_recv_action))
break;
meshpl_recv_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_MESHLMETRIC:
if (act >= N(meshlm_recv_action))
break;
meshlm_recv_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_MESHPATH:
if (act >= N(hwmp_recv_action))
break;
hwmp_recv_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_VENDOR:
if (act >= N(vendor_recv_action))
break;
vendor_recv_action[act] = f;
return 0;
}
return EINVAL;
#undef N
}
void
ieee80211_recv_action_unregister(int cat, int act)
{
ieee80211_recv_action_register(cat, act, recv_inval);
}
int
ieee80211_recv_action(struct ieee80211_node *ni,
const struct ieee80211_frame *wh,
const uint8_t *frm, const uint8_t *efrm)
{
#define N(a) (sizeof(a) / sizeof(a[0]))
ieee80211_recv_action_func *f = recv_inval;
const struct ieee80211_action *ia =
(const struct ieee80211_action *) frm;
switch (ia->ia_category) {
case IEEE80211_ACTION_CAT_BA:
if (ia->ia_action < N(ba_recv_action))
f = ba_recv_action[ia->ia_action];
break;
case IEEE80211_ACTION_CAT_HT:
if (ia->ia_action < N(ht_recv_action))
f = ht_recv_action[ia->ia_action];
break;
case IEEE80211_ACTION_CAT_MESHPEERING:
if (ia->ia_action < N(meshpl_recv_action))
f = meshpl_recv_action[ia->ia_action];
break;
case IEEE80211_ACTION_CAT_MESHLMETRIC:
if (ia->ia_action < N(meshlm_recv_action))
f = meshlm_recv_action[ia->ia_action];
break;
case IEEE80211_ACTION_CAT_MESHPATH:
if (ia->ia_action < N(hwmp_recv_action))
f = hwmp_recv_action[ia->ia_action];
break;
case IEEE80211_ACTION_CAT_VENDOR:
if (ia->ia_action < N(vendor_recv_action))
f = vendor_recv_action[ia->ia_action];
break;
}
return f(ni, wh, frm, efrm);
#undef N
}
@@ -0,0 +1,52 @@
/*-
* Copyright (c) 2009 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_ACTION_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_ACTION_H_
/*
* 802.11 send/recv action frame support.
*/
struct ieee80211_node;
struct ieee80211_frame;
typedef int ieee80211_send_action_func(struct ieee80211_node *,
int, int, void *);
int ieee80211_send_action_register(int cat, int act,
ieee80211_send_action_func *f);
void ieee80211_send_action_unregister(int cat, int act);
int ieee80211_send_action(struct ieee80211_node *, int, int, void *);
typedef int ieee80211_recv_action_func(struct ieee80211_node *,
const struct ieee80211_frame *, const uint8_t *, const uint8_t *);
int ieee80211_recv_action_register(int cat, int act,
ieee80211_recv_action_func *);
void ieee80211_recv_action_unregister(int cat, int act);
int ieee80211_recv_action(struct ieee80211_node *,
const struct ieee80211_frame *,
const uint8_t *, const uint8_t *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_ACTION_H_ */
@@ -0,0 +1,928 @@
/*-
* Copyright (c) 2007-2009 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.
*
* 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.
*/
#include <sys/cdefs.h>
#ifdef __FreeBSD__
__FBSDID("$FreeBSD$");
#endif
/*
* IEEE 802.11 IBSS mode support.
*/
#include "opt_inet.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/endian.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_llc.h>
#include <net/ethernet.h>
#include <net/bpf.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_adhoc.h>
#include <net80211/ieee80211_input.h>
#ifdef IEEE80211_SUPPORT_SUPERG
#include <net80211/ieee80211_superg.h>
#endif
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
static void adhoc_vattach(struct ieee80211vap *);
static int adhoc_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static int adhoc_input(struct ieee80211_node *, struct mbuf *, int, int);
static void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *,
int subtype, int, int);
static void ahdemo_recv_mgmt(struct ieee80211_node *, struct mbuf *,
int subtype, int, int);
static void adhoc_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype);
void
ieee80211_adhoc_attach(struct ieee80211com *ic)
{
ic->ic_vattach[IEEE80211_M_IBSS] = adhoc_vattach;
ic->ic_vattach[IEEE80211_M_AHDEMO] = adhoc_vattach;
}
void
ieee80211_adhoc_detach(struct ieee80211com *ic)
{
}
static void
adhoc_vdetach(struct ieee80211vap *vap)
{
}
static void
adhoc_vattach(struct ieee80211vap *vap)
{
vap->iv_newstate = adhoc_newstate;
vap->iv_input = adhoc_input;
if (vap->iv_opmode == IEEE80211_M_IBSS)
vap->iv_recv_mgmt = adhoc_recv_mgmt;
else
vap->iv_recv_mgmt = ahdemo_recv_mgmt;
vap->iv_recv_ctl = adhoc_recv_ctl;
vap->iv_opdetach = adhoc_vdetach;
#ifdef IEEE80211_SUPPORT_TDMA
/*
* Throw control to tdma support. Note we do this
* after setting up our callbacks so it can piggyback
* on top of us.
*/
if (vap->iv_caps & IEEE80211_C_TDMA)
ieee80211_tdma_vattach(vap);
#endif
}
static void
sta_leave(void *arg, struct ieee80211_node *ni)
{
struct ieee80211vap *vap = arg;
if (ni->ni_vap == vap && ni != vap->iv_bss)
ieee80211_node_leave(ni);
}
/*
* IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler.
*/
static int
adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
enum ieee80211_state ostate;
IEEE80211_LOCK_ASSERT(vap->iv_ic);
ostate = vap->iv_state;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
__func__, ieee80211_state_name[ostate],
ieee80211_state_name[nstate], arg);
vap->iv_state = nstate; /* state transition */
if (ostate != IEEE80211_S_SCAN)
ieee80211_cancel_scan(vap); /* background scan */
ni = vap->iv_bss; /* NB: no reference held */
switch (nstate) {
case IEEE80211_S_INIT:
switch (ostate) {
case IEEE80211_S_SCAN:
ieee80211_cancel_scan(vap);
break;
default:
break;
}
if (ostate != IEEE80211_S_INIT) {
/* NB: optimize INIT -> INIT case */
ieee80211_reset_bss(vap);
}
break;
case IEEE80211_S_SCAN:
switch (ostate) {
case IEEE80211_S_RUN: /* beacon miss */
/* purge station table; entries are stale */
ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap);
/* fall thru... */
case IEEE80211_S_INIT:
if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
!IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
/*
* Already have a channel; bypass the
* scan and startup immediately.
*/
ieee80211_create_ibss(vap, vap->iv_des_chan);
break;
}
/*
* Initiate a scan. We can come here as a result
* of an IEEE80211_IOC_SCAN_REQ too in which case
* the vap will be marked with IEEE80211_FEXT_SCANREQ
* and the scan request parameters will be present
* in iv_scanreq. Otherwise we do the default.
*/
if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
ieee80211_check_scan(vap,
vap->iv_scanreq_flags,
vap->iv_scanreq_duration,
vap->iv_scanreq_mindwell,
vap->iv_scanreq_maxdwell,
vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
} else
ieee80211_check_scan_current(vap);
break;
case IEEE80211_S_SCAN:
/*
* This can happen because of a change in state
* that requires a reset. Trigger a new scan
* unless we're in manual roaming mode in which
* case an application must issue an explicit request.
*/
if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
ieee80211_check_scan_current(vap);
break;
default:
goto invalid;
}
break;
case IEEE80211_S_RUN:
if (vap->iv_flags & IEEE80211_F_WPA) {
/* XXX validate prerequisites */
}
switch (ostate) {
case IEEE80211_S_SCAN:
#ifdef IEEE80211_DEBUG
if (ieee80211_msg_debug(vap)) {
ieee80211_note(vap,
"synchronized with %s ssid ",
ether_sprintf(ni->ni_bssid));
ieee80211_print_essid(vap->iv_bss->ni_essid,
ni->ni_esslen);
/* XXX MCS/HT */
printf(" channel %d start %uMb\n",
ieee80211_chan2ieee(ic, ic->ic_curchan),
IEEE80211_RATE2MBS(ni->ni_txrate));
}
#endif
break;
default:
goto invalid;
}
/*
* When 802.1x is not in use mark the port authorized
* at this point so traffic can flow.
*/
if (ni->ni_authmode != IEEE80211_AUTH_8021X)
ieee80211_node_authorize(ni);
/*
* Fake association when joining an existing bss.
*/
if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, vap->iv_myaddr) &&
ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, ostate != IEEE80211_S_RUN);
break;
case IEEE80211_S_SLEEP:
ieee80211_sta_pwrsave(vap, 0);
break;
default:
invalid:
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: unexpected state transition %s -> %s\n", __func__,
ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
break;
}
return 0;
}
/*
* Decide if a received management frame should be
* printed when debugging is enabled. This filters some
* of the less interesting frames that come frequently
* (e.g. beacons).
*/
static __inline int
doprint(struct ieee80211vap *vap, int subtype)
{
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_BEACON:
return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
return 1;
}
return 1;
}
/*
* 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 iv_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.
*/
static int
adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
{
#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
#define HAS_SEQ(type) ((type & 0x4) == 0)
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ifnet *ifp = vap->iv_ifp;
struct ieee80211_frame *wh;
struct ieee80211_key *key;
struct ether_header *eh;
int hdrspace, need_tap;
uint8_t dir, type, subtype, qos;
uint8_t *bssid;
uint16_t rxseq;
if (m->m_flags & M_AMPDU_MPDU) {
/*
* Fastpath for A-MPDU reorder q resubmission. Frames
* w/ M_AMPDU_MPDU 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_MPDU flag and we can bypass most of
* the normal processing.
*/
wh = mtod(m, struct ieee80211_frame *);
type = IEEE80211_FC0_TYPE_DATA;
dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
subtype = IEEE80211_FC0_SUBTYPE_QOS;
hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
goto resubmit_ampdu;
}
KASSERT(ni != NULL, ("null node"));
ni->ni_inact = ni->ni_inact_reload;
need_tap = 1; /* mbuf need to be tapped. */
type = -1; /* undefined */
if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, NULL,
"too short (1): len %u", m->m_pkthdr.len);
vap->iv_stats.is_rx_tooshort++;
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.
*/
wh = mtod(m, struct ieee80211_frame *);
if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
IEEE80211_FC0_VERSION_0) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x",
wh->i_fc[0], wh->i_fc[1]);
vap->iv_stats.is_rx_badversion++;
goto err;
}
dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
if (dir != IEEE80211_FC1_DIR_NODS)
bssid = wh->i_addr1;
else if (type == IEEE80211_FC0_TYPE_CTL)
bssid = wh->i_addr1;
else {
if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_ANY, ni->ni_macaddr,
NULL, "too short (2): len %u",
m->m_pkthdr.len);
vap->iv_stats.is_rx_tooshort++;
goto out;
}
bssid = wh->i_addr3;
}
/*
* Validate the bssid.
*/
if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
!IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
/* not interested in */
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
bssid, NULL, "%s", "not to bss");
vap->iv_stats.is_rx_wrongbss++;
goto out;
}
/*
* Data frame, cons up a node when it doesn't
* exist. This should probably done after an ACL check.
*/
if (type == IEEE80211_FC0_TYPE_DATA &&
ni == vap->iv_bss &&
!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
/*
* Beware of frames that come in too early; we
* can receive broadcast frames and creating sta
* entries will blow up because there is no bss
* channel yet.
*/
if (vap->iv_state != IEEE80211_S_RUN) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, "data", "not in RUN state (%s)",
ieee80211_state_name[vap->iv_state]);
vap->iv_stats.is_rx_badstate++;
goto err;
}
/*
* Fake up a node for this newly
* discovered member of the IBSS.
*/
ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2);
if (ni == NULL) {
/* NB: stat kept for alloc failure */
goto err;
}
}
IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
ni->ni_noise = nf;
if (HAS_SEQ(type)) {
uint8_t tid = ieee80211_gettid(wh);
if (IEEE80211_QOS_HAS_SEQ(wh) &&
TID_TO_WME_AC(tid) >= WME_AC_VI)
ic->ic_wme.wme_hipri_traffic++;
rxseq = le16toh(*(uint16_t *)wh->i_seq);
if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
(wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
/* duplicate, discard */
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
bssid, "duplicate",
"seqno <%u,%u> fragno <%u,%u> tid %u",
rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
ni->ni_rxseqs[tid] >>
IEEE80211_SEQ_SEQ_SHIFT,
rxseq & IEEE80211_SEQ_FRAG_MASK,
ni->ni_rxseqs[tid] &
IEEE80211_SEQ_FRAG_MASK,
tid);
vap->iv_stats.is_rx_dup++;
IEEE80211_NODE_STAT(ni, rx_dup);
goto out;
}
ni->ni_rxseqs[tid] = rxseq;
}
}
switch (type) {
case IEEE80211_FC0_TYPE_DATA:
hdrspace = ieee80211_hdrspace(ic, wh);
if (m->m_len < hdrspace &&
(m = m_pullup(m, hdrspace)) == NULL) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, NULL,
"data too short: expecting %u", hdrspace);
vap->iv_stats.is_rx_tooshort++;
goto out; /* XXX */
}
if (dir != IEEE80211_FC1_DIR_NODS) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, "data", "incorrect dir 0x%x", dir);
vap->iv_stats.is_rx_wrongdir++;
goto out;
}
/* XXX no power-save support */
/*
* Handle 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.
*/
if ((m->m_flags & M_AMPDU) &&
ieee80211_ampdu_reorder(ni, m) != 0) {
m = NULL;
goto out;
}
resubmit_ampdu:
/*
* Handle privacy requirements. Note that we
* must not be preempted from here until after
* we (potentially) call ieee80211_crypto_demic;
* otherwise we may violate assumptions in the
* crypto cipher modules used to do delayed update
* of replay sequence numbers.
*/
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
/*
* Discard encrypted frames when privacy is off.
*/
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, "WEP", "%s", "PRIVACY off");
vap->iv_stats.is_rx_noprivacy++;
IEEE80211_NODE_STAT(ni, rx_noprivacy);
goto out;
}
key = ieee80211_crypto_decap(ni, m, hdrspace);
if (key == NULL) {
/* NB: stats+msgs handled in crypto_decap */
IEEE80211_NODE_STAT(ni, rx_wepfail);
goto out;
}
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
} else {
/* XXX M_WEP and IEEE80211_F_PRIVACY */
key = NULL;
}
/*
* Save QoS bits for use below--before we strip the header.
*/
if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
((struct ieee80211_qosframe *)wh)->i_qos[0];
} else
qos = 0;
/*
* Next up, any fragmentation.
*/
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
m = ieee80211_defrag(ni, m, hdrspace);
if (m == NULL) {
/* Fragment dropped or frame not complete yet */
goto out;
}
}
wh = NULL; /* no longer valid, catch any uses */
/*
* Next strip any MSDU crypto bits.
*/
if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
ni->ni_macaddr, "data", "%s", "demic error");
vap->iv_stats.is_rx_demicfail++;
IEEE80211_NODE_STAT(ni, rx_demicfail);
goto out;
}
/* copy to listener after decrypt */
if (ieee80211_radiotap_active_vap(vap))
ieee80211_radiotap_rx(vap, m);
need_tap = 0;
/*
* Finally, strip the 802.11 header.
*/
m = ieee80211_decap(vap, m, hdrspace);
if (m == NULL) {
/* XXX mask bit to check for both */
/* don't count Null data frames as errors */
if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
goto out;
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
ni->ni_macaddr, "data", "%s", "decap error");
vap->iv_stats.is_rx_decap++;
IEEE80211_NODE_STAT(ni, rx_decap);
goto err;
}
eh = mtod(m, struct ether_header *);
if (!ieee80211_node_is_authorized(ni)) {
/*
* Deny any non-PAE frames received prior to
* authorization. For open/shared-key
* authentication the port is mark authorized
* after authentication completes. For 802.1x
* the port is not marked authorized by the
* authenticator until the handshake has completed.
*/
if (eh->ether_type != htons(ETHERTYPE_PAE)) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
eh->ether_shost, "data",
"unauthorized port: ether type 0x%x len %u",
eh->ether_type, m->m_pkthdr.len);
vap->iv_stats.is_rx_unauth++;
IEEE80211_NODE_STAT(ni, rx_unauth);
goto err;
}
} else {
/*
* When denying unencrypted frames, discard
* any non-PAE frames received without encryption.
*/
if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
(key == NULL && (m->m_flags & M_WEP) == 0) &&
eh->ether_type != htons(ETHERTYPE_PAE)) {
/*
* Drop unencrypted frames.
*/
vap->iv_stats.is_rx_unencrypted++;
IEEE80211_NODE_STAT(ni, rx_unencrypted);
goto out;
}
}
/* XXX require HT? */
if (qos & IEEE80211_QOS_AMSDU) {
m = ieee80211_decap_amsdu(ni, m);
if (m == NULL)
return IEEE80211_FC0_TYPE_DATA;
} else {
#ifdef IEEE80211_SUPPORT_SUPERG
m = ieee80211_decap_fastframe(vap, ni, m);
if (m == NULL)
return IEEE80211_FC0_TYPE_DATA;
#endif
}
if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
else
ieee80211_deliver_data(vap, ni, m);
return IEEE80211_FC0_TYPE_DATA;
case IEEE80211_FC0_TYPE_MGT:
vap->iv_stats.is_rx_mgmt++;
IEEE80211_NODE_STAT(ni, rx_mgmt);
if (dir != IEEE80211_FC1_DIR_NODS) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, "data", "incorrect dir 0x%x", dir);
vap->iv_stats.is_rx_wrongdir++;
goto err;
}
if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "mgt", "too short: len %u",
m->m_pkthdr.len);
vap->iv_stats.is_rx_tooshort++;
goto out;
}
#ifdef IEEE80211_DEBUG
if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
ieee80211_msg_dumppkts(vap)) {
if_printf(ifp, "received %s from %s rssi %d\n",
ieee80211_mgt_subtype_name[subtype >>
IEEE80211_FC0_SUBTYPE_SHIFT],
ether_sprintf(wh->i_addr2), rssi);
}
#endif
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, NULL, "%s", "WEP set but not permitted");
vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
goto out;
}
vap->iv_recv_mgmt(ni, m, subtype, rssi, nf);
goto out;
case IEEE80211_FC0_TYPE_CTL:
vap->iv_stats.is_rx_ctl++;
IEEE80211_NODE_STAT(ni, rx_ctrl);
vap->iv_recv_ctl(ni, m, subtype);
goto out;
default:
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
wh, "bad", "frame type 0x%x", type);
/* should not come here */
break;
}
err:
ifp->if_ierrors++;
out:
if (m != NULL) {
if (need_tap && ieee80211_radiotap_active_vap(vap))
ieee80211_radiotap_rx(vap, m);
m_freem(m);
}
return type;
#undef SEQ_LEQ
}
static int
is11bclient(const uint8_t *rates, const uint8_t *xrates)
{
static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
int i;
/* NB: the 11b clients we care about will not have xrates */
if (xrates != NULL || rates == NULL)
return 0;
for (i = 0; i < rates[1]; i++) {
int r = rates[2+i] & IEEE80211_RATE_VAL;
if (r > 2*11 || ((1<<r) & brates) == 0)
return 0;
}
return 1;
}
static void
adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
int subtype, int rssi, int nf)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
uint8_t *frm, *efrm, *sfrm;
uint8_t *ssid, *rates, *xrates;
wh = mtod(m0, struct ieee80211_frame *);
frm = (uint8_t *)&wh[1];
efrm = mtod(m0, uint8_t *) + m0->m_len;
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
case IEEE80211_FC0_SUBTYPE_BEACON: {
struct ieee80211_scanparams scan;
/*
* We process beacon/probe response
* frames to discover neighbors.
*/
if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
return;
/*
* Count frame now that we know it's to be processed.
*/
if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
vap->iv_stats.is_rx_beacon++; /* XXX remove */
IEEE80211_NODE_STAT(ni, rx_beacons);
} else
IEEE80211_NODE_STAT(ni, rx_proberesp);
/*
* If scanning, just pass information to the scan module.
*/
if (ic->ic_flags & IEEE80211_F_SCAN) {
if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
/*
* Actively scanning a channel marked passive;
* send a probe request now that we know there
* is 802.11 traffic present.
*
* XXX check if the beacon we recv'd gives
* us what we need and suppress the probe req
*/
ieee80211_probe_curchan(vap, 1);
ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
}
ieee80211_add_scan(vap, &scan, wh, subtype, rssi, nf);
return;
}
if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
/*
* Create a new entry in the neighbor table.
*/
ni = ieee80211_add_neighbor(vap, wh, &scan);
} else if (ni->ni_capinfo == 0) {
/*
* Update faked node created on transmit.
* Note this also updates the tsf.
*/
ieee80211_init_neighbor(ni, wh, &scan);
} else {
/*
* Record tsf for potential resync.
*/
memcpy(ni->ni_tstamp.data, scan.tstamp,
sizeof(ni->ni_tstamp));
}
if (ni != NULL) {
IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
ni->ni_noise = nf;
}
}
break;
}
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
if (vap->iv_state != IEEE80211_S_RUN) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, NULL, "wrong state %s",
ieee80211_state_name[vap->iv_state]);
vap->iv_stats.is_rx_mgtdiscard++;
return;
}
if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
/* frame must be directed */
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, NULL, "%s", "not unicast");
vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */
return;
}
/*
* prreq frame format
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
*/
ssid = rates = xrates = NULL;
sfrm = frm;
while (efrm - frm > 1) {
IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
switch (*frm) {
case IEEE80211_ELEMID_SSID:
ssid = frm;
break;
case IEEE80211_ELEMID_RATES:
rates = frm;
break;
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
}
frm += frm[1] + 2;
}
IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
if (xrates != NULL)
IEEE80211_VERIFY_ELEMENT(xrates,
IEEE80211_RATE_MAXSIZE - rates[1], return);
IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, NULL,
"%s", "no ssid with ssid suppression enabled");
vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
return;
}
/* XXX find a better class or define it's own */
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
"%s", "recv probe req");
/*
* Some legacy 11b clients cannot hack a complete
* probe response frame. When the request includes
* only a bare-bones rate set, communicate this to
* the transmit side.
*/
ieee80211_send_proberesp(vap, wh->i_addr2,
is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
break;
case IEEE80211_FC0_SUBTYPE_ACTION: {
const struct ieee80211_action *ia;
if (vap->iv_state != IEEE80211_S_RUN) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, NULL, "wrong state %s",
ieee80211_state_name[vap->iv_state]);
vap->iv_stats.is_rx_mgtdiscard++;
return;
}
/*
* action frame format:
* [1] category
* [1] action
* [tlv] parameters
*/
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action), return);
ia = (const struct ieee80211_action *) frm;
vap->iv_stats.is_rx_action++;
IEEE80211_NODE_STAT(ni, rx_action);
/* verify frame payloads but defer processing */
/* XXX maybe push this to method */
switch (ia->ia_category) {
case IEEE80211_ACTION_CAT_BA:
switch (ia->ia_action) {
case IEEE80211_ACTION_BA_ADDBA_REQUEST:
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action_ba_addbarequest),
return);
break;
case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action_ba_addbaresponse),
return);
break;
case IEEE80211_ACTION_BA_DELBA:
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action_ba_delba),
return);
break;
}
break;
case IEEE80211_ACTION_CAT_HT:
switch (ia->ia_action) {
case IEEE80211_ACTION_HT_TXCHWIDTH:
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action_ht_txchwidth),
return);
break;
}
break;
}
ic->ic_recv_action(ni, wh, frm, efrm);
break;
}
case IEEE80211_FC0_SUBTYPE_AUTH:
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_DEAUTH:
case IEEE80211_FC0_SUBTYPE_DISASSOC:
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, NULL, "%s", "not handled");
vap->iv_stats.is_rx_mgtdiscard++;
return;
default:
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
wh, "mgt", "subtype 0x%x not handled", subtype);
vap->iv_stats.is_rx_badsubtype++;
break;
}
}
#undef IEEE80211_VERIFY_LENGTH
#undef IEEE80211_VERIFY_ELEMENT
static void
ahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
int subtype, int rssi, int nf)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
/*
* Process management frames when scanning; useful for doing
* a site-survey.
*/
if (ic->ic_flags & IEEE80211_F_SCAN)
adhoc_recv_mgmt(ni, m0, subtype, rssi, nf);
else
vap->iv_stats.is_rx_mgtdiscard++;
}
static void
adhoc_recv_ctl(struct ieee80211_node *ni, struct mbuf *m0, int subtype)
{
}
@@ -0,0 +1,35 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_ADHOC_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_ADHOC_H_
/*
* Adhoc-mode (ibss+ahdemo) implementation definitions.
*/
void ieee80211_adhoc_attach(struct ieee80211com *);
void ieee80211_adhoc_detach(struct ieee80211com *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_STA_H_ */
@@ -0,0 +1,237 @@
/*-
* Copyright (c) 2009 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.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11 age queue support.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
/*
* Initialize an ageq.
*/
void
ieee80211_ageq_init(struct ieee80211_ageq *aq, int maxlen, const char *name)
{
memset(aq, 0, sizeof(aq));
aq->aq_maxlen = maxlen;
IEEE80211_AGEQ_INIT(aq, name); /* OS-dependent setup */
}
/*
* Cleanup an ageq initialized with ieee80211_ageq_init. Note
* the queue is assumed empty; this can be done with ieee80211_ageq_drain.
*/
void
ieee80211_ageq_cleanup(struct ieee80211_ageq *aq)
{
KASSERT(aq->aq_len == 0, ("%d frames on ageq", aq->aq_len));
IEEE80211_AGEQ_DESTROY(aq); /* OS-dependent cleanup */
}
/*
* Free an mbuf according to ageq rules: if marked as holding
* and 802.11 frame then also reclaim a node reference from
* the packet header; this handles packets q'd in the tx path.
*/
static void
ageq_mfree(struct mbuf *m)
{
if (m->m_flags & M_ENCAP) {
struct ieee80211_node *ni = (void *) m->m_pkthdr.rcvif;
ieee80211_free_node(ni);
}
m->m_nextpkt = NULL;
m_freem(m);
}
/*
* Free a list of mbufs using ageq rules (see above).
*/
void
ieee80211_ageq_mfree(struct mbuf *m)
{
struct mbuf *next;
for (; m != NULL; m = next) {
next = m->m_nextpkt;
ageq_mfree(m);
}
}
/*
* Append an mbuf to the ageq and mark it with the specified max age
* If the frame is not removed before the age (in seconds) expires
* then it is reclaimed (along with any node reference).
*/
int
ieee80211_ageq_append(struct ieee80211_ageq *aq, struct mbuf *m, int age)
{
IEEE80211_AGEQ_LOCK(aq);
if (__predict_true(aq->aq_len < aq->aq_maxlen)) {
if (aq->aq_tail == NULL) {
aq->aq_head = m;
} else {
aq->aq_tail->m_nextpkt = m;
age -= M_AGE_GET(aq->aq_head);
}
KASSERT(age >= 0, ("age %d", age));
M_AGE_SET(m, age);
m->m_nextpkt = NULL;
aq->aq_tail = m;
aq->aq_len++;
IEEE80211_AGEQ_UNLOCK(aq);
return 0;
} else {
/*
* No space, drop and cleanup references.
*/
aq->aq_drops++;
IEEE80211_AGEQ_UNLOCK(aq);
/* XXX tail drop? */
ageq_mfree(m);
return ENOSPC;
}
}
/*
* Drain/reclaim all frames from an ageq.
*/
void
ieee80211_ageq_drain(struct ieee80211_ageq *aq)
{
ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, NULL));
}
/*
* Drain/reclaim frames associated with a specific node from an ageq.
*/
void
ieee80211_ageq_drain_node(struct ieee80211_ageq *aq,
struct ieee80211_node *ni)
{
ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, ni));
}
/*
* Age frames on the age queue. Ages are stored as time
* deltas (in seconds) relative to the head so we can check
* and/or adjust only the head of the list. If a frame's age
* exceeds the time quanta then remove it. The list of removed
* frames is is returned to the caller joined by m_nextpkt.
*/
struct mbuf *
ieee80211_ageq_age(struct ieee80211_ageq *aq, int quanta)
{
struct mbuf *head, **phead;
struct mbuf *m;
phead = &head;
if (aq->aq_len != 0) {
IEEE80211_AGEQ_LOCK(aq);
while ((m = aq->aq_head) != NULL && M_AGE_GET(m) < quanta) {
if ((aq->aq_head = m->m_nextpkt) == NULL)
aq->aq_tail = NULL;
KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len));
aq->aq_len--;
/* add to private list for return */
*phead = m;
phead = &m->m_nextpkt;
}
if (m != NULL)
M_AGE_SUB(m, quanta);
IEEE80211_AGEQ_UNLOCK(aq);
}
*phead = NULL;
return head;
}
/*
* Remove all frames matching the specified node identifier
* (NULL matches all). Frames are returned as a list joined
* by m_nextpkt.
*/
struct mbuf *
ieee80211_ageq_remove(struct ieee80211_ageq *aq,
struct ieee80211_node *match)
{
struct mbuf *m, **prev, *ohead;
struct mbuf *head, **phead;
IEEE80211_AGEQ_LOCK(aq);
ohead = aq->aq_head;
prev = &aq->aq_head;
phead = &head;
while ((m = *prev) != NULL) {
if (match != NULL && m->m_pkthdr.rcvif != (void *) match) {
prev = &m->m_nextpkt;
continue;
}
/*
* Adjust q length.
*/
KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len));
aq->aq_len--;
/*
* Remove from forward list; tail pointer is harder.
*/
if (aq->aq_tail == m) {
KASSERT(m->m_nextpkt == NULL, ("not last"));
if (aq->aq_head == m) { /* list empty */
KASSERT(aq->aq_len == 0,
("not empty, len %d", aq->aq_len));
aq->aq_tail = NULL;
} else { /* must be one before */
aq->aq_tail = (struct mbuf *)((uintptr_t)prev -
offsetof(struct mbuf, m_nextpkt));
}
}
*prev = m->m_nextpkt;
/* add to private list for return */
*phead = m;
phead = &m->m_nextpkt;
}
if (head == ohead && aq->aq_head != NULL) /* correct age */
M_AGE_SET(aq->aq_head, M_AGE_GET(head));
IEEE80211_AGEQ_UNLOCK(aq);
*phead = NULL;
return head;
}
@@ -0,0 +1,54 @@
/*-
* Copyright (c) 2009 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_STAGEQ_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_STAGEQ_H_
struct ieee80211_node;
struct mbuf;
struct ieee80211_ageq {
ieee80211_ageq_lock_t aq_lock;
int aq_len; /* # items on queue */
int aq_maxlen; /* max queue length */
int aq_drops; /* frames dropped */
struct mbuf *aq_head; /* frames linked w/ m_nextpkt */
struct mbuf *aq_tail; /* last frame in queue */
};
void ieee80211_ageq_init(struct ieee80211_ageq *, int maxlen,
const char *name);
void ieee80211_ageq_cleanup(struct ieee80211_ageq *);
void ieee80211_ageq_mfree(struct mbuf *);
int ieee80211_ageq_append(struct ieee80211_ageq *, struct mbuf *,
int age);
void ieee80211_ageq_drain(struct ieee80211_ageq *);
void ieee80211_ageq_drain_node(struct ieee80211_ageq *,
struct ieee80211_node *);
struct mbuf *ieee80211_ageq_age(struct ieee80211_ageq *, int quanta);
struct mbuf *ieee80211_ageq_remove(struct ieee80211_ageq *,
struct ieee80211_node *match);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_STAGEQ_H_ */
@@ -0,0 +1,226 @@
/* $OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $ */
/*-
* Copyright (c) 2006
* Damien Bergamini <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*-
* Naive implementation of the Adaptive Multi Rate Retry algorithm:
*
* "IEEE 802.11 Rate Adaptation: A Practical Approach"
* Mathieu Lacage, Hossein Manshaei, Thierry Turletti
* INRIA Sophia - Projet Planete
* http://www-sop.inria.fr/rapports/sophia/RR-5208.html
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_media.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_amrr.h>
#define is_success(amn) \
((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
#define is_failure(amn) \
((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
#define is_enough(amn) \
((amn)->amn_txcnt > 10)
static void amrr_sysctlattach(struct ieee80211_amrr *amrr,
struct sysctl_ctx_list *ctx, struct sysctl_oid *tree);
/* number of references from net80211 layer */
static int nrefs = 0;
void
ieee80211_amrr_setinterval(struct ieee80211_amrr *amrr, int msecs)
{
int t;
if (msecs < 100)
msecs = 100;
t = msecs_to_ticks(msecs);
amrr->amrr_interval = (t < 1) ? 1 : t;
}
void
ieee80211_amrr_init(struct ieee80211_amrr *amrr,
struct ieee80211vap *vap, int amin, int amax, int interval)
{
/* XXX bounds check? */
amrr->amrr_min_success_threshold = amin;
amrr->amrr_max_success_threshold = amax;
ieee80211_amrr_setinterval(amrr, interval);
amrr_sysctlattach(amrr, vap->iv_sysctl, vap->iv_oid);
}
void
ieee80211_amrr_cleanup(struct ieee80211_amrr *amrr)
{
}
void
ieee80211_amrr_node_init(struct ieee80211_amrr *amrr,
struct ieee80211_amrr_node *amn, struct ieee80211_node *ni)
{
const struct ieee80211_rateset *rs = &ni->ni_rates;
amn->amn_amrr = amrr;
amn->amn_success = 0;
amn->amn_recovery = 0;
amn->amn_txcnt = amn->amn_retrycnt = 0;
amn->amn_success_threshold = amrr->amrr_min_success_threshold;
/* pick initial rate */
for (amn->amn_rix = rs->rs_nrates - 1;
amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72;
amn->amn_rix--)
;
ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
amn->amn_ticks = ticks;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
"AMRR initial rate %d", ni->ni_txrate);
}
static int
amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
struct ieee80211_node *ni)
{
int rix = amn->amn_rix;
KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
if (is_success(amn)) {
amn->amn_success++;
if (amn->amn_success >= amn->amn_success_threshold &&
rix + 1 < ni->ni_rates.rs_nrates) {
amn->amn_recovery = 1;
amn->amn_success = 0;
rix++;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
"AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
} else {
amn->amn_recovery = 0;
}
} else if (is_failure(amn)) {
amn->amn_success = 0;
if (rix > 0) {
if (amn->amn_recovery) {
amn->amn_success_threshold *= 2;
if (amn->amn_success_threshold >
amrr->amrr_max_success_threshold)
amn->amn_success_threshold =
amrr->amrr_max_success_threshold;
} else {
amn->amn_success_threshold =
amrr->amrr_min_success_threshold;
}
rix--;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
"AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
}
amn->amn_recovery = 0;
}
/* reset counters */
amn->amn_txcnt = 0;
amn->amn_retrycnt = 0;
return rix;
}
/*
* Return the rate index to use in sending a data frame.
* Update our internal state if it's been long enough.
* If the rate changes we also update ni_txrate to match.
*/
int
ieee80211_amrr_choose(struct ieee80211_node *ni,
struct ieee80211_amrr_node *amn)
{
struct ieee80211_amrr *amrr = amn->amn_amrr;
int rix;
if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
rix = amrr_update(amrr, amn, ni);
if (rix != amn->amn_rix) {
/* update public rate */
ni->ni_txrate =
ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
amn->amn_rix = rix;
}
amn->amn_ticks = ticks;
} else
rix = amn->amn_rix;
return rix;
}
static int
amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
{
struct ieee80211_amrr *amrr = arg1;
int msecs = ticks_to_msecs(amrr->amrr_interval);
int error;
error = sysctl_handle_int(oidp, &msecs, 0, req);
if (error || !req->newptr)
return error;
ieee80211_amrr_setinterval(amrr, msecs);
return 0;
}
static void
amrr_sysctlattach(struct ieee80211_amrr *amrr,
struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
{
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, amrr,
0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
/* XXX bounds check values */
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"amrr_max_sucess_threshold", CTLFLAG_RW,
&amrr->amrr_max_success_threshold, 0, "");
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"amrr_min_sucess_threshold", CTLFLAG_RW,
&amrr->amrr_min_success_threshold, 0, "");
}
/*
* Module glue.
*/
IEEE80211_RATE_MODULE(amrr, 1);
@@ -0,0 +1,101 @@
/* $FreeBSD$ */
/* $OpenBSD: ieee80211_amrr.h,v 1.3 2006/06/17 19:34:31 damien Exp $ */
/*-
* Copyright (c) 2006
* Damien Bergamini <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_AMRR_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_AMRR_H_
/*-
* Naive implementation of the Adaptive Multi Rate Retry algorithm:
*
* "IEEE 802.11 Rate Adaptation: A Practical Approach"
* Mathieu Lacage, Hossein Manshaei, Thierry Turletti
* INRIA Sophia - Projet Planete
* http://www-sop.inria.fr/rapports/sophia/RR-5208.html
*/
/*
* Rate control settings.
*/
struct ieee80211vap;
struct ieee80211_amrr {
u_int amrr_min_success_threshold;
u_int amrr_max_success_threshold;
int amrr_interval; /* update interval (ticks) */
};
#define IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD 1
#define IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD 15
/*
* Rate control state for a given node.
*/
struct ieee80211_amrr_node {
struct ieee80211_amrr *amn_amrr;/* backpointer */
int amn_rix; /* current rate index */
int amn_ticks; /* time of last update */
/* statistics */
u_int amn_txcnt;
u_int amn_success;
u_int amn_success_threshold;
u_int amn_recovery;
u_int amn_retrycnt;
};
void ieee80211_amrr_init(struct ieee80211_amrr *, struct ieee80211vap *,
int, int, int);
void ieee80211_amrr_cleanup(struct ieee80211_amrr *);
void ieee80211_amrr_setinterval(struct ieee80211_amrr *, int);
void ieee80211_amrr_node_init(struct ieee80211_amrr *,
struct ieee80211_amrr_node *, struct ieee80211_node *);
int ieee80211_amrr_choose(struct ieee80211_node *,
struct ieee80211_amrr_node *);
#define IEEE80211_AMRR_SUCCESS 1
#define IEEE80211_AMRR_FAILURE 0
/*
* Update statistics with tx complete status. Ok is non-zero
* if the packet is known to be ACK'd. Retries has the number
* retransmissions (i.e. xmit attempts - 1).
*/
static __inline void
ieee80211_amrr_tx_complete(struct ieee80211_amrr_node *amn,
int ok, int retries)
{
amn->amn_txcnt++;
if (ok)
amn->amn_success++;
amn->amn_retrycnt += retries;
}
/*
* Set tx count/retry statistics explicitly. Intended for
* drivers that poll the device for statistics maintained
* in the device.
*/
static __inline void
ieee80211_amrr_tx_update(struct ieee80211_amrr_node *amn,
int txcnt, int success, int retrycnt)
{
amn->amn_txcnt = txcnt;
amn->amn_success = success;
amn->amn_retrycnt = retrycnt;
}
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_AMRR_H_ */
@@ -0,0 +1,659 @@
/*-
* 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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11 generic crypto support.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h> /* XXX ETHER_HDR_LEN */
#include <net80211/ieee80211_var.h>
static int _ieee80211_crypto_delkey(struct ieee80211vap *,
struct ieee80211_key *);
/*
* Table of registered cipher modules.
*/
static const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX];
/*
* Default "null" key management routines.
*/
static int
null_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
if (!(&vap->iv_nw_keys[0] <= k &&
k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
/*
* Not in the global key table, the driver should handle this
* by allocating a slot in the h/w key table/cache. In
* lieu of that return key slot 0 for any unicast key
* request. We disallow the request if this is a group key.
* This default policy does the right thing for legacy hardware
* with a 4 key table. It also handles devices that pass
* packets through untouched when marked with the WEP bit
* and key index 0.
*/
if (k->wk_flags & IEEE80211_KEY_GROUP)
return 0;
*keyix = 0; /* NB: use key index 0 for ucast key */
} else {
*keyix = k - vap->iv_nw_keys;
}
*rxkeyix = IEEE80211_KEYIX_NONE; /* XXX maybe *keyix? */
return 1;
}
static int
null_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
return 1;
}
static int
null_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
const uint8_t mac[IEEE80211_ADDR_LEN])
{
return 1;
}
static void null_key_update(struct ieee80211vap *vap) {}
/*
* Write-arounds for common operations.
*/
static __inline void
cipher_detach(struct ieee80211_key *key)
{
key->wk_cipher->ic_detach(key);
}
static __inline void *
cipher_attach(struct ieee80211vap *vap, struct ieee80211_key *key)
{
return key->wk_cipher->ic_attach(vap, key);
}
/*
* Wrappers for driver key management methods.
*/
static __inline int
dev_key_alloc(struct ieee80211vap *vap,
struct ieee80211_key *key,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
return vap->iv_key_alloc(vap, key, keyix, rxkeyix);
}
static __inline int
dev_key_delete(struct ieee80211vap *vap,
const struct ieee80211_key *key)
{
return vap->iv_key_delete(vap, key);
}
static __inline int
dev_key_set(struct ieee80211vap *vap, const struct ieee80211_key *key)
{
return vap->iv_key_set(vap, key, key->wk_macaddr);
}
/*
* Setup crypto support for a device/shared instance.
*/
void
ieee80211_crypto_attach(struct ieee80211com *ic)
{
/* NB: we assume everything is pre-zero'd */
ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
}
/*
* Teardown crypto support.
*/
void
ieee80211_crypto_detach(struct ieee80211com *ic)
{
}
/*
* Setup crypto support for a vap.
*/
void
ieee80211_crypto_vattach(struct ieee80211vap *vap)
{
int i;
/* NB: we assume everything is pre-zero'd */
vap->iv_max_keyix = IEEE80211_WEP_NKID;
vap->iv_def_txkey = IEEE80211_KEYIX_NONE;
for (i = 0; i < IEEE80211_WEP_NKID; i++)
ieee80211_crypto_resetkey(vap, &vap->iv_nw_keys[i],
IEEE80211_KEYIX_NONE);
/*
* Initialize the driver key support routines to noop entries.
* This is useful especially for the cipher test modules.
*/
vap->iv_key_alloc = null_key_alloc;
vap->iv_key_set = null_key_set;
vap->iv_key_delete = null_key_delete;
vap->iv_key_update_begin = null_key_update;
vap->iv_key_update_end = null_key_update;
}
/*
* Teardown crypto support for a vap.
*/
void
ieee80211_crypto_vdetach(struct ieee80211vap *vap)
{
ieee80211_crypto_delglobalkeys(vap);
}
/*
* Register a crypto cipher module.
*/
void
ieee80211_crypto_register(const struct ieee80211_cipher *cip)
{
if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
printf("%s: cipher %s has an invalid cipher index %u\n",
__func__, cip->ic_name, cip->ic_cipher);
return;
}
if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
printf("%s: cipher %s registered with a different template\n",
__func__, cip->ic_name);
return;
}
ciphers[cip->ic_cipher] = cip;
}
/*
* Unregister a crypto cipher module.
*/
void
ieee80211_crypto_unregister(const struct ieee80211_cipher *cip)
{
if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
printf("%s: cipher %s has an invalid cipher index %u\n",
__func__, cip->ic_name, cip->ic_cipher);
return;
}
if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
printf("%s: cipher %s registered with a different template\n",
__func__, cip->ic_name);
return;
}
/* NB: don't complain about not being registered */
/* XXX disallow if references */
ciphers[cip->ic_cipher] = NULL;
}
int
ieee80211_crypto_available(u_int cipher)
{
return cipher < IEEE80211_CIPHER_MAX && ciphers[cipher] != NULL;
}
/* XXX well-known names! */
static const char *cipher_modnames[IEEE80211_CIPHER_MAX] = {
[IEEE80211_CIPHER_WEP] = "wlan_wep",
[IEEE80211_CIPHER_TKIP] = "wlan_tkip",
[IEEE80211_CIPHER_AES_OCB] = "wlan_aes_ocb",
[IEEE80211_CIPHER_AES_CCM] = "wlan_ccmp",
[IEEE80211_CIPHER_TKIPMIC] = "#4", /* NB: reserved */
[IEEE80211_CIPHER_CKIP] = "wlan_ckip",
[IEEE80211_CIPHER_NONE] = "wlan_none",
};
/* NB: there must be no overlap between user-supplied and device-owned flags */
CTASSERT((IEEE80211_KEY_COMMON & IEEE80211_KEY_DEVICE) == 0);
/*
* Establish a relationship between the specified key and cipher
* and, if necessary, allocate a hardware index from the driver.
* Note that when a fixed key index is required it must be specified.
*
* This must be the first call applied to a key; all the other key
* routines assume wk_cipher is setup.
*
* Locking must be handled by the caller using:
* ieee80211_key_update_begin(vap);
* ieee80211_key_update_end(vap);
*/
int
ieee80211_crypto_newkey(struct ieee80211vap *vap,
int cipher, int flags, struct ieee80211_key *key)
{
struct ieee80211com *ic = vap->iv_ic;
const struct ieee80211_cipher *cip;
ieee80211_keyix keyix, rxkeyix;
void *keyctx;
int oflags;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: cipher %u flags 0x%x keyix %u\n",
__func__, cipher, flags, key->wk_keyix);
/*
* Validate cipher and set reference to cipher routines.
*/
if (cipher >= IEEE80211_CIPHER_MAX) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: invalid cipher %u\n", __func__, cipher);
vap->iv_stats.is_crypto_badcipher++;
return 0;
}
cip = ciphers[cipher];
if (cip == NULL) {
/*
* Auto-load cipher module if we have a well-known name
* for it. It might be better to use string names rather
* than numbers and craft a module name based on the cipher
* name; e.g. wlan_cipher_<cipher-name>.
*/
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unregistered cipher %u, load module %s\n",
__func__, cipher, cipher_modnames[cipher]);
ieee80211_load_module(cipher_modnames[cipher]);
/*
* If cipher module loaded it should immediately
* call ieee80211_crypto_register which will fill
* in the entry in the ciphers array.
*/
cip = ciphers[cipher];
if (cip == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unable to load cipher %u, module %s\n",
__func__, cipher, cipher_modnames[cipher]);
vap->iv_stats.is_crypto_nocipher++;
return 0;
}
}
oflags = key->wk_flags;
flags &= IEEE80211_KEY_COMMON;
/* NB: preserve device attributes */
flags |= (oflags & IEEE80211_KEY_DEVICE);
/*
* If the hardware does not support the cipher then
* fallback to a host-based implementation.
*/
if ((ic->ic_cryptocaps & (1<<cipher)) == 0) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w support for cipher %s, falling back to s/w\n",
__func__, cip->ic_name);
flags |= IEEE80211_KEY_SWCRYPT;
}
/*
* Hardware TKIP with software MIC is an important
* combination; we handle it by flagging each key,
* the cipher modules honor it.
*/
if (cipher == IEEE80211_CIPHER_TKIP &&
(ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIPMIC) == 0) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w support for TKIP MIC, falling back to s/w\n",
__func__);
flags |= IEEE80211_KEY_SWMIC;
}
/*
* Bind cipher to key instance. Note we do this
* after checking the device capabilities so the
* cipher module can optimize space usage based on
* whether or not it needs to do the cipher work.
*/
if (key->wk_cipher != cip || key->wk_flags != flags) {
/*
* Fillin the flags so cipher modules can see s/w
* crypto requirements and potentially allocate
* different state and/or attach different method
* pointers.
*/
key->wk_flags = flags;
keyctx = cip->ic_attach(vap, key);
if (keyctx == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unable to attach cipher %s\n",
__func__, cip->ic_name);
key->wk_flags = oflags; /* restore old flags */
vap->iv_stats.is_crypto_attachfail++;
return 0;
}
cipher_detach(key);
key->wk_cipher = cip; /* XXX refcnt? */
key->wk_private = keyctx;
}
/*
* Ask the driver for a key index if we don't have one.
* Note that entries in the global key table always have
* an index; this means it's safe to call this routine
* for these entries just to setup the reference to the
* cipher template. Note also that when using software
* crypto we also call the driver to give us a key index.
*/
if ((key->wk_flags & IEEE80211_KEY_DEVKEY) == 0) {
if (!dev_key_alloc(vap, key, &keyix, &rxkeyix)) {
/*
* Unable to setup driver state.
*/
vap->iv_stats.is_crypto_keyfail++;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unable to setup cipher %s\n",
__func__, cip->ic_name);
return 0;
}
if (key->wk_flags != flags) {
/*
* Driver overrode flags we setup; typically because
* resources were unavailable to handle _this_ key.
* Re-attach the cipher context to allow cipher
* modules to handle differing requirements.
*/
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: driver override for cipher %s, flags "
"0x%x -> 0x%x\n", __func__, cip->ic_name,
oflags, key->wk_flags);
keyctx = cip->ic_attach(vap, key);
if (keyctx == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unable to attach cipher %s with "
"flags 0x%x\n", __func__, cip->ic_name,
key->wk_flags);
key->wk_flags = oflags; /* restore old flags */
vap->iv_stats.is_crypto_attachfail++;
return 0;
}
cipher_detach(key);
key->wk_cipher = cip; /* XXX refcnt? */
key->wk_private = keyctx;
}
key->wk_keyix = keyix;
key->wk_rxkeyix = rxkeyix;
key->wk_flags |= IEEE80211_KEY_DEVKEY;
}
return 1;
}
/*
* Remove the key (no locking, for internal use).
*/
static int
_ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key)
{
KASSERT(key->wk_cipher != NULL, ("No cipher!"));
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: %s keyix %u flags 0x%x rsc %ju tsc %ju len %u\n",
__func__, key->wk_cipher->ic_name,
key->wk_keyix, key->wk_flags,
key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc,
key->wk_keylen);
if (key->wk_flags & IEEE80211_KEY_DEVKEY) {
/*
* Remove hardware entry.
*/
/* XXX key cache */
if (!dev_key_delete(vap, key)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: driver did not delete key index %u\n",
__func__, key->wk_keyix);
vap->iv_stats.is_crypto_delkey++;
/* XXX recovery? */
}
}
cipher_detach(key);
memset(key, 0, sizeof(*key));
ieee80211_crypto_resetkey(vap, key, IEEE80211_KEYIX_NONE);
return 1;
}
/*
* Remove the specified key.
*/
int
ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key)
{
int status;
ieee80211_key_update_begin(vap);
status = _ieee80211_crypto_delkey(vap, key);
ieee80211_key_update_end(vap);
return status;
}
/*
* Clear the global key table.
*/
void
ieee80211_crypto_delglobalkeys(struct ieee80211vap *vap)
{
int i;
ieee80211_key_update_begin(vap);
for (i = 0; i < IEEE80211_WEP_NKID; i++)
(void) _ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[i]);
ieee80211_key_update_end(vap);
}
/*
* Set the contents of the specified key.
*
* Locking must be handled by the caller using:
* ieee80211_key_update_begin(vap);
* ieee80211_key_update_end(vap);
*/
int
ieee80211_crypto_setkey(struct ieee80211vap *vap, struct ieee80211_key *key)
{
const struct ieee80211_cipher *cip = key->wk_cipher;
KASSERT(cip != NULL, ("No cipher!"));
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: %s keyix %u flags 0x%x mac %s rsc %ju tsc %ju len %u\n",
__func__, cip->ic_name, key->wk_keyix,
key->wk_flags, ether_sprintf(key->wk_macaddr),
key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc,
key->wk_keylen);
if ((key->wk_flags & IEEE80211_KEY_DEVKEY) == 0) {
/* XXX nothing allocated, should not happen */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no device key setup done; should not happen!\n",
__func__);
vap->iv_stats.is_crypto_setkey_nokey++;
return 0;
}
/*
* Give cipher a chance to validate key contents.
* XXX should happen before modifying state.
*/
if (!cip->ic_setkey(key)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: cipher %s rejected key index %u len %u flags 0x%x\n",
__func__, cip->ic_name, key->wk_keyix,
key->wk_keylen, key->wk_flags);
vap->iv_stats.is_crypto_setkey_cipher++;
return 0;
}
return dev_key_set(vap, key);
}
/*
* Add privacy headers appropriate for the specified key.
*/
struct ieee80211_key *
ieee80211_crypto_encap(struct ieee80211_node *ni, struct mbuf *m)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_key *k;
struct ieee80211_frame *wh;
const struct ieee80211_cipher *cip;
uint8_t keyid;
/*
* Multicast traffic always uses the multicast key.
* Otherwise if a unicast key is set we use that and
* it is always key index 0. When no unicast key is
* set we fall back to the default transmit key.
*/
wh = mtod(m, struct ieee80211_frame *);
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE) {
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
wh->i_addr1,
"no default transmit key (%s) deftxkey %u",
__func__, vap->iv_def_txkey);
vap->iv_stats.is_tx_nodefkey++;
return NULL;
}
keyid = vap->iv_def_txkey;
k = &vap->iv_nw_keys[vap->iv_def_txkey];
} else {
keyid = 0;
k = &ni->ni_ucastkey;
}
cip = k->wk_cipher;
return (cip->ic_encap(k, m, keyid<<6) ? k : NULL);
}
/*
* Validate and strip privacy headers (and trailer) for a
* received frame that has the WEP/Privacy bit set.
*/
struct ieee80211_key *
ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
{
#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
#define IEEE80211_WEP_MINLEN \
(sizeof(struct ieee80211_frame) + \
IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN)
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_key *k;
struct ieee80211_frame *wh;
const struct ieee80211_cipher *cip;
uint8_t keyid;
/* NB: this minimum size data frame could be bigger */
if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
"%s: WEP data frame too short, len %u\n",
__func__, m->m_pkthdr.len);
vap->iv_stats.is_rx_tooshort++; /* XXX need unique stat? */
return NULL;
}
/*
* Locate the key. If unicast and there is no unicast
* key then we fall back to the key id in the header.
* This assumes unicast keys are only configured when
* the key id in the header is meaningless (typically 0).
*/
wh = mtod(m, struct ieee80211_frame *);
m_copydata(m, hdrlen + IEEE80211_WEP_IVLEN, sizeof(keyid), &keyid);
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey))
k = &vap->iv_nw_keys[keyid >> 6];
else
k = &ni->ni_ucastkey;
/*
* Insure crypto header is contiguous for all decap work.
*/
cip = k->wk_cipher;
if (m->m_len < hdrlen + cip->ic_header &&
(m = m_pullup(m, hdrlen + cip->ic_header)) == NULL) {
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"unable to pullup %s header", cip->ic_name);
vap->iv_stats.is_rx_wepfail++; /* XXX */
return NULL;
}
return (cip->ic_decap(k, m, hdrlen) ? k : NULL);
#undef IEEE80211_WEP_MINLEN
#undef IEEE80211_WEP_HDRLEN
}
static void
load_ucastkey(void *arg, struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_key *k;
if (vap->iv_state != IEEE80211_S_RUN)
return;
k = &ni->ni_ucastkey;
if (k->wk_flags & IEEE80211_KEY_DEVKEY)
dev_key_set(vap, k);
}
/*
* Re-load all keys known to the 802.11 layer that may
* have hardware state backing them. This is used by
* drivers on resume to push keys down into the device.
*/
void
ieee80211_crypto_reload_keys(struct ieee80211com *ic)
{
struct ieee80211vap *vap;
int i;
/*
* Keys in the global key table of each vap.
*/
/* NB: used only during resume so don't lock for now */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap->iv_state != IEEE80211_S_RUN)
continue;
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
const struct ieee80211_key *k = &vap->iv_nw_keys[i];
if (k->wk_flags & IEEE80211_KEY_DEVKEY)
dev_key_set(vap, k);
}
}
/*
* Unicast keys.
*/
ieee80211_iterate_nodes(&ic->ic_sta, load_ucastkey, NULL);
}
@@ -0,0 +1,243 @@
/*-
* 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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_CRYPTO_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_CRYPTO_H_
/*
* 802.11 protocol crypto-related definitions.
*/
#define IEEE80211_KEYBUF_SIZE 16
#define IEEE80211_MICBUF_SIZE (8+8) /* space for both tx+rx keys */
/*
* Old WEP-style key. Deprecated.
*/
struct ieee80211_wepkey {
u_int wk_len; /* key length in bytes */
uint8_t wk_key[IEEE80211_KEYBUF_SIZE];
};
struct ieee80211_rsnparms {
uint8_t rsn_mcastcipher; /* mcast/group cipher */
uint8_t rsn_mcastkeylen; /* mcast key length */
uint8_t rsn_ucastcipher; /* selected unicast cipher */
uint8_t rsn_ucastkeylen; /* unicast key length */
uint8_t rsn_keymgmt; /* selected key mgmt algo */
uint16_t rsn_caps; /* capabilities */
};
struct ieee80211_cipher;
/*
* Crypto key state. There is sufficient room for all supported
* ciphers (see below). The underlying ciphers are handled
* separately through loadable cipher modules that register with
* the generic crypto support. A key has a reference to an instance
* of the cipher; any per-key state is hung off wk_private by the
* cipher when it is attached. Ciphers are automatically called
* to detach and cleanup any such state when the key is deleted.
*
* The generic crypto support handles encap/decap of cipher-related
* frame contents for both hardware- and software-based implementations.
* A key requiring software crypto support is automatically flagged and
* the cipher is expected to honor this and do the necessary work.
* Ciphers such as TKIP may also support mixed hardware/software
* encrypt/decrypt and MIC processing.
*/
typedef uint16_t ieee80211_keyix; /* h/w key index */
struct ieee80211_key {
uint8_t wk_keylen; /* key length in bytes */
uint8_t wk_pad;
uint16_t wk_flags;
#define IEEE80211_KEY_XMIT 0x0001 /* key used for xmit */
#define IEEE80211_KEY_RECV 0x0002 /* key used for recv */
#define IEEE80211_KEY_GROUP 0x0004 /* key used for WPA group operation */
#define IEEE80211_KEY_SWENCRYPT 0x0010 /* host-based encrypt */
#define IEEE80211_KEY_SWDECRYPT 0x0020 /* host-based decrypt */
#define IEEE80211_KEY_SWENMIC 0x0040 /* host-based enmic */
#define IEEE80211_KEY_SWDEMIC 0x0080 /* host-based demic */
#define IEEE80211_KEY_DEVKEY 0x0100 /* device key request completed */
#define IEEE80211_KEY_CIPHER0 0x1000 /* cipher-specific action 0 */
#define IEEE80211_KEY_CIPHER1 0x2000 /* cipher-specific action 1 */
ieee80211_keyix wk_keyix; /* h/w key index */
ieee80211_keyix wk_rxkeyix; /* optional h/w rx key index */
uint8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
#define wk_txmic wk_key+IEEE80211_KEYBUF_SIZE+0 /* XXX can't () right */
#define wk_rxmic wk_key+IEEE80211_KEYBUF_SIZE+8 /* XXX can't () right */
/* key receive sequence counter */
uint64_t wk_keyrsc[IEEE80211_TID_SIZE];
uint64_t wk_keytsc; /* key transmit sequence counter */
const struct ieee80211_cipher *wk_cipher;
void *wk_private; /* private cipher state */
uint8_t wk_macaddr[IEEE80211_ADDR_LEN];
};
#define IEEE80211_KEY_COMMON /* common flags passed in by apps */\
(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP)
#define IEEE80211_KEY_DEVICE /* flags owned by device driver */\
(IEEE80211_KEY_DEVKEY|IEEE80211_KEY_CIPHER0|IEEE80211_KEY_CIPHER1)
#define IEEE80211_KEY_SWCRYPT \
(IEEE80211_KEY_SWENCRYPT | IEEE80211_KEY_SWDECRYPT)
#define IEEE80211_KEY_SWMIC (IEEE80211_KEY_SWENMIC | IEEE80211_KEY_SWDEMIC)
#define IEEE80211_KEY_BITS \
"\20\1XMIT\2RECV\3GROUP\4SWENCRYPT\5SWDECRYPT\6SWENMIC\7SWDEMIC" \
"\10DEVKEY\11CIPHER0\12CIPHER1"
#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1)
/*
* NB: these values are ordered carefully; there are lots of
* of implications in any reordering. Beware that 4 is used
* only to indicate h/w TKIP MIC support in driver capabilities;
* there is no separate cipher support (it's rolled into the
* TKIP cipher support).
*/
#define IEEE80211_CIPHER_WEP 0
#define IEEE80211_CIPHER_TKIP 1
#define IEEE80211_CIPHER_AES_OCB 2
#define IEEE80211_CIPHER_AES_CCM 3
#define IEEE80211_CIPHER_TKIPMIC 4 /* TKIP MIC capability */
#define IEEE80211_CIPHER_CKIP 5
#define IEEE80211_CIPHER_NONE 6 /* pseudo value */
#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1)
/* capability bits in ic_cryptocaps/iv_cryptocaps */
#define IEEE80211_CRYPTO_WEP (1<<IEEE80211_CIPHER_WEP)
#define IEEE80211_CRYPTO_TKIP (1<<IEEE80211_CIPHER_TKIP)
#define IEEE80211_CRYPTO_AES_OCB (1<<IEEE80211_CIPHER_AES_OCB)
#define IEEE80211_CRYPTO_AES_CCM (1<<IEEE80211_CIPHER_AES_CCM)
#define IEEE80211_CRYPTO_TKIPMIC (1<<IEEE80211_CIPHER_TKIPMIC)
#define IEEE80211_CRYPTO_CKIP (1<<IEEE80211_CIPHER_CKIP)
#define IEEE80211_CRYPTO_BITS \
"\20\1WEP\2TKIP\3AES\4AES_CCM\5TKIPMIC\6CKIP"
#if defined(__KERNEL__) || defined(_KERNEL)
struct ieee80211com;
struct ieee80211vap;
struct ieee80211_node;
struct mbuf;
void ieee80211_crypto_attach(struct ieee80211com *);
void ieee80211_crypto_detach(struct ieee80211com *);
void ieee80211_crypto_vattach(struct ieee80211vap *);
void ieee80211_crypto_vdetach(struct ieee80211vap *);
int ieee80211_crypto_newkey(struct ieee80211vap *,
int cipher, int flags, struct ieee80211_key *);
int ieee80211_crypto_delkey(struct ieee80211vap *,
struct ieee80211_key *);
int ieee80211_crypto_setkey(struct ieee80211vap *, struct ieee80211_key *);
void ieee80211_crypto_delglobalkeys(struct ieee80211vap *);
void ieee80211_crypto_reload_keys(struct ieee80211com *);
/*
* Template for a supported cipher. Ciphers register with the
* crypto code and are typically loaded as separate modules
* (the null cipher is always present).
* XXX may need refcnts
*/
struct ieee80211_cipher {
const char *ic_name; /* printable name */
u_int ic_cipher; /* IEEE80211_CIPHER_* */
u_int ic_header; /* size of privacy header (bytes) */
u_int ic_trailer; /* size of privacy trailer (bytes) */
u_int ic_miclen; /* size of mic trailer (bytes) */
void* (*ic_attach)(struct ieee80211vap *, struct ieee80211_key *);
void (*ic_detach)(struct ieee80211_key *);
int (*ic_setkey)(struct ieee80211_key *);
int (*ic_encap)(struct ieee80211_key *, struct mbuf *,
uint8_t keyid);
int (*ic_decap)(struct ieee80211_key *, struct mbuf *, int);
int (*ic_enmic)(struct ieee80211_key *, struct mbuf *, int);
int (*ic_demic)(struct ieee80211_key *, struct mbuf *, int);
};
extern const struct ieee80211_cipher ieee80211_cipher_none;
#define IEEE80211_KEY_UNDEFINED(k) \
((k)->wk_cipher == &ieee80211_cipher_none)
void ieee80211_crypto_register(const struct ieee80211_cipher *);
void ieee80211_crypto_unregister(const struct ieee80211_cipher *);
int ieee80211_crypto_available(u_int cipher);
struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211_node *,
struct mbuf *);
struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211_node *,
struct mbuf *, int);
/*
* Check and remove any MIC.
*/
static __inline int
ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k,
struct mbuf *m, int force)
{
const struct ieee80211_cipher *cip = k->wk_cipher;
return (cip->ic_miclen > 0 ? cip->ic_demic(k, m, force) : 1);
}
/*
* Add any MIC.
*/
static __inline int
ieee80211_crypto_enmic(struct ieee80211vap *vap,
struct ieee80211_key *k, struct mbuf *m, int force)
{
const struct ieee80211_cipher *cip = k->wk_cipher;
return (cip->ic_miclen > 0 ? cip->ic_enmic(k, m, force) : 1);
}
/*
* Reset key state to an unused state. The crypto
* key allocation mechanism insures other state (e.g.
* key data) is properly setup before a key is used.
*/
static __inline void
ieee80211_crypto_resetkey(struct ieee80211vap *vap,
struct ieee80211_key *k, ieee80211_keyix ix)
{
k->wk_cipher = &ieee80211_cipher_none;
k->wk_private = k->wk_cipher->ic_attach(vap, k);
k->wk_keyix = k->wk_rxkeyix = ix;
k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
}
/*
* Crypt-related notification methods.
*/
void ieee80211_notify_replay_failure(struct ieee80211vap *,
const struct ieee80211_frame *, const struct ieee80211_key *,
uint64_t rsc, int tid);
void ieee80211_notify_michael_failure(struct ieee80211vap *,
const struct ieee80211_frame *, u_int keyix);
#endif /* defined(__KERNEL__) || defined(_KERNEL) */
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_CRYPTO_H_ */
@@ -0,0 +1,634 @@
/*-
* 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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11i AES-CCMP crypto support.
*
* Part of this module is derived from similar code in the Host
* AP driver. The code is used with the consent of the author and
* it's license is included below.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <crypto/rijndael/rijndael.h>
#define AES_BLOCK_LEN 16
struct ccmp_ctx {
struct ieee80211vap *cc_vap; /* for diagnostics+statistics */
struct ieee80211com *cc_ic;
rijndael_ctx cc_aes;
};
static void *ccmp_attach(struct ieee80211vap *, struct ieee80211_key *);
static void ccmp_detach(struct ieee80211_key *);
static int ccmp_setkey(struct ieee80211_key *);
static int ccmp_encap(struct ieee80211_key *k, struct mbuf *, uint8_t keyid);
static int ccmp_decap(struct ieee80211_key *, struct mbuf *, int);
static int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int);
static int ccmp_demic(struct ieee80211_key *, struct mbuf *, int);
static const struct ieee80211_cipher ccmp = {
.ic_name = "AES-CCM",
.ic_cipher = IEEE80211_CIPHER_AES_CCM,
.ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
IEEE80211_WEP_EXTIVLEN,
.ic_trailer = IEEE80211_WEP_MICLEN,
.ic_miclen = 0,
.ic_attach = ccmp_attach,
.ic_detach = ccmp_detach,
.ic_setkey = ccmp_setkey,
.ic_encap = ccmp_encap,
.ic_decap = ccmp_decap,
.ic_enmic = ccmp_enmic,
.ic_demic = ccmp_demic,
};
static int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
static int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn,
struct mbuf *, int hdrlen);
/* number of references from net80211 layer */
static int nrefs = 0;
static void *
ccmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct ccmp_ctx *ctx;
ctx = (struct ccmp_ctx *) malloc(sizeof(struct ccmp_ctx),
M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
vap->iv_stats.is_crypto_nomem++;
return NULL;
}
ctx->cc_vap = vap;
ctx->cc_ic = vap->iv_ic;
nrefs++; /* NB: we assume caller locking */
return ctx;
}
static void
ccmp_detach(struct ieee80211_key *k)
{
struct ccmp_ctx *ctx = k->wk_private;
free(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
static int
ccmp_setkey(struct ieee80211_key *k)
{
struct ccmp_ctx *ctx = k->wk_private;
if (k->wk_keylen != (128/NBBY)) {
IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
"%s: Invalid key length %u, expecting %u\n",
__func__, k->wk_keylen, 128/NBBY);
return 0;
}
if (k->wk_flags & IEEE80211_KEY_SWENCRYPT)
rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY);
return 1;
}
/*
* Add privacy headers appropriate for the specified key.
*/
static int
ccmp_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct ccmp_ctx *ctx = k->wk_private;
struct ieee80211com *ic = ctx->cc_ic;
uint8_t *ivp;
int hdrlen;
hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
/*
* Copy down 802.11 header and add the IV, KeyID, and ExtIV.
*/
M_PREPEND(m, ccmp.ic_header, M_NOWAIT);
if (m == NULL)
return 0;
ivp = mtod(m, uint8_t *);
ovbcopy(ivp + ccmp.ic_header, ivp, hdrlen);
ivp += hdrlen;
k->wk_keytsc++; /* XXX wrap at 48 bits */
ivp[0] = k->wk_keytsc >> 0; /* PN0 */
ivp[1] = k->wk_keytsc >> 8; /* PN1 */
ivp[2] = 0; /* Reserved */
ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */
ivp[4] = k->wk_keytsc >> 16; /* PN2 */
ivp[5] = k->wk_keytsc >> 24; /* PN3 */
ivp[6] = k->wk_keytsc >> 32; /* PN4 */
ivp[7] = k->wk_keytsc >> 40; /* PN5 */
/*
* Finally, do software encrypt if neeed.
*/
if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&
!ccmp_encrypt(k, m, hdrlen))
return 0;
return 1;
}
/*
* Add MIC to the frame as needed.
*/
static int
ccmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
{
return 1;
}
static __inline uint64_t
READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
{
uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
uint16_t iv16 = (b4 << 0) | (b5 << 8);
return (((uint64_t)iv16) << 32) | iv32;
}
/*
* Validate and strip privacy headers (and trailer) for a
* received frame. The specified key should be correct but
* is also verified.
*/
static int
ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct ccmp_ctx *ctx = k->wk_private;
struct ieee80211vap *vap = ctx->cc_vap;
struct ieee80211_frame *wh;
uint8_t *ivp, tid;
uint64_t pn;
/*
* Header should have extended IV and sequence number;
* verify the former and validate the latter.
*/
wh = mtod(m, struct ieee80211_frame *);
ivp = mtod(m, uint8_t *) + hdrlen;
if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
/*
* No extended IV; discard frame.
*/
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"%s", "missing ExtIV for AES-CCM cipher");
vap->iv_stats.is_rx_ccmpformat++;
return 0;
}
tid = ieee80211_gettid(wh);
pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
if (pn <= k->wk_keyrsc[tid]) {
/*
* Replay violation.
*/
ieee80211_notify_replay_failure(vap, wh, k, pn, tid);
vap->iv_stats.is_rx_ccmpreplay++;
return 0;
}
/*
* Check if the device handled the decrypt in hardware.
* If so we just strip the header; otherwise we need to
* handle the decrypt in software. Note that for the
* latter we leave the header in place for use in the
* decryption work.
*/
if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) &&
!ccmp_decrypt(k, pn, m, hdrlen))
return 0;
/*
* Copy up 802.11 header and strip crypto bits.
*/
ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header, hdrlen);
m_adj(m, ccmp.ic_header);
m_adj(m, -ccmp.ic_trailer);
/*
* Ok to update rsc now.
*/
k->wk_keyrsc[tid] = pn;
return 1;
}
/*
* Verify and strip MIC from the frame.
*/
static int
ccmp_demic(struct ieee80211_key *k, struct mbuf *m, int force)
{
return 1;
}
static __inline void
xor_block(uint8_t *b, const uint8_t *a, size_t len)
{
int i;
for (i = 0; i < len; i++)
b[i] ^= a[i];
}
/*
* Host AP crypt: host-based CCMP encryption implementation for Host AP driver
*
* Copyright (c) 2003-2004, Jouni Malinen <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See README and COPYING for
* more details.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*/
static void
ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh,
u_int64_t pn, size_t dlen,
uint8_t b0[AES_BLOCK_LEN], uint8_t aad[2 * AES_BLOCK_LEN],
uint8_t auth[AES_BLOCK_LEN], uint8_t s0[AES_BLOCK_LEN])
{
#define IS_QOS_DATA(wh) IEEE80211_QOS_HAS_SEQ(wh)
/* CCM Initial Block:
* Flag (Include authentication header, M=3 (8-octet MIC),
* L=1 (2-octet Dlen))
* Nonce: 0x00 | A2 | PN
* Dlen */
b0[0] = 0x59;
/* NB: b0[1] set below */
IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2);
b0[8] = pn >> 40;
b0[9] = pn >> 32;
b0[10] = pn >> 24;
b0[11] = pn >> 16;
b0[12] = pn >> 8;
b0[13] = pn >> 0;
b0[14] = (dlen >> 8) & 0xff;
b0[15] = dlen & 0xff;
/* AAD:
* FC with bits 4..6 and 11..13 masked to zero; 14 is always one
* A1 | A2 | A3
* SC with bits 4..15 (seq#) masked to zero
* A4 (if present)
* QC (if present)
*/
aad[0] = 0; /* AAD length >> 8 */
/* NB: aad[1] set below */
aad[2] = wh->i_fc[0] & 0x8f; /* XXX magic #s */
aad[3] = wh->i_fc[1] & 0xc7; /* XXX magic #s */
/* NB: we know 3 addresses are contiguous */
memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
aad[23] = 0; /* all bits masked */
/*
* Construct variable-length portion of AAD based
* on whether this is a 4-address frame/QOS frame.
* We always zero-pad to 32 bytes before running it
* through the cipher.
*
* We also fill in the priority bits of the CCM
* initial block as we know whether or not we have
* a QOS frame.
*/
if (IEEE80211_IS_DSTODS(wh)) {
IEEE80211_ADDR_COPY(aad + 24,
((struct ieee80211_frame_addr4 *)wh)->i_addr4);
if (IS_QOS_DATA(wh)) {
struct ieee80211_qosframe_addr4 *qwh4 =
(struct ieee80211_qosframe_addr4 *) wh;
aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */
aad[31] = 0;
b0[1] = aad[30];
aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
} else {
*(uint16_t *)&aad[30] = 0;
b0[1] = 0;
aad[1] = 22 + IEEE80211_ADDR_LEN;
}
} else {
if (IS_QOS_DATA(wh)) {
struct ieee80211_qosframe *qwh =
(struct ieee80211_qosframe*) wh;
aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */
aad[25] = 0;
b0[1] = aad[24];
aad[1] = 22 + 2;
} else {
*(uint16_t *)&aad[24] = 0;
b0[1] = 0;
aad[1] = 22;
}
*(uint16_t *)&aad[26] = 0;
*(uint32_t *)&aad[28] = 0;
}
/* Start with the first block and AAD */
rijndael_encrypt(ctx, b0, auth);
xor_block(auth, aad, AES_BLOCK_LEN);
rijndael_encrypt(ctx, auth, auth);
xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
rijndael_encrypt(ctx, auth, auth);
b0[0] &= 0x07;
b0[14] = b0[15] = 0;
rijndael_encrypt(ctx, b0, s0);
#undef IS_QOS_DATA
}
#define CCMP_ENCRYPT(_i, _b, _b0, _pos, _e, _len) do { \
/* Authentication */ \
xor_block(_b, _pos, _len); \
rijndael_encrypt(&ctx->cc_aes, _b, _b); \
/* Encryption, with counter */ \
_b0[14] = (_i >> 8) & 0xff; \
_b0[15] = _i & 0xff; \
rijndael_encrypt(&ctx->cc_aes, _b0, _e); \
xor_block(_pos, _e, _len); \
} while (0)
static int
ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
{
struct ccmp_ctx *ctx = key->wk_private;
struct ieee80211_frame *wh;
struct mbuf *m = m0;
int data_len, i, space;
uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN],
e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN];
uint8_t *pos;
ctx->cc_vap->iv_stats.is_crypto_ccmp++;
wh = mtod(m, struct ieee80211_frame *);
data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header);
ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc,
data_len, b0, aad, b, s0);
i = 1;
pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header;
/* NB: assumes header is entirely in first mbuf */
space = m->m_len - (hdrlen + ccmp.ic_header);
for (;;) {
if (space > data_len)
space = data_len;
/*
* Do full blocks.
*/
while (space >= AES_BLOCK_LEN) {
CCMP_ENCRYPT(i, b, b0, pos, e, AES_BLOCK_LEN);
pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;
data_len -= AES_BLOCK_LEN;
i++;
}
if (data_len <= 0) /* no more data */
break;
m = m->m_next;
if (m == NULL) { /* last buffer */
if (space != 0) {
/*
* Short last block.
*/
CCMP_ENCRYPT(i, b, b0, pos, e, space);
}
break;
}
if (space != 0) {
uint8_t *pos_next;
int space_next;
int len, dl, sp;
struct mbuf *n;
/*
* Block straddles one or more mbufs, gather data
* into the block buffer b, apply the cipher, then
* scatter the results back into the mbuf chain.
* The buffer will automatically get space bytes
* of data at offset 0 copied in+out by the
* CCMP_ENCRYPT request so we must take care of
* the remaining data.
*/
n = m;
dl = data_len;
sp = space;
for (;;) {
pos_next = mtod(n, uint8_t *);
len = min(dl, AES_BLOCK_LEN);
space_next = len > sp ? len - sp : 0;
if (n->m_len >= space_next) {
/*
* This mbuf has enough data; just grab
* what we need and stop.
*/
xor_block(b+sp, pos_next, space_next);
break;
}
/*
* This mbuf's contents are insufficient,
* take 'em all and prepare to advance to
* the next mbuf.
*/
xor_block(b+sp, pos_next, n->m_len);
sp += n->m_len, dl -= n->m_len;
n = n->m_next;
if (n == NULL)
break;
}
CCMP_ENCRYPT(i, b, b0, pos, e, space);
/* NB: just like above, but scatter data to mbufs */
dl = data_len;
sp = space;
for (;;) {
pos_next = mtod(m, uint8_t *);
len = min(dl, AES_BLOCK_LEN);
space_next = len > sp ? len - sp : 0;
if (m->m_len >= space_next) {
xor_block(pos_next, e+sp, space_next);
break;
}
xor_block(pos_next, e+sp, m->m_len);
sp += m->m_len, dl -= m->m_len;
m = m->m_next;
if (m == NULL)
goto done;
}
/*
* Do bookkeeping. m now points to the last mbuf
* we grabbed data from. We know we consumed a
* full block of data as otherwise we'd have hit
* the end of the mbuf chain, so deduct from data_len.
* Otherwise advance the block number (i) and setup
* pos+space to reflect contents of the new mbuf.
*/
data_len -= AES_BLOCK_LEN;
i++;
pos = pos_next + space_next;
space = m->m_len - space_next;
} else {
/*
* Setup for next buffer.
*/
pos = mtod(m, uint8_t *);
space = m->m_len;
}
}
done:
/* tack on MIC */
xor_block(b, s0, ccmp.ic_trailer);
return m_append(m0, ccmp.ic_trailer, b);
}
#undef CCMP_ENCRYPT
#define CCMP_DECRYPT(_i, _b, _b0, _pos, _a, _len) do { \
/* Decrypt, with counter */ \
_b0[14] = (_i >> 8) & 0xff; \
_b0[15] = _i & 0xff; \
rijndael_encrypt(&ctx->cc_aes, _b0, _b); \
xor_block(_pos, _b, _len); \
/* Authentication */ \
xor_block(_a, _pos, _len); \
rijndael_encrypt(&ctx->cc_aes, _a, _a); \
} while (0)
static int
ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen)
{
struct ccmp_ctx *ctx = key->wk_private;
struct ieee80211vap *vap = ctx->cc_vap;
struct ieee80211_frame *wh;
uint8_t aad[2 * AES_BLOCK_LEN];
uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN];
uint8_t mic[AES_BLOCK_LEN];
size_t data_len;
int i;
uint8_t *pos;
u_int space;
ctx->cc_vap->iv_stats.is_crypto_ccmp++;
wh = mtod(m, struct ieee80211_frame *);
data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer);
ccmp_init_blocks(&ctx->cc_aes, wh, pn, data_len, b0, aad, a, b);
m_copydata(m, m->m_pkthdr.len - ccmp.ic_trailer, ccmp.ic_trailer, mic);
xor_block(mic, b, ccmp.ic_trailer);
i = 1;
pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header;
space = m->m_len - (hdrlen + ccmp.ic_header);
for (;;) {
if (space > data_len)
space = data_len;
while (space >= AES_BLOCK_LEN) {
CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN);
pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;
data_len -= AES_BLOCK_LEN;
i++;
}
if (data_len <= 0) /* no more data */
break;
m = m->m_next;
if (m == NULL) { /* last buffer */
if (space != 0) /* short last block */
CCMP_DECRYPT(i, b, b0, pos, a, space);
break;
}
if (space != 0) {
uint8_t *pos_next;
u_int space_next;
u_int len;
/*
* Block straddles buffers, split references. We
* do not handle splits that require >2 buffers
* since rx'd frames are never badly fragmented
* because drivers typically recv in clusters.
*/
pos_next = mtod(m, uint8_t *);
len = min(data_len, AES_BLOCK_LEN);
space_next = len > space ? len - space : 0;
KASSERT(m->m_len >= space_next,
("not enough data in following buffer, "
"m_len %u need %u\n", m->m_len, space_next));
xor_block(b+space, pos_next, space_next);
CCMP_DECRYPT(i, b, b0, pos, a, space);
xor_block(pos_next, b+space, space_next);
data_len -= len;
i++;
pos = pos_next + space_next;
space = m->m_len - space_next;
} else {
/*
* Setup for next buffer.
*/
pos = mtod(m, uint8_t *);
space = m->m_len;
}
}
if (memcmp(mic, a, ccmp.ic_trailer) != 0) {
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"%s", "AES-CCM decrypt failed; MIC mismatch");
vap->iv_stats.is_rx_ccmpmic++;
return 0;
}
return 1;
}
#undef CCMP_DECRYPT
/*
* Module glue.
*/
IEEE80211_CRYPTO_MODULE(ccmp, 1);
@@ -0,0 +1,144 @@
/*-
* 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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11 NULL crypto support.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
static void *none_attach(struct ieee80211vap *, struct ieee80211_key *);
static void none_detach(struct ieee80211_key *);
static int none_setkey(struct ieee80211_key *);
static int none_encap(struct ieee80211_key *, struct mbuf *, uint8_t);
static int none_decap(struct ieee80211_key *, struct mbuf *, int);
static int none_enmic(struct ieee80211_key *, struct mbuf *, int);
static int none_demic(struct ieee80211_key *, struct mbuf *, int);
const struct ieee80211_cipher ieee80211_cipher_none = {
.ic_name = "NONE",
.ic_cipher = IEEE80211_CIPHER_NONE,
.ic_header = 0,
.ic_trailer = 0,
.ic_miclen = 0,
.ic_attach = none_attach,
.ic_detach = none_detach,
.ic_setkey = none_setkey,
.ic_encap = none_encap,
.ic_decap = none_decap,
.ic_enmic = none_enmic,
.ic_demic = none_demic,
};
static void *
none_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
return vap; /* for diagnostics+stats */
}
static void
none_detach(struct ieee80211_key *k)
{
(void) k;
}
static int
none_setkey(struct ieee80211_key *k)
{
(void) k;
return 1;
}
static int
none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct ieee80211vap *vap = k->wk_private;
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
#endif
/*
* The specified key is not setup; this can
* happen, at least, when changing keys.
*/
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr1,
"key id %u is not set (encap)", keyid>>6);
vap->iv_stats.is_tx_badcipher++;
return 0;
}
static int
none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct ieee80211vap *vap = k->wk_private;
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
const uint8_t *ivp = (const uint8_t *)&wh[1];
#endif
/*
* The specified key is not setup; this can
* happen, at least, when changing keys.
*/
/* XXX useful to know dst too */
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"key id %u is not set (decap)", ivp[IEEE80211_WEP_IVLEN] >> 6);
vap->iv_stats.is_rx_badkeyid++;
return 0;
}
static int
none_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
{
struct ieee80211vap *vap = k->wk_private;
vap->iv_stats.is_tx_badcipher++;
return 0;
}
static int
none_demic(struct ieee80211_key *k, struct mbuf *m, int force)
{
struct ieee80211vap *vap = k->wk_private;
vap->iv_stats.is_rx_badkeyid++;
return 0;
}
@@ -0,0 +1,997 @@
/*-
* 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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11i TKIP crypto support.
*
* Part of this module is derived from similar code in the Host
* AP driver. The code is used with the consent of the author and
* it's license is included below.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
static void *tkip_attach(struct ieee80211vap *, struct ieee80211_key *);
static void tkip_detach(struct ieee80211_key *);
static int tkip_setkey(struct ieee80211_key *);
static int tkip_encap(struct ieee80211_key *, struct mbuf *m, uint8_t keyid);
static int tkip_enmic(struct ieee80211_key *, struct mbuf *, int);
static int tkip_decap(struct ieee80211_key *, struct mbuf *, int);
static int tkip_demic(struct ieee80211_key *, struct mbuf *, int);
static const struct ieee80211_cipher tkip = {
.ic_name = "TKIP",
.ic_cipher = IEEE80211_CIPHER_TKIP,
.ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
IEEE80211_WEP_EXTIVLEN,
.ic_trailer = IEEE80211_WEP_CRCLEN,
.ic_miclen = IEEE80211_WEP_MICLEN,
.ic_attach = tkip_attach,
.ic_detach = tkip_detach,
.ic_setkey = tkip_setkey,
.ic_encap = tkip_encap,
.ic_decap = tkip_decap,
.ic_enmic = tkip_enmic,
.ic_demic = tkip_demic,
};
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t __u32;
typedef uint32_t u32;
struct tkip_ctx {
struct ieee80211vap *tc_vap; /* for diagnostics+statistics */
u16 tx_ttak[5];
int tx_phase1_done;
u8 tx_rc4key[16]; /* XXX for test module; make locals? */
u16 rx_ttak[5];
int rx_phase1_done;
u8 rx_rc4key[16]; /* XXX for test module; make locals? */
uint64_t rx_rsc; /* held until MIC verified */
};
static void michael_mic(struct tkip_ctx *, const u8 *key,
struct mbuf *m, u_int off, size_t data_len,
u8 mic[IEEE80211_WEP_MICLEN]);
static int tkip_encrypt(struct tkip_ctx *, struct ieee80211_key *,
struct mbuf *, int hdr_len);
static int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *,
struct mbuf *, int hdr_len);
/* number of references from net80211 layer */
static int nrefs = 0;
static void *
tkip_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct tkip_ctx *ctx;
ctx = (struct tkip_ctx *) malloc(sizeof(struct tkip_ctx),
M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
vap->iv_stats.is_crypto_nomem++;
return NULL;
}
ctx->tc_vap = vap;
nrefs++; /* NB: we assume caller locking */
return ctx;
}
static void
tkip_detach(struct ieee80211_key *k)
{
struct tkip_ctx *ctx = k->wk_private;
free(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
static int
tkip_setkey(struct ieee80211_key *k)
{
struct tkip_ctx *ctx = k->wk_private;
if (k->wk_keylen != (128/NBBY)) {
(void) ctx; /* XXX */
IEEE80211_DPRINTF(ctx->tc_vap, IEEE80211_MSG_CRYPTO,
"%s: Invalid key length %u, expecting %u\n",
__func__, k->wk_keylen, 128/NBBY);
return 0;
}
k->wk_keytsc = 1; /* TSC starts at 1 */
return 1;
}
/*
* Add privacy headers and do any s/w encryption required.
*/
static int
tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct tkip_ctx *ctx = k->wk_private;
struct ieee80211vap *vap = ctx->tc_vap;
struct ieee80211com *ic = vap->iv_ic;
uint8_t *ivp;
int hdrlen;
/*
* Handle TKIP counter measures requirement.
*/
if (vap->iv_flags & IEEE80211_F_COUNTERM) {
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
#endif
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"discard frame due to countermeasures (%s)", __func__);
vap->iv_stats.is_crypto_tkipcm++;
return 0;
}
hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
/*
* Copy down 802.11 header and add the IV, KeyID, and ExtIV.
*/
M_PREPEND(m, tkip.ic_header, M_NOWAIT);
if (m == NULL)
return 0;
ivp = mtod(m, uint8_t *);
memmove(ivp, ivp + tkip.ic_header, hdrlen);
ivp += hdrlen;
ivp[0] = k->wk_keytsc >> 8; /* TSC1 */
ivp[1] = (ivp[0] | 0x20) & 0x7f; /* WEP seed */
ivp[2] = k->wk_keytsc >> 0; /* TSC0 */
ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */
ivp[4] = k->wk_keytsc >> 16; /* TSC2 */
ivp[5] = k->wk_keytsc >> 24; /* TSC3 */
ivp[6] = k->wk_keytsc >> 32; /* TSC4 */
ivp[7] = k->wk_keytsc >> 40; /* TSC5 */
/*
* Finally, do software encrypt if neeed.
*/
if (k->wk_flags & IEEE80211_KEY_SWENCRYPT) {
if (!tkip_encrypt(ctx, k, m, hdrlen))
return 0;
/* NB: tkip_encrypt handles wk_keytsc */
} else
k->wk_keytsc++;
return 1;
}
/*
* Add MIC to the frame as needed.
*/
static int
tkip_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
{
struct tkip_ctx *ctx = k->wk_private;
if (force || (k->wk_flags & IEEE80211_KEY_SWENMIC)) {
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
struct ieee80211vap *vap = ctx->tc_vap;
struct ieee80211com *ic = vap->iv_ic;
int hdrlen;
uint8_t mic[IEEE80211_WEP_MICLEN];
vap->iv_stats.is_crypto_tkipenmic++;
hdrlen = ieee80211_hdrspace(ic, wh);
michael_mic(ctx, k->wk_txmic,
m, hdrlen, m->m_pkthdr.len - hdrlen, mic);
return m_append(m, tkip.ic_miclen, mic);
}
return 1;
}
static __inline uint64_t
READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
{
uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
uint16_t iv16 = (b4 << 0) | (b5 << 8);
return (((uint64_t)iv16) << 32) | iv32;
}
/*
* Validate and strip privacy headers (and trailer) for a
* received frame. If necessary, decrypt the frame using
* the specified key.
*/
static int
tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct tkip_ctx *ctx = k->wk_private;
struct ieee80211vap *vap = ctx->tc_vap;
struct ieee80211_frame *wh;
uint8_t *ivp, tid;
/*
* Header should have extended IV and sequence number;
* verify the former and validate the latter.
*/
wh = mtod(m, struct ieee80211_frame *);
ivp = mtod(m, uint8_t *) + hdrlen;
if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
/*
* No extended IV; discard frame.
*/
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"%s", "missing ExtIV for TKIP cipher");
vap->iv_stats.is_rx_tkipformat++;
return 0;
}
/*
* Handle TKIP counter measures requirement.
*/
if (vap->iv_flags & IEEE80211_F_COUNTERM) {
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"discard frame due to countermeasures (%s)", __func__);
vap->iv_stats.is_crypto_tkipcm++;
return 0;
}
tid = ieee80211_gettid(wh);
ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5], ivp[6], ivp[7]);
if (ctx->rx_rsc <= k->wk_keyrsc[tid]) {
/*
* Replay violation; notify upper layer.
*/
ieee80211_notify_replay_failure(vap, wh, k, ctx->rx_rsc, tid);
vap->iv_stats.is_rx_tkipreplay++;
return 0;
}
/*
* NB: We can't update the rsc in the key until MIC is verified.
*
* We assume we are not preempted between doing the check above
* and updating wk_keyrsc when stripping the MIC in tkip_demic.
* Otherwise we might process another packet and discard it as
* a replay.
*/
/*
* Check if the device handled the decrypt in hardware.
* If so we just strip the header; otherwise we need to
* handle the decrypt in software.
*/
if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) &&
!tkip_decrypt(ctx, k, m, hdrlen))
return 0;
/*
* Copy up 802.11 header and strip crypto bits.
*/
memmove(mtod(m, uint8_t *) + tkip.ic_header, mtod(m, void *), hdrlen);
m_adj(m, tkip.ic_header);
m_adj(m, -tkip.ic_trailer);
return 1;
}
/*
* Verify and strip MIC from the frame.
*/
static int
tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
{
struct tkip_ctx *ctx = k->wk_private;
struct ieee80211_frame *wh;
uint8_t tid;
wh = mtod(m, struct ieee80211_frame *);
if ((k->wk_flags & IEEE80211_KEY_SWDEMIC) || force) {
struct ieee80211vap *vap = ctx->tc_vap;
int hdrlen = ieee80211_hdrspace(vap->iv_ic, wh);
u8 mic[IEEE80211_WEP_MICLEN];
u8 mic0[IEEE80211_WEP_MICLEN];
vap->iv_stats.is_crypto_tkipdemic++;
michael_mic(ctx, k->wk_rxmic,
m, hdrlen, m->m_pkthdr.len - (hdrlen + tkip.ic_miclen),
mic);
m_copydata(m, m->m_pkthdr.len - tkip.ic_miclen,
tkip.ic_miclen, mic0);
if (memcmp(mic, mic0, tkip.ic_miclen)) {
/* NB: 802.11 layer handles statistic and debug msg */
ieee80211_notify_michael_failure(vap, wh,
k->wk_rxkeyix != IEEE80211_KEYIX_NONE ?
k->wk_rxkeyix : k->wk_keyix);
return 0;
}
}
/*
* Strip MIC from the tail.
*/
m_adj(m, -tkip.ic_miclen);
/*
* Ok to update rsc now that MIC has been verified.
*/
tid = ieee80211_gettid(wh);
k->wk_keyrsc[tid] = ctx->rx_rsc;
return 1;
}
/*
* Host AP crypt: host-based TKIP encryption implementation for Host AP driver
*
* Copyright (c) 2003-2004, Jouni Malinen <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See README and COPYING for
* more details.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*/
static const __u32 crc32_table[256] = {
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
0x2d02ef8dL
};
static __inline u16 RotR1(u16 val)
{
return (val >> 1) | (val << 15);
}
static __inline u8 Lo8(u16 val)
{
return val & 0xff;
}
static __inline u8 Hi8(u16 val)
{
return val >> 8;
}
static __inline u16 Lo16(u32 val)
{
return val & 0xffff;
}
static __inline u16 Hi16(u32 val)
{
return val >> 16;
}
static __inline u16 Mk16(u8 hi, u8 lo)
{
return lo | (((u16) hi) << 8);
}
static __inline u16 Mk16_le(const u16 *v)
{
return le16toh(*v);
}
static const u16 Sbox[256] = {
0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
};
static __inline u16 _S_(u16 v)
{
u16 t = Sbox[Hi8(v)];
return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
}
#define PHASE1_LOOP_COUNT 8
static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32)
{
int i, j;
/* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
TTAK[0] = Lo16(IV32);
TTAK[1] = Hi16(IV32);
TTAK[2] = Mk16(TA[1], TA[0]);
TTAK[3] = Mk16(TA[3], TA[2]);
TTAK[4] = Mk16(TA[5], TA[4]);
for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
j = 2 * (i & 1);
TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
}
}
#ifndef _BYTE_ORDER
#error "Don't know native byte order"
#endif
static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
u16 IV16)
{
/* Make temporary area overlap WEP seed so that the final copy can be
* avoided on little endian hosts. */
u16 *PPK = (u16 *) &WEPSeed[4];
/* Step 1 - make copy of TTAK and bring in TSC */
PPK[0] = TTAK[0];
PPK[1] = TTAK[1];
PPK[2] = TTAK[2];
PPK[3] = TTAK[3];
PPK[4] = TTAK[4];
PPK[5] = TTAK[4] + IV16;
/* Step 2 - 96-bit bijective mixing using S-box */
PPK[0] += _S_(PPK[5] ^ Mk16_le((const u16 *) &TK[0]));
PPK[1] += _S_(PPK[0] ^ Mk16_le((const u16 *) &TK[2]));
PPK[2] += _S_(PPK[1] ^ Mk16_le((const u16 *) &TK[4]));
PPK[3] += _S_(PPK[2] ^ Mk16_le((const u16 *) &TK[6]));
PPK[4] += _S_(PPK[3] ^ Mk16_le((const u16 *) &TK[8]));
PPK[5] += _S_(PPK[4] ^ Mk16_le((const u16 *) &TK[10]));
PPK[0] += RotR1(PPK[5] ^ Mk16_le((const u16 *) &TK[12]));
PPK[1] += RotR1(PPK[0] ^ Mk16_le((const u16 *) &TK[14]));
PPK[2] += RotR1(PPK[1]);
PPK[3] += RotR1(PPK[2]);
PPK[4] += RotR1(PPK[3]);
PPK[5] += RotR1(PPK[4]);
/* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
* WEPSeed[0..2] is transmitted as WEP IV */
WEPSeed[0] = Hi8(IV16);
WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
WEPSeed[2] = Lo8(IV16);
WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((const u16 *) &TK[0])) >> 1);
#if _BYTE_ORDER == _BIG_ENDIAN
{
int i;
for (i = 0; i < 6; i++)
PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
}
#endif
}
static void
wep_encrypt(u8 *key, struct mbuf *m0, u_int off, size_t data_len,
uint8_t icv[IEEE80211_WEP_CRCLEN])
{
u32 i, j, k, crc;
size_t buflen;
u8 S[256];
u8 *pos;
struct mbuf *m;
#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
/* Setup RC4 state */
for (i = 0; i < 256; i++)
S[i] = i;
j = 0;
for (i = 0; i < 256; i++) {
j = (j + S[i] + key[i & 0x0f]) & 0xff;
S_SWAP(i, j);
}
/* Compute CRC32 over unencrypted data and apply RC4 to data */
crc = ~0;
i = j = 0;
m = m0;
pos = mtod(m, uint8_t *) + off;
buflen = m->m_len - off;
for (;;) {
if (buflen > data_len)
buflen = data_len;
data_len -= buflen;
for (k = 0; k < buflen; k++) {
crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
i = (i + 1) & 0xff;
j = (j + S[i]) & 0xff;
S_SWAP(i, j);
*pos++ ^= S[(S[i] + S[j]) & 0xff];
}
m = m->m_next;
if (m == NULL) {
KASSERT(data_len == 0,
("out of buffers with data_len %zu\n", data_len));
break;
}
pos = mtod(m, uint8_t *);
buflen = m->m_len;
}
crc = ~crc;
/* Append little-endian CRC32 and encrypt it to produce ICV */
icv[0] = crc;
icv[1] = crc >> 8;
icv[2] = crc >> 16;
icv[3] = crc >> 24;
for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
i = (i + 1) & 0xff;
j = (j + S[i]) & 0xff;
S_SWAP(i, j);
icv[k] ^= S[(S[i] + S[j]) & 0xff];
}
}
static int
wep_decrypt(u8 *key, struct mbuf *m, u_int off, size_t data_len)
{
u32 i, j, k, crc;
u8 S[256];
u8 *pos, icv[4];
size_t buflen;
/* Setup RC4 state */
for (i = 0; i < 256; i++)
S[i] = i;
j = 0;
for (i = 0; i < 256; i++) {
j = (j + S[i] + key[i & 0x0f]) & 0xff;
S_SWAP(i, j);
}
/* Apply RC4 to data and compute CRC32 over decrypted data */
crc = ~0;
i = j = 0;
pos = mtod(m, uint8_t *) + off;
buflen = m->m_len - off;
for (;;) {
if (buflen > data_len)
buflen = data_len;
data_len -= buflen;
for (k = 0; k < buflen; k++) {
i = (i + 1) & 0xff;
j = (j + S[i]) & 0xff;
S_SWAP(i, j);
*pos ^= S[(S[i] + S[j]) & 0xff];
crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
pos++;
}
m = m->m_next;
if (m == NULL) {
KASSERT(data_len == 0,
("out of buffers with data_len %zu\n", data_len));
break;
}
pos = mtod(m, uint8_t *);
buflen = m->m_len;
}
crc = ~crc;
/* Encrypt little-endian CRC32 and verify that it matches with the
* received ICV */
icv[0] = crc;
icv[1] = crc >> 8;
icv[2] = crc >> 16;
icv[3] = crc >> 24;
for (k = 0; k < 4; k++) {
i = (i + 1) & 0xff;
j = (j + S[i]) & 0xff;
S_SWAP(i, j);
if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
/* ICV mismatch - drop frame */
return -1;
}
}
return 0;
}
static __inline u32 rotl(u32 val, int bits)
{
return (val << bits) | (val >> (32 - bits));
}
static __inline u32 rotr(u32 val, int bits)
{
return (val >> bits) | (val << (32 - bits));
}
static __inline u32 xswap(u32 val)
{
return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
}
#define michael_block(l, r) \
do { \
r ^= rotl(l, 17); \
l += r; \
r ^= xswap(l); \
l += r; \
r ^= rotl(l, 3); \
l += r; \
r ^= rotr(l, 2); \
l += r; \
} while (0)
static __inline u32 get_le32_split(u8 b0, u8 b1, u8 b2, u8 b3)
{
return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
}
static __inline u32 get_le32(const u8 *p)
{
return get_le32_split(p[0], p[1], p[2], p[3]);
}
static __inline void put_le32(u8 *p, u32 v)
{
p[0] = v;
p[1] = v >> 8;
p[2] = v >> 16;
p[3] = v >> 24;
}
/*
* Craft pseudo header used to calculate the MIC.
*/
static void
michael_mic_hdr(const struct ieee80211_frame *wh0, uint8_t hdr[16])
{
const struct ieee80211_frame_addr4 *wh =
(const struct ieee80211_frame_addr4 *) wh0;
switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
case IEEE80211_FC1_DIR_NODS:
IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */
IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2);
break;
case IEEE80211_FC1_DIR_TODS:
IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */
IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2);
break;
case IEEE80211_FC1_DIR_FROMDS:
IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */
IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr3);
break;
case IEEE80211_FC1_DIR_DSTODS:
IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */
IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr4);
break;
}
if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
const struct ieee80211_qosframe *qwh =
(const struct ieee80211_qosframe *) wh;
hdr[12] = qwh->i_qos[0] & IEEE80211_QOS_TID;
} else
hdr[12] = 0;
hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
}
static void
michael_mic(struct tkip_ctx *ctx, const u8 *key,
struct mbuf *m, u_int off, size_t data_len,
u8 mic[IEEE80211_WEP_MICLEN])
{
uint8_t hdr[16];
u32 l, r;
const uint8_t *data;
u_int space;
michael_mic_hdr(mtod(m, struct ieee80211_frame *), hdr);
l = get_le32(key);
r = get_le32(key + 4);
/* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */
l ^= get_le32(hdr);
michael_block(l, r);
l ^= get_le32(&hdr[4]);
michael_block(l, r);
l ^= get_le32(&hdr[8]);
michael_block(l, r);
l ^= get_le32(&hdr[12]);
michael_block(l, r);
/* first buffer has special handling */
data = mtod(m, const uint8_t *) + off;
space = m->m_len - off;
for (;;) {
if (space > data_len)
space = data_len;
/* collect 32-bit blocks from current buffer */
while (space >= sizeof(uint32_t)) {
l ^= get_le32(data);
michael_block(l, r);
data += sizeof(uint32_t), space -= sizeof(uint32_t);
data_len -= sizeof(uint32_t);
}
/*
* NB: when space is zero we make one more trip around
* the loop to advance to the next mbuf where there is
* data. This handles the case where there are 4*n
* bytes in an mbuf followed by <4 bytes in a later mbuf.
* By making an extra trip we'll drop out of the loop
* with m pointing at the mbuf with 3 bytes and space
* set as required by the remainder handling below.
*/
if (data_len == 0 ||
(data_len < sizeof(uint32_t) && space != 0))
break;
m = m->m_next;
if (m == NULL) {
KASSERT(0, ("out of data, data_len %zu\n", data_len));
break;
}
if (space != 0) {
const uint8_t *data_next;
/*
* Block straddles buffers, split references.
*/
data_next = mtod(m, const uint8_t *);
KASSERT(m->m_len >= sizeof(uint32_t) - space,
("not enough data in following buffer, "
"m_len %u need %zu\n", m->m_len,
sizeof(uint32_t) - space));
switch (space) {
case 1:
l ^= get_le32_split(data[0], data_next[0],
data_next[1], data_next[2]);
data = data_next + 3;
space = m->m_len - 3;
break;
case 2:
l ^= get_le32_split(data[0], data[1],
data_next[0], data_next[1]);
data = data_next + 2;
space = m->m_len - 2;
break;
case 3:
l ^= get_le32_split(data[0], data[1],
data[2], data_next[0]);
data = data_next + 1;
space = m->m_len - 1;
break;
}
michael_block(l, r);
data_len -= sizeof(uint32_t);
} else {
/*
* Setup for next buffer.
*/
data = mtod(m, const uint8_t *);
space = m->m_len;
}
}
/*
* Catch degenerate cases like mbuf[4*n+1 bytes] followed by
* mbuf[2 bytes]. I don't believe these should happen; if they
* do then we'll need more involved logic.
*/
KASSERT(data_len <= space,
("not enough data, data_len %zu space %u\n", data_len, space));
/* Last block and padding (0x5a, 4..7 x 0) */
switch (data_len) {
case 0:
l ^= get_le32_split(0x5a, 0, 0, 0);
break;
case 1:
l ^= get_le32_split(data[0], 0x5a, 0, 0);
break;
case 2:
l ^= get_le32_split(data[0], data[1], 0x5a, 0);
break;
case 3:
l ^= get_le32_split(data[0], data[1], data[2], 0x5a);
break;
}
michael_block(l, r);
/* l ^= 0; */
michael_block(l, r);
put_le32(mic, l);
put_le32(mic + 4, r);
}
static int
tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
struct mbuf *m, int hdrlen)
{
struct ieee80211_frame *wh;
uint8_t icv[IEEE80211_WEP_CRCLEN];
ctx->tc_vap->iv_stats.is_crypto_tkip++;
wh = mtod(m, struct ieee80211_frame *);
if (!ctx->tx_phase1_done) {
tkip_mixing_phase1(ctx->tx_ttak, key->wk_key, wh->i_addr2,
(u32)(key->wk_keytsc >> 16));
ctx->tx_phase1_done = 1;
}
tkip_mixing_phase2(ctx->tx_rc4key, key->wk_key, ctx->tx_ttak,
(u16) key->wk_keytsc);
wep_encrypt(ctx->tx_rc4key,
m, hdrlen + tkip.ic_header,
m->m_pkthdr.len - (hdrlen + tkip.ic_header),
icv);
(void) m_append(m, IEEE80211_WEP_CRCLEN, icv); /* XXX check return */
key->wk_keytsc++;
if ((u16)(key->wk_keytsc) == 0)
ctx->tx_phase1_done = 0;
return 1;
}
static int
tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
struct mbuf *m, int hdrlen)
{
struct ieee80211_frame *wh;
struct ieee80211vap *vap = ctx->tc_vap;
u32 iv32;
u16 iv16;
u8 tid;
vap->iv_stats.is_crypto_tkip++;
wh = mtod(m, struct ieee80211_frame *);
/* NB: tkip_decap already verified header and left seq in rx_rsc */
iv16 = (u16) ctx->rx_rsc;
iv32 = (u32) (ctx->rx_rsc >> 16);
tid = ieee80211_gettid(wh);
if (iv32 != (u32)(key->wk_keyrsc[tid] >> 16) || !ctx->rx_phase1_done) {
tkip_mixing_phase1(ctx->rx_ttak, key->wk_key,
wh->i_addr2, iv32);
ctx->rx_phase1_done = 1;
}
tkip_mixing_phase2(ctx->rx_rc4key, key->wk_key, ctx->rx_ttak, iv16);
/* NB: m is unstripped; deduct headers + ICV to get payload */
if (wep_decrypt(ctx->rx_rc4key,
m, hdrlen + tkip.ic_header,
m->m_pkthdr.len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) {
if (iv32 != (u32)(key->wk_keyrsc[tid] >> 16)) {
/* Previously cached Phase1 result was already lost, so
* it needs to be recalculated for the next packet. */
ctx->rx_phase1_done = 0;
}
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"%s", "TKIP ICV mismatch on decrypt");
vap->iv_stats.is_rx_tkipicv++;
return 0;
}
return 1;
}
/*
* Module glue.
*/
IEEE80211_CRYPTO_MODULE(tkip, 1);
@@ -0,0 +1,480 @@
/*-
* 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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11 WEP crypto support.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
static void *wep_attach(struct ieee80211vap *, struct ieee80211_key *);
static void wep_detach(struct ieee80211_key *);
static int wep_setkey(struct ieee80211_key *);
static int wep_encap(struct ieee80211_key *, struct mbuf *, uint8_t keyid);
static int wep_decap(struct ieee80211_key *, struct mbuf *, int hdrlen);
static int wep_enmic(struct ieee80211_key *, struct mbuf *, int);
static int wep_demic(struct ieee80211_key *, struct mbuf *, int);
static const struct ieee80211_cipher wep = {
.ic_name = "WEP",
.ic_cipher = IEEE80211_CIPHER_WEP,
.ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN,
.ic_trailer = IEEE80211_WEP_CRCLEN,
.ic_miclen = 0,
.ic_attach = wep_attach,
.ic_detach = wep_detach,
.ic_setkey = wep_setkey,
.ic_encap = wep_encap,
.ic_decap = wep_decap,
.ic_enmic = wep_enmic,
.ic_demic = wep_demic,
};
static int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
static int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
struct wep_ctx {
struct ieee80211vap *wc_vap; /* for diagnostics+statistics */
struct ieee80211com *wc_ic;
uint32_t wc_iv; /* initial vector for crypto */
};
/* number of references from net80211 layer */
static int nrefs = 0;
static void *
wep_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct wep_ctx *ctx;
ctx = (struct wep_ctx *) malloc(sizeof(struct wep_ctx),
M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
vap->iv_stats.is_crypto_nomem++;
return NULL;
}
ctx->wc_vap = vap;
ctx->wc_ic = vap->iv_ic;
get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
nrefs++; /* NB: we assume caller locking */
return ctx;
}
static void
wep_detach(struct ieee80211_key *k)
{
struct wep_ctx *ctx = k->wk_private;
free(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
static int
wep_setkey(struct ieee80211_key *k)
{
return k->wk_keylen >= 40/NBBY;
}
/*
* Add privacy headers appropriate for the specified key.
*/
static int
wep_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct wep_ctx *ctx = k->wk_private;
struct ieee80211com *ic = ctx->wc_ic;
uint32_t iv;
uint8_t *ivp;
int hdrlen;
hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
/*
* Copy down 802.11 header and add the IV + KeyID.
*/
M_PREPEND(m, wep.ic_header, M_NOWAIT);
if (m == NULL)
return 0;
ivp = mtod(m, uint8_t *);
ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
ivp += hdrlen;
/*
* XXX
* IV must not duplicate during the lifetime of the key.
* But no mechanism to renew keys is defined in IEEE 802.11
* for WEP. And the IV may be duplicated at other stations
* because the session key itself is shared. So we use a
* pseudo random IV for now, though it is not the right way.
*
* NB: Rather than use a strictly random IV we select a
* random one to start and then increment the value for
* each frame. This is an explicit tradeoff between
* overhead and security. Given the basic insecurity of
* WEP this seems worthwhile.
*/
/*
* Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
* (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255
*/
iv = ctx->wc_iv;
if ((iv & 0xff00) == 0xff00) {
int B = (iv & 0xff0000) >> 16;
if (3 <= B && B < 16)
iv += 0x0100;
}
ctx->wc_iv = iv + 1;
/*
* NB: Preserve byte order of IV for packet
* sniffers; it doesn't matter otherwise.
*/
#if _BYTE_ORDER == _BIG_ENDIAN
ivp[0] = iv >> 0;
ivp[1] = iv >> 8;
ivp[2] = iv >> 16;
#else
ivp[2] = iv >> 0;
ivp[1] = iv >> 8;
ivp[0] = iv >> 16;
#endif
ivp[3] = keyid;
/*
* Finally, do software encrypt if neeed.
*/
if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&
!wep_encrypt(k, m, hdrlen))
return 0;
return 1;
}
/*
* Add MIC to the frame as needed.
*/
static int
wep_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
{
return 1;
}
/*
* Validate and strip privacy headers (and trailer) for a
* received frame. If necessary, decrypt the frame using
* the specified key.
*/
static int
wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct wep_ctx *ctx = k->wk_private;
struct ieee80211vap *vap = ctx->wc_vap;
struct ieee80211_frame *wh;
wh = mtod(m, struct ieee80211_frame *);
/*
* Check if the device handled the decrypt in hardware.
* If so we just strip the header; otherwise we need to
* handle the decrypt in software.
*/
if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) &&
!wep_decrypt(k, m, hdrlen)) {
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
"%s", "WEP ICV mismatch on decrypt");
vap->iv_stats.is_rx_wepfail++;
return 0;
}
/*
* Copy up 802.11 header and strip crypto bits.
*/
ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + wep.ic_header, hdrlen);
m_adj(m, wep.ic_header);
m_adj(m, -wep.ic_trailer);
return 1;
}
/*
* Verify and strip MIC from the frame.
*/
static int
wep_demic(struct ieee80211_key *k, struct mbuf *skb, int force)
{
return 1;
}
static const uint32_t crc32_table[256] = {
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
0x2d02ef8dL
};
static int
wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
{
#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
struct wep_ctx *ctx = key->wk_private;
struct ieee80211vap *vap = ctx->wc_vap;
struct mbuf *m = m0;
uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
uint8_t icv[IEEE80211_WEP_CRCLEN];
uint32_t i, j, k, crc;
size_t buflen, data_len;
uint8_t S[256];
uint8_t *pos;
u_int off, keylen;
vap->iv_stats.is_crypto_wep++;
/* NB: this assumes the header was pulled up */
memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
/* Setup RC4 state */
for (i = 0; i < 256; i++)
S[i] = i;
j = 0;
keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
for (i = 0; i < 256; i++) {
j = (j + S[i] + rc4key[i % keylen]) & 0xff;
S_SWAP(i, j);
}
off = hdrlen + wep.ic_header;
data_len = m->m_pkthdr.len - off;
/* Compute CRC32 over unencrypted data and apply RC4 to data */
crc = ~0;
i = j = 0;
pos = mtod(m, uint8_t *) + off;
buflen = m->m_len - off;
for (;;) {
if (buflen > data_len)
buflen = data_len;
data_len -= buflen;
for (k = 0; k < buflen; k++) {
crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
i = (i + 1) & 0xff;
j = (j + S[i]) & 0xff;
S_SWAP(i, j);
*pos++ ^= S[(S[i] + S[j]) & 0xff];
}
if (m->m_next == NULL) {
if (data_len != 0) { /* out of data */
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
ether_sprintf(mtod(m0,
struct ieee80211_frame *)->i_addr2),
"out of data for WEP (data_len %zu)",
data_len);
/* XXX stat */
return 0;
}
break;
}
m = m->m_next;
pos = mtod(m, uint8_t *);
buflen = m->m_len;
}
crc = ~crc;
/* Append little-endian CRC32 and encrypt it to produce ICV */
icv[0] = crc;
icv[1] = crc >> 8;
icv[2] = crc >> 16;
icv[3] = crc >> 24;
for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
i = (i + 1) & 0xff;
j = (j + S[i]) & 0xff;
S_SWAP(i, j);
icv[k] ^= S[(S[i] + S[j]) & 0xff];
}
return m_append(m0, IEEE80211_WEP_CRCLEN, icv);
#undef S_SWAP
}
static int
wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
{
#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
struct wep_ctx *ctx = key->wk_private;
struct ieee80211vap *vap = ctx->wc_vap;
struct mbuf *m = m0;
uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
uint8_t icv[IEEE80211_WEP_CRCLEN];
uint32_t i, j, k, crc;
size_t buflen, data_len;
uint8_t S[256];
uint8_t *pos;
u_int off, keylen;
vap->iv_stats.is_crypto_wep++;
/* NB: this assumes the header was pulled up */
memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
/* Setup RC4 state */
for (i = 0; i < 256; i++)
S[i] = i;
j = 0;
keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
for (i = 0; i < 256; i++) {
j = (j + S[i] + rc4key[i % keylen]) & 0xff;
S_SWAP(i, j);
}
off = hdrlen + wep.ic_header;
data_len = m->m_pkthdr.len - (off + wep.ic_trailer),
/* Compute CRC32 over unencrypted data and apply RC4 to data */
crc = ~0;
i = j = 0;
pos = mtod(m, uint8_t *) + off;
buflen = m->m_len - off;
for (;;) {
if (buflen > data_len)
buflen = data_len;
data_len -= buflen;
for (k = 0; k < buflen; k++) {
i = (i + 1) & 0xff;
j = (j + S[i]) & 0xff;
S_SWAP(i, j);
*pos ^= S[(S[i] + S[j]) & 0xff];
crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
pos++;
}
m = m->m_next;
if (m == NULL) {
if (data_len != 0) { /* out of data */
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
mtod(m0, struct ieee80211_frame *)->i_addr2,
"out of data for WEP (data_len %zu)",
data_len);
return 0;
}
break;
}
pos = mtod(m, uint8_t *);
buflen = m->m_len;
}
crc = ~crc;
/* Encrypt little-endian CRC32 and verify that it matches with
* received ICV */
icv[0] = crc;
icv[1] = crc >> 8;
icv[2] = crc >> 16;
icv[3] = crc >> 24;
for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
i = (i + 1) & 0xff;
j = (j + S[i]) & 0xff;
S_SWAP(i, j);
/* XXX assumes ICV is contiguous in mbuf */
if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
/* ICV mismatch - drop frame */
return 0;
}
}
return 1;
#undef S_SWAP
}
/*
* Module glue.
*/
IEEE80211_CRYPTO_MODULE(wep, 1);
@@ -0,0 +1,879 @@
/*-
* Copyright (c) 2007-2009 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.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_ddb.h"
#include "opt_wlan.h"
#ifdef DDB
/*
* IEEE 802.11 DDB support
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/ethernet.h>
#include <net/vnet.h>
#include <net80211/ieee80211_var.h>
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
#ifdef IEEE80211_SUPPORT_MESH
#include <net80211/ieee80211_mesh.h>
#endif
#include <ddb/ddb.h>
#include <ddb/db_sym.h>
#define DB_PRINTSYM(prefix, name, addr) do { \
db_printf("%s%-25s : ", prefix, name); \
db_printsym((db_addr_t) addr, DB_STGY_ANY); \
db_printf("\n"); \
} while (0)
static void _db_show_sta(const struct ieee80211_node *);
static void _db_show_vap(const struct ieee80211vap *, int);
static void _db_show_com(const struct ieee80211com *,
int showvaps, int showsta, int showprocs);
static void _db_show_node_table(const char *tag,
const struct ieee80211_node_table *);
static void _db_show_channel(const char *tag, const struct ieee80211_channel *);
static void _db_show_ssid(const char *tag, int ix, int len, const uint8_t *);
static void _db_show_appie(const char *tag, const struct ieee80211_appie *);
static void _db_show_key(const char *tag, int ix, const struct ieee80211_key *);
static void _db_show_roamparams(const char *tag, const void *arg,
const struct ieee80211_roamparam *rp);
static void _db_show_txparams(const char *tag, const void *arg,
const struct ieee80211_txparam *tp);
static void _db_show_ageq(const char *tag, const struct ieee80211_ageq *q);
static void _db_show_stats(const struct ieee80211_stats *);
#ifdef IEEE80211_SUPPORT_MESH
static void _db_show_mesh(const struct ieee80211_mesh_state *);
#endif
DB_SHOW_COMMAND(sta, db_show_sta)
{
if (!have_addr) {
db_printf("usage: show sta <addr>\n");
return;
}
_db_show_sta((const struct ieee80211_node *) addr);
}
DB_SHOW_COMMAND(statab, db_show_statab)
{
if (!have_addr) {
db_printf("usage: show statab <addr>\n");
return;
}
_db_show_node_table("", (const struct ieee80211_node_table *) addr);
}
DB_SHOW_COMMAND(vap, db_show_vap)
{
int i, showprocs = 0;
if (!have_addr) {
db_printf("usage: show vap <addr>\n");
return;
}
for (i = 0; modif[i] != '\0'; i++)
switch (modif[i]) {
case 'a':
showprocs = 1;
break;
case 'p':
showprocs = 1;
break;
}
_db_show_vap((const struct ieee80211vap *) addr, showprocs);
}
DB_SHOW_COMMAND(com, db_show_com)
{
const struct ieee80211com *ic;
int i, showprocs = 0, showvaps = 0, showsta = 0;
if (!have_addr) {
db_printf("usage: show com <addr>\n");
return;
}
for (i = 0; modif[i] != '\0'; i++)
switch (modif[i]) {
case 'a':
showsta = showvaps = showprocs = 1;
break;
case 's':
showsta = 1;
break;
case 'v':
showvaps = 1;
break;
case 'p':
showprocs = 1;
break;
}
ic = (const struct ieee80211com *) addr;
_db_show_com(ic, showvaps, showsta, showprocs);
}
DB_SHOW_ALL_COMMAND(vaps, db_show_all_vaps)
{
VNET_ITERATOR_DECL(vnet_iter);
const struct ifnet *ifp;
int i, showall = 0;
for (i = 0; modif[i] != '\0'; i++)
switch (modif[i]) {
case 'a':
showall = 1;
break;
}
VNET_FOREACH(vnet_iter) {
TAILQ_FOREACH(ifp, &V_ifnet, if_list)
if (ifp->if_type == IFT_IEEE80211) {
const struct ieee80211com *ic = ifp->if_l2com;
if (!showall) {
const struct ieee80211vap *vap;
db_printf("%s: com %p vaps:",
ifp->if_xname, ic);
TAILQ_FOREACH(vap, &ic->ic_vaps,
iv_next)
db_printf(" %s(%p)",
vap->iv_ifp->if_xname, vap);
db_printf("\n");
} else
_db_show_com(ic, 1, 1, 1);
}
}
}
#ifdef IEEE80211_SUPPORT_MESH
DB_SHOW_ALL_COMMAND(mesh, db_show_mesh)
{
const struct ieee80211_mesh_state *ms;
if (!have_addr) {
db_printf("usage: show mesh <addr>\n");
return;
}
ms = (const struct ieee80211_mesh_state *) addr;
_db_show_mesh(ms);
}
#endif /* IEEE80211_SUPPORT_MESH */
static void
_db_show_txampdu(const char *sep, int ix, const struct ieee80211_tx_ampdu *tap)
{
db_printf("%stxampdu[%d]: %p flags %b %s\n",
sep, ix, tap, tap->txa_flags, IEEE80211_AGGR_BITS,
ieee80211_wme_acnames[tap->txa_ac]);
db_printf("%s token %u lastsample %d pkts %d avgpps %d qbytes %d qframes %d\n",
sep, tap->txa_token, tap->txa_lastsample, tap->txa_pkts,
tap->txa_avgpps, tap->txa_qbytes, tap->txa_qframes);
db_printf("%s start %u seqpending %u wnd %u attempts %d nextrequest %d\n",
sep, tap->txa_start, tap->txa_seqpending, tap->txa_wnd,
tap->txa_attempts, tap->txa_nextrequest);
/* XXX timer */
}
static void
_db_show_rxampdu(const char *sep, int ix, const struct ieee80211_rx_ampdu *rap)
{
int i;
db_printf("%srxampdu[%d]: %p flags 0x%x tid %u\n",
sep, ix, rap, rap->rxa_flags, ix /*XXX */);
db_printf("%s qbytes %d qframes %d seqstart %u start %u wnd %u\n",
sep, rap->rxa_qbytes, rap->rxa_qframes,
rap->rxa_seqstart, rap->rxa_start, rap->rxa_wnd);
db_printf("%s age %d nframes %d\n", sep,
rap->rxa_age, rap->rxa_nframes);
for (i = 0; i < IEEE80211_AGGR_BAWMAX; i++)
if (rap->rxa_m[i] != NULL)
db_printf("%s m[%2u:%4u] %p\n", sep, i,
IEEE80211_SEQ_ADD(rap->rxa_start, i),
rap->rxa_m[i]);
}
static void
_db_show_sta(const struct ieee80211_node *ni)
{
int i;
db_printf("0x%p: mac %s refcnt %d\n", ni,
ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni));
db_printf("\tvap %p wdsvap %p ic %p table %p\n",
ni->ni_vap, ni->ni_wdsvap, ni->ni_ic, ni->ni_table);
db_printf("\tflags=%b\n", ni->ni_flags, IEEE80211_NODE_BITS);
db_printf("\tscangen %u authmode %u ath_flags 0x%x ath_defkeyix %u\n",
ni->ni_scangen, ni->ni_authmode,
ni->ni_ath_flags, ni->ni_ath_defkeyix);
db_printf("\tassocid 0x%x txpower %u vlan %u\n",
ni->ni_associd, ni->ni_txpower, ni->ni_vlan);
db_printf("\tjointime %d (%lu secs) challenge %p\n",
ni->ni_jointime, (unsigned long)(time_uptime - ni->ni_jointime),
ni->ni_challenge);
db_printf("\ties: data %p len %d\n", ni->ni_ies.data, ni->ni_ies.len);
db_printf("\t[wpa_ie %p rsn_ie %p wme_ie %p ath_ie %p\n",
ni->ni_ies.wpa_ie, ni->ni_ies.rsn_ie, ni->ni_ies.wme_ie,
ni->ni_ies.ath_ie);
db_printf("\t htcap_ie %p htinfo_ie %p]\n",
ni->ni_ies.htcap_ie, ni->ni_ies.htinfo_ie);
if (ni->ni_flags & IEEE80211_NODE_QOS) {
for (i = 0; i < WME_NUM_TID; i++) {
if (ni->ni_txseqs[i] || ni->ni_rxseqs[i])
db_printf("\t[%u] txseq %u rxseq %u fragno %u\n",
i, ni->ni_txseqs[i],
ni->ni_rxseqs[i] >> IEEE80211_SEQ_SEQ_SHIFT,
ni->ni_rxseqs[i] & IEEE80211_SEQ_FRAG_MASK);
}
}
db_printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n",
ni->ni_txseqs[IEEE80211_NONQOS_TID],
ni->ni_rxseqs[IEEE80211_NONQOS_TID] >> IEEE80211_SEQ_SEQ_SHIFT,
ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK,
ni->ni_rxfragstamp);
db_printf("\trxfrag[0] %p rxfrag[1] %p rxfrag[2] %p\n",
ni->ni_rxfrag[0], ni->ni_rxfrag[1], ni->ni_rxfrag[2]);
_db_show_key("\tucastkey", 0, &ni->ni_ucastkey);
db_printf("\tavgrssi 0x%x (rssi %d) noise %d\n",
ni->ni_avgrssi, IEEE80211_RSSI_GET(ni->ni_avgrssi),
ni->ni_noise);
db_printf("\tintval %u capinfo %b\n",
ni->ni_intval, ni->ni_capinfo, IEEE80211_CAPINFO_BITS);
db_printf("\tbssid %s", ether_sprintf(ni->ni_bssid));
_db_show_ssid(" essid ", 0, ni->ni_esslen, ni->ni_essid);
db_printf("\n");
_db_show_channel("\tchannel", ni->ni_chan);
db_printf("\n");
db_printf("\terp %b dtim_period %u dtim_count %u\n",
ni->ni_erp, IEEE80211_ERP_BITS,
ni->ni_dtim_period, ni->ni_dtim_count);
db_printf("\thtcap %b htparam 0x%x htctlchan %u ht2ndchan %u\n",
ni->ni_htcap, IEEE80211_HTCAP_BITS,
ni->ni_htparam, ni->ni_htctlchan, ni->ni_ht2ndchan);
db_printf("\thtopmode 0x%x htstbc 0x%x chw %u\n",
ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw);
/* XXX ampdu state */
for (i = 0; i < WME_NUM_AC; i++)
if (ni->ni_tx_ampdu[i].txa_flags & IEEE80211_AGGR_SETUP)
_db_show_txampdu("\t", i, &ni->ni_tx_ampdu[i]);
for (i = 0; i < WME_NUM_TID; i++)
if (ni->ni_rx_ampdu[i].rxa_flags)
_db_show_rxampdu("\t", i, &ni->ni_rx_ampdu[i]);
db_printf("\tinact %u inact_reload %u txrate %u\n",
ni->ni_inact, ni->ni_inact_reload, ni->ni_txrate);
#ifdef IEEE80211_SUPPORT_MESH
_db_show_ssid("\tmeshid ", 0, ni->ni_meshidlen, ni->ni_meshid);
db_printf(" mlstate %b mllid 0x%x mlpid 0x%x mlrcnt %u mltval %u\n",
ni->ni_mlstate, IEEE80211_MESH_MLSTATE_BITS,
ni->ni_mllid, ni->ni_mlpid, ni->ni_mlrcnt, ni->ni_mltval);
#endif
}
#ifdef IEEE80211_SUPPORT_TDMA
static void
_db_show_tdma(const char *sep, const struct ieee80211_tdma_state *ts, int showprocs)
{
db_printf("%stdma %p:\n", sep, ts);
db_printf("%s version %u slot %u bintval %u peer %p\n", sep,
ts->tdma_version, ts->tdma_slot, ts->tdma_bintval, ts->tdma_peer);
db_printf("%s slotlen %u slotcnt %u", sep,
ts->tdma_slotlen, ts->tdma_slotcnt);
db_printf(" inuse 0x%x active 0x%x count %d\n",
ts->tdma_inuse[0], ts->tdma_active[0], ts->tdma_count);
if (showprocs) {
DB_PRINTSYM(sep, " tdma_newstate", ts->tdma_newstate);
DB_PRINTSYM(sep, " tdma_recv_mgmt", ts->tdma_recv_mgmt);
DB_PRINTSYM(sep, " tdma_opdetach", ts->tdma_opdetach);
}
}
#endif /* IEEE80211_SUPPORT_TDMA */
static void
_db_show_vap(const struct ieee80211vap *vap, int showprocs)
{
const struct ieee80211com *ic = vap->iv_ic;
int i;
db_printf("%p:", vap);
db_printf(" bss %p", vap->iv_bss);
db_printf(" myaddr %s", ether_sprintf(vap->iv_myaddr));
db_printf("\n");
db_printf("\topmode %s", ieee80211_opmode_name[vap->iv_opmode]);
db_printf(" state %s", ieee80211_state_name[vap->iv_state]);
db_printf(" ifp %p(%s)", vap->iv_ifp, vap->iv_ifp->if_xname);
db_printf("\n");
db_printf("\tic %p", vap->iv_ic);
db_printf(" media %p", &vap->iv_media);
db_printf(" bpf_if %p", vap->iv_rawbpf);
db_printf(" mgtsend %p", &vap->iv_mgtsend);
#if 0
struct sysctllog *iv_sysctl; /* dynamic sysctl context */
#endif
db_printf("\n");
db_printf("\tdebug=%b\n", vap->iv_debug, IEEE80211_MSG_BITS);
db_printf("\tflags=%b\n", vap->iv_flags, IEEE80211_F_BITS);
db_printf("\tflags_ext=%b\n", vap->iv_flags_ext, IEEE80211_FEXT_BITS);
db_printf("\tflags_ht=%b\n", vap->iv_flags_ht, IEEE80211_FHT_BITS);
db_printf("\tflags_ven=%b\n", vap->iv_flags_ven, IEEE80211_FVEN_BITS);
db_printf("\tcaps=%b\n", vap->iv_caps, IEEE80211_C_BITS);
db_printf("\thtcaps=%b\n", vap->iv_htcaps, IEEE80211_C_HTCAP_BITS);
_db_show_stats(&vap->iv_stats);
db_printf("\tinact_init %d", vap->iv_inact_init);
db_printf(" inact_auth %d", vap->iv_inact_auth);
db_printf(" inact_run %d", vap->iv_inact_run);
db_printf(" inact_probe %d", vap->iv_inact_probe);
db_printf("\n");
db_printf("\tdes_nssid %d", vap->iv_des_nssid);
if (vap->iv_des_nssid)
_db_show_ssid(" des_ssid[%u] ", 0,
vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
db_printf(" des_bssid %s", ether_sprintf(vap->iv_des_bssid));
db_printf("\n");
db_printf("\tdes_mode %d", vap->iv_des_mode);
_db_show_channel(" des_chan", vap->iv_des_chan);
db_printf("\n");
#if 0
int iv_nicknamelen; /* XXX junk */
uint8_t iv_nickname[IEEE80211_NWID_LEN];
#endif
db_printf("\tbgscanidle %u", vap->iv_bgscanidle);
db_printf(" bgscanintvl %u", vap->iv_bgscanintvl);
db_printf(" scanvalid %u", vap->iv_scanvalid);
db_printf("\n");
db_printf("\tscanreq_duration %u", vap->iv_scanreq_duration);
db_printf(" scanreq_mindwell %u", vap->iv_scanreq_mindwell);
db_printf(" scanreq_maxdwell %u", vap->iv_scanreq_maxdwell);
db_printf("\n");
db_printf("\tscanreq_flags 0x%x", vap->iv_scanreq_flags);
db_printf(" scanreq_nssid %d", vap->iv_scanreq_nssid);
for (i = 0; i < vap->iv_scanreq_nssid; i++)
_db_show_ssid(" scanreq_ssid[%u]", i,
vap->iv_scanreq_ssid[i].len, vap->iv_scanreq_ssid[i].ssid);
db_printf(" roaming %d", vap->iv_roaming);
db_printf("\n");
for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++)
if (isset(ic->ic_modecaps, i)) {
_db_show_roamparams("\troamparms[%s]",
ieee80211_phymode_name[i], &vap->iv_roamparms[i]);
db_printf("\n");
}
db_printf("\tbmissthreshold %u", vap->iv_bmissthreshold);
db_printf(" bmiss_max %u", vap->iv_bmiss_count);
db_printf(" bmiss_max %d", vap->iv_bmiss_max);
db_printf("\n");
db_printf("\tswbmiss_count %u", vap->iv_swbmiss_count);
db_printf(" swbmiss_period %u", vap->iv_swbmiss_period);
db_printf(" swbmiss %p", &vap->iv_swbmiss);
db_printf("\n");
db_printf("\tampdu_rxmax %d", vap->iv_ampdu_rxmax);
db_printf(" ampdu_density %d", vap->iv_ampdu_density);
db_printf(" ampdu_limit %d", vap->iv_ampdu_limit);
db_printf(" amsdu_limit %d", vap->iv_amsdu_limit);
db_printf("\n");
db_printf("\tmax_aid %u", vap->iv_max_aid);
db_printf(" aid_bitmap %p", vap->iv_aid_bitmap);
db_printf("\n");
db_printf("\tsta_assoc %u", vap->iv_sta_assoc);
db_printf(" ps_sta %u", vap->iv_ps_sta);
db_printf(" ps_pending %u", vap->iv_ps_pending);
db_printf(" tim_len %u", vap->iv_tim_len);
db_printf(" tim_bitmap %p", vap->iv_tim_bitmap);
db_printf("\n");
db_printf("\tdtim_period %u", vap->iv_dtim_period);
db_printf(" dtim_count %u", vap->iv_dtim_count);
db_printf(" set_tim %p", vap->iv_set_tim);
db_printf(" csa_count %d", vap->iv_csa_count);
db_printf("\n");
db_printf("\trtsthreshold %u", vap->iv_rtsthreshold);
db_printf(" fragthreshold %u", vap->iv_fragthreshold);
db_printf(" inact_timer %d", vap->iv_inact_timer);
db_printf("\n");
for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++)
if (isset(ic->ic_modecaps, i)) {
_db_show_txparams("\ttxparms[%s]",
ieee80211_phymode_name[i], &vap->iv_txparms[i]);
db_printf("\n");
}
/* application-specified IE's to attach to mgt frames */
_db_show_appie("\tappie_beacon", vap->iv_appie_beacon);
_db_show_appie("\tappie_probereq", vap->iv_appie_probereq);
_db_show_appie("\tappie_proberesp", vap->iv_appie_proberesp);
_db_show_appie("\tappie_assocreq", vap->iv_appie_assocreq);
_db_show_appie("\tappie_asscoresp", vap->iv_appie_assocresp);
_db_show_appie("\tappie_wpa", vap->iv_appie_wpa);
if (vap->iv_wpa_ie != NULL || vap->iv_rsn_ie != NULL) {
if (vap->iv_wpa_ie != NULL)
db_printf("\twpa_ie %p", vap->iv_wpa_ie);
if (vap->iv_rsn_ie != NULL)
db_printf("\trsn_ie %p", vap->iv_rsn_ie);
db_printf("\n");
}
db_printf("\tmax_keyix %u", vap->iv_max_keyix);
db_printf(" def_txkey %d", vap->iv_def_txkey);
db_printf("\n");
for (i = 0; i < IEEE80211_WEP_NKID; i++)
_db_show_key("\tnw_keys[%u]", i, &vap->iv_nw_keys[i]);
db_printf("\tauth %p(%s)", vap->iv_auth, vap->iv_auth->ia_name);
db_printf(" ec %p", vap->iv_ec);
db_printf(" acl %p", vap->iv_acl);
db_printf(" as %p", vap->iv_as);
db_printf("\n");
#ifdef IEEE80211_SUPPORT_TDMA
if (vap->iv_tdma != NULL)
_db_show_tdma("\t", vap->iv_tdma, showprocs);
#endif /* IEEE80211_SUPPORT_TDMA */
if (showprocs) {
DB_PRINTSYM("\t", "iv_key_alloc", vap->iv_key_alloc);
DB_PRINTSYM("\t", "iv_key_delete", vap->iv_key_delete);
DB_PRINTSYM("\t", "iv_key_set", vap->iv_key_set);
DB_PRINTSYM("\t", "iv_key_update_begin", vap->iv_key_update_begin);
DB_PRINTSYM("\t", "iv_key_update_end", vap->iv_key_update_end);
DB_PRINTSYM("\t", "iv_opdetach", vap->iv_opdetach);
DB_PRINTSYM("\t", "iv_input", vap->iv_input);
DB_PRINTSYM("\t", "iv_recv_mgmt", vap->iv_recv_mgmt);
DB_PRINTSYM("\t", "iv_deliver_data", vap->iv_deliver_data);
DB_PRINTSYM("\t", "iv_bmiss", vap->iv_bmiss);
DB_PRINTSYM("\t", "iv_reset", vap->iv_reset);
DB_PRINTSYM("\t", "iv_update_beacon", vap->iv_update_beacon);
DB_PRINTSYM("\t", "iv_newstate", vap->iv_newstate);
DB_PRINTSYM("\t", "iv_output", vap->iv_output);
}
}
static void
_db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showprocs)
{
struct ieee80211vap *vap;
db_printf("%p:", ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
db_printf(" %s(%p)", vap->iv_ifp->if_xname, vap);
db_printf("\n");
db_printf("\tifp %p(%s)", ic->ic_ifp, ic->ic_ifp->if_xname);
db_printf(" comlock %p", &ic->ic_comlock);
db_printf("\n");
db_printf("\theadroom %d", ic->ic_headroom);
db_printf(" phytype %d", ic->ic_phytype);
db_printf(" opmode %s", ieee80211_opmode_name[ic->ic_opmode]);
db_printf("\n");
db_printf("\tmedia %p", &ic->ic_media);
db_printf(" inact %p", &ic->ic_inact);
db_printf("\n");
db_printf("\tflags=%b\n", ic->ic_flags, IEEE80211_F_BITS);
db_printf("\tflags_ext=%b\n", ic->ic_flags_ext, IEEE80211_FEXT_BITS);
db_printf("\tflags_ht=%b\n", ic->ic_flags_ht, IEEE80211_FHT_BITS);
db_printf("\tflags_ven=%b\n", ic->ic_flags_ven, IEEE80211_FVEN_BITS);
db_printf("\tcaps=%b\n", ic->ic_caps, IEEE80211_C_BITS);
db_printf("\tcryptocaps=%b\n",
ic->ic_cryptocaps, IEEE80211_CRYPTO_BITS);
db_printf("\thtcaps=%b\n", ic->ic_htcaps, IEEE80211_HTCAP_BITS);
#if 0
uint8_t ic_modecaps[2]; /* set of mode capabilities */
#endif
db_printf("\tcurmode %u", ic->ic_curmode);
db_printf(" promisc %u", ic->ic_promisc);
db_printf(" allmulti %u", ic->ic_allmulti);
db_printf(" nrunning %u", ic->ic_nrunning);
db_printf("\n");
db_printf("\tbintval %u", ic->ic_bintval);
db_printf(" lintval %u", ic->ic_lintval);
db_printf(" holdover %u", ic->ic_holdover);
db_printf(" txpowlimit %u", ic->ic_txpowlimit);
db_printf("\n");
#if 0
struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
#endif
/*
* Channel state:
*
* ic_channels is the set of available channels for the device;
* it is setup by the driver
* ic_nchans is the number of valid entries in ic_channels
* ic_chan_avail is a bit vector of these channels used to check
* whether a channel is available w/o searching the channel table.
* ic_chan_active is a (potentially) constrained subset of
* ic_chan_avail that reflects any mode setting or user-specified
* limit on the set of channels to use/scan
* ic_curchan is the current channel the device is set to; it may
* be different from ic_bsschan when we are off-channel scanning
* or otherwise doing background work
* ic_bsschan is the channel selected for operation; it may
* be undefined (IEEE80211_CHAN_ANYC)
* ic_prevchan is a cached ``previous channel'' used to optimize
* lookups when switching back+forth between two channels
* (e.g. for dynamic turbo)
*/
db_printf("\tnchans %d", ic->ic_nchans);
#if 0
struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX];
uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES];
uint8_t ic_chan_active[IEEE80211_CHAN_BYTES];
uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES];
#endif
db_printf("\n");
_db_show_channel("\tcurchan", ic->ic_curchan);
db_printf("\n");
_db_show_channel("\tbsschan", ic->ic_bsschan);
db_printf("\n");
_db_show_channel("\tprevchan", ic->ic_prevchan);
db_printf("\n");
db_printf("\tregdomain %p", &ic->ic_regdomain);
db_printf("\n");
_db_show_channel("\tcsa_newchan", ic->ic_csa_newchan);
db_printf(" csa_count %d", ic->ic_csa_count);
db_printf( "dfs %p", &ic->ic_dfs);
db_printf("\n");
db_printf("\tscan %p", ic->ic_scan);
db_printf(" lastdata %d", ic->ic_lastdata);
db_printf(" lastscan %d", ic->ic_lastscan);
db_printf("\n");
db_printf("\tmax_keyix %d", ic->ic_max_keyix);
db_printf(" hash_key 0x%x", ic->ic_hash_key);
db_printf(" wme %p", &ic->ic_wme);
if (!showsta)
db_printf(" sta %p", &ic->ic_sta);
db_printf("\n");
db_printf("\tstageq@%p:\n", &ic->ic_stageq);
_db_show_ageq("\t", &ic->ic_stageq);
if (showsta)
_db_show_node_table("\t", &ic->ic_sta);
db_printf("\tprotmode %d", ic->ic_protmode);
db_printf(" nonerpsta %u", ic->ic_nonerpsta);
db_printf(" longslotsta %u", ic->ic_longslotsta);
db_printf(" lastnonerp %d", ic->ic_lastnonerp);
db_printf("\n");
db_printf("\tsta_assoc %u", ic->ic_sta_assoc);
db_printf(" ht_sta_assoc %u", ic->ic_ht_sta_assoc);
db_printf(" ht40_sta_assoc %u", ic->ic_ht40_sta_assoc);
db_printf("\n");
db_printf("\tcurhtprotmode 0x%x", ic->ic_curhtprotmode);
db_printf(" htprotmode %d", ic->ic_htprotmode);
db_printf(" lastnonht %d", ic->ic_lastnonht);
db_printf("\n");
db_printf("\tsuperg %p\n", ic->ic_superg);
db_printf("\tmontaps %d th %p txchan %p rh %p rxchan %p\n",
ic->ic_montaps, ic->ic_th, ic->ic_txchan, ic->ic_rh, ic->ic_rxchan);
if (showprocs) {
DB_PRINTSYM("\t", "ic_vap_create", ic->ic_vap_create);
DB_PRINTSYM("\t", "ic_vap_delete", ic->ic_vap_delete);
#if 0
/* operating mode attachment */
ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX];
#endif
DB_PRINTSYM("\t", "ic_newassoc", ic->ic_newassoc);
DB_PRINTSYM("\t", "ic_getradiocaps", ic->ic_getradiocaps);
DB_PRINTSYM("\t", "ic_setregdomain", ic->ic_setregdomain);
DB_PRINTSYM("\t", "ic_send_mgmt", ic->ic_send_mgmt);
DB_PRINTSYM("\t", "ic_raw_xmit", ic->ic_raw_xmit);
DB_PRINTSYM("\t", "ic_updateslot", ic->ic_updateslot);
DB_PRINTSYM("\t", "ic_update_mcast", ic->ic_update_mcast);
DB_PRINTSYM("\t", "ic_update_promisc", ic->ic_update_promisc);
DB_PRINTSYM("\t", "ic_node_alloc", ic->ic_node_alloc);
DB_PRINTSYM("\t", "ic_node_free", ic->ic_node_free);
DB_PRINTSYM("\t", "ic_node_cleanup", ic->ic_node_cleanup);
DB_PRINTSYM("\t", "ic_node_getrssi", ic->ic_node_getrssi);
DB_PRINTSYM("\t", "ic_node_getsignal", ic->ic_node_getsignal);
DB_PRINTSYM("\t", "ic_node_getmimoinfo", ic->ic_node_getmimoinfo);
DB_PRINTSYM("\t", "ic_scan_start", ic->ic_scan_start);
DB_PRINTSYM("\t", "ic_scan_end", ic->ic_scan_end);
DB_PRINTSYM("\t", "ic_set_channel", ic->ic_set_channel);
DB_PRINTSYM("\t", "ic_scan_curchan", ic->ic_scan_curchan);
DB_PRINTSYM("\t", "ic_scan_mindwell", ic->ic_scan_mindwell);
DB_PRINTSYM("\t", "ic_recv_action", ic->ic_recv_action);
DB_PRINTSYM("\t", "ic_send_action", ic->ic_send_action);
DB_PRINTSYM("\t", "ic_addba_request", ic->ic_addba_request);
DB_PRINTSYM("\t", "ic_addba_response", ic->ic_addba_response);
DB_PRINTSYM("\t", "ic_addba_stop", ic->ic_addba_stop);
}
if (showvaps && !TAILQ_EMPTY(&ic->ic_vaps)) {
db_printf("\n");
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
_db_show_vap(vap, showprocs);
}
if (showsta && !TAILQ_EMPTY(&ic->ic_sta.nt_node)) {
const struct ieee80211_node_table *nt = &ic->ic_sta;
const struct ieee80211_node *ni;
TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
db_printf("\n");
_db_show_sta(ni);
}
}
}
static void
_db_show_node_table(const char *tag, const struct ieee80211_node_table *nt)
{
int i;
db_printf("%s%s@%p:\n", tag, nt->nt_name, nt);
db_printf("%s nodelock %p", tag, &nt->nt_nodelock);
db_printf(" inact_init %d", nt->nt_inact_init);
db_printf(" scanlock %p", &nt->nt_scanlock);
db_printf(" scangen %u\n", nt->nt_scangen);
db_printf("%s keyixmax %d keyixmap %p\n",
tag, nt->nt_keyixmax, nt->nt_keyixmap);
for (i = 0; i < nt->nt_keyixmax; i++) {
const struct ieee80211_node *ni = nt->nt_keyixmap[i];
if (ni != NULL)
db_printf("%s [%3u] %p %s\n", tag, i, ni,
ether_sprintf(ni->ni_macaddr));
}
}
static void
_db_show_channel(const char *tag, const struct ieee80211_channel *c)
{
db_printf("%s ", tag);
if (c == NULL)
db_printf("<NULL>");
else if (c == IEEE80211_CHAN_ANYC)
db_printf("<ANY>");
else
db_printf("[%u (%u) flags=%b maxreg %d maxpow %d minpow %d state 0x%x extieee %u]",
c->ic_freq, c->ic_ieee,
c->ic_flags, IEEE80211_CHAN_BITS,
c->ic_maxregpower, c->ic_maxpower, c->ic_minpower,
c->ic_state, c->ic_extieee);
}
static void
_db_show_ssid(const char *tag, int ix, int len, const uint8_t *ssid)
{
const uint8_t *p;
int i;
db_printf(tag, ix);
if (len > IEEE80211_NWID_LEN)
len = IEEE80211_NWID_LEN;
/* determine printable or not */
for (i = 0, p = ssid; i < len; i++, p++) {
if (*p < ' ' || *p > 0x7e)
break;
}
if (i == len) {
db_printf("\"");
for (i = 0, p = ssid; i < len; i++, p++)
db_printf("%c", *p);
db_printf("\"");
} else {
db_printf("0x");
for (i = 0, p = ssid; i < len; i++, p++)
db_printf("%02x", *p);
}
}
static void
_db_show_appie(const char *tag, const struct ieee80211_appie *ie)
{
const uint8_t *p;
int i;
if (ie == NULL)
return;
db_printf("%s [0x", tag);
for (i = 0, p = ie->ie_data; i < ie->ie_len; i++, p++)
db_printf("%02x", *p);
db_printf("]\n");
}
static void
_db_show_key(const char *tag, int ix, const struct ieee80211_key *wk)
{
static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
const struct ieee80211_cipher *cip = wk->wk_cipher;
int keylen = wk->wk_keylen;
db_printf(tag, ix);
switch (cip->ic_cipher) {
case IEEE80211_CIPHER_WEP:
/* compatibility */
db_printf(" wepkey %u:%s", wk->wk_keyix,
keylen <= 5 ? "40-bit" :
keylen <= 13 ? "104-bit" : "128-bit");
break;
case IEEE80211_CIPHER_TKIP:
if (keylen > 128/8)
keylen -= 128/8; /* ignore MIC for now */
db_printf(" TKIP %u:%u-bit", wk->wk_keyix, 8*keylen);
break;
case IEEE80211_CIPHER_AES_OCB:
db_printf(" AES-OCB %u:%u-bit", wk->wk_keyix, 8*keylen);
break;
case IEEE80211_CIPHER_AES_CCM:
db_printf(" AES-CCM %u:%u-bit", wk->wk_keyix, 8*keylen);
break;
case IEEE80211_CIPHER_CKIP:
db_printf(" CKIP %u:%u-bit", wk->wk_keyix, 8*keylen);
break;
case IEEE80211_CIPHER_NONE:
db_printf(" NULL %u:%u-bit", wk->wk_keyix, 8*keylen);
break;
default:
db_printf(" UNKNOWN (0x%x) %u:%u-bit",
cip->ic_cipher, wk->wk_keyix, 8*keylen);
break;
}
if (wk->wk_rxkeyix != wk->wk_keyix)
db_printf(" rxkeyix %u", wk->wk_rxkeyix);
if (memcmp(wk->wk_key, zerodata, keylen) != 0) {
int i;
db_printf(" <");
for (i = 0; i < keylen; i++)
db_printf("%02x", wk->wk_key[i]);
db_printf(">");
if (cip->ic_cipher != IEEE80211_CIPHER_WEP &&
wk->wk_keyrsc[IEEE80211_NONQOS_TID] != 0)
db_printf(" rsc %ju", (uintmax_t)wk->wk_keyrsc[IEEE80211_NONQOS_TID]);
if (cip->ic_cipher != IEEE80211_CIPHER_WEP &&
wk->wk_keytsc != 0)
db_printf(" tsc %ju", (uintmax_t)wk->wk_keytsc);
db_printf(" flags=%b", wk->wk_flags, IEEE80211_KEY_BITS);
}
db_printf("\n");
}
static void
printrate(const char *tag, int v)
{
if (v == IEEE80211_FIXED_RATE_NONE)
db_printf(" %s <none>", tag);
else if (v == 11)
db_printf(" %s 5.5", tag);
else if (v & IEEE80211_RATE_MCS)
db_printf(" %s MCS%d", tag, v &~ IEEE80211_RATE_MCS);
else
db_printf(" %s %d", tag, v/2);
}
static void
_db_show_roamparams(const char *tag, const void *arg,
const struct ieee80211_roamparam *rp)
{
db_printf(tag, arg);
if (rp->rssi & 1)
db_printf(" rssi %u.5", rp->rssi/2);
else
db_printf(" rssi %u", rp->rssi/2);
printrate("rate", rp->rate);
}
static void
_db_show_txparams(const char *tag, const void *arg,
const struct ieee80211_txparam *tp)
{
db_printf(tag, arg);
printrate("ucastrate", tp->ucastrate);
printrate("mcastrate", tp->mcastrate);
printrate("mgmtrate", tp->mgmtrate);
db_printf(" maxretry %d", tp->maxretry);
}
static void
_db_show_ageq(const char *tag, const struct ieee80211_ageq *q)
{
const struct mbuf *m;
db_printf("%s lock %p len %d maxlen %d drops %d head %p tail %p\n",
tag, &q->aq_lock, q->aq_len, q->aq_maxlen, q->aq_drops,
q->aq_head, q->aq_tail);
for (m = q->aq_head; m != NULL; m = m->m_nextpkt)
db_printf("%s %p (len %d, %b)\n", tag, m, m->m_len,
/* XXX could be either TX or RX but is mostly TX */
m->m_flags, IEEE80211_MBUF_TX_FLAG_BITS);
}
static void
_db_show_stats(const struct ieee80211_stats *is)
{
}
#ifdef IEEE80211_SUPPORT_MESH
static void
_db_show_mesh(const struct ieee80211_mesh_state *ms)
{
struct ieee80211_mesh_route *rt;
int i;
_db_show_ssid(" meshid ", 0, ms->ms_idlen, ms->ms_id);
db_printf("nextseq %u ttl %u flags 0x%x\n", ms->ms_seq,
ms->ms_ttl, ms->ms_flags);
db_printf("routing table:\n");
i = 0;
TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) {
db_printf("entry %d:\tdest: %6D nexthop: %6D metric: %u", i,
rt->rt_dest, ":", rt->rt_nexthop, ":", rt->rt_metric);
db_printf("\tlifetime: %u lastseq: %u priv: %p\n",
rt->rt_lifetime, rt->rt_lastmseq, rt->rt_priv);
i++;
}
}
#endif /* IEEE80211_SUPPORT_MESH */
#endif /* DDB */
@@ -0,0 +1,375 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
#ifdef __FreeBSD__
__FBSDID("$FreeBSD$");
#endif
/*
* IEEE 802.11 DFS/Radar support.
*/
#include "opt_inet.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/endian.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net80211/ieee80211_var.h>
static int ieee80211_nol_timeout = 30*60; /* 30 minutes */
SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW,
&ieee80211_nol_timeout, 0, "NOL timeout (secs)");
#define NOL_TIMEOUT msecs_to_ticks(ieee80211_nol_timeout*1000)
static int ieee80211_cac_timeout = 60; /* 60 seconds */
SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW,
&ieee80211_cac_timeout, 0, "CAC timeout (secs)");
#define CAC_TIMEOUT msecs_to_ticks(ieee80211_cac_timeout*1000)
void
ieee80211_dfs_attach(struct ieee80211com *ic)
{
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
callout_init_mtx(&dfs->nol_timer, IEEE80211_LOCK_OBJ(ic), 0);
callout_init_mtx(&dfs->cac_timer, IEEE80211_LOCK_OBJ(ic), 0);
}
void
ieee80211_dfs_detach(struct ieee80211com *ic)
{
/* NB: we assume no locking is needed */
ieee80211_dfs_reset(ic);
}
void
ieee80211_dfs_reset(struct ieee80211com *ic)
{
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
int i;
/* NB: we assume no locking is needed */
/* NB: cac_timer should be cleared by the state machine */
callout_drain(&dfs->nol_timer);
for (i = 0; i < ic->ic_nchans; i++)
ic->ic_channels[i].ic_state = 0;
dfs->lastchan = NULL;
}
static void
cac_timeout(void *arg)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
int i;
IEEE80211_LOCK_ASSERT(ic);
if (vap->iv_state != IEEE80211_S_CAC) /* NB: just in case */
return;
/*
* When radar is detected during a CAC we are woken
* up prematurely to switch to a new channel.
* Check the channel to decide how to act.
*/
if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan)) {
ieee80211_notify_cac(ic, ic->ic_curchan,
IEEE80211_NOTIFY_CAC_RADAR);
if_printf(vap->iv_ifp,
"CAC timer on channel %u (%u MHz) stopped due to radar\n",
ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
/* XXX clobbers any existing desired channel */
/* NB: dfs->newchan may be NULL, that's ok */
vap->iv_des_chan = dfs->newchan;
/* XXX recursive lock need ieee80211_new_state_locked */
ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
} else {
if_printf(vap->iv_ifp,
"CAC timer on channel %u (%u MHz) expired; "
"no radar detected\n",
ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
/*
* Mark all channels with the current frequency
* as having completed CAC; this keeps us from
* doing it again until we change channels.
*/
for (i = 0; i < ic->ic_nchans; i++) {
struct ieee80211_channel *c = &ic->ic_channels[i];
if (c->ic_freq == ic->ic_curchan->ic_freq)
c->ic_state |= IEEE80211_CHANSTATE_CACDONE;
}
ieee80211_notify_cac(ic, ic->ic_curchan,
IEEE80211_NOTIFY_CAC_EXPIRE);
ieee80211_cac_completeswitch(vap);
}
}
/*
* Initiate the CAC timer. The driver is responsible
* for setting up the hardware to scan for radar on the
* channnel, we just handle timing things out.
*/
void
ieee80211_dfs_cac_start(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
IEEE80211_LOCK_ASSERT(ic);
callout_reset(&dfs->cac_timer, CAC_TIMEOUT, cac_timeout, vap);
if_printf(vap->iv_ifp, "start %d second CAC timer on channel %u (%u MHz)\n",
ticks_to_secs(CAC_TIMEOUT),
ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_START);
}
/*
* Clear the CAC timer.
*/
void
ieee80211_dfs_cac_stop(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
IEEE80211_LOCK_ASSERT(ic);
/* NB: racey but not important */
if (callout_pending(&dfs->cac_timer)) {
if_printf(vap->iv_ifp, "stop CAC timer on channel %u (%u MHz)\n",
ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
ieee80211_notify_cac(ic, ic->ic_curchan,
IEEE80211_NOTIFY_CAC_STOP);
}
callout_stop(&dfs->cac_timer);
}
void
ieee80211_dfs_cac_clear(struct ieee80211com *ic,
const struct ieee80211_channel *chan)
{
int i;
for (i = 0; i < ic->ic_nchans; i++) {
struct ieee80211_channel *c = &ic->ic_channels[i];
if (c->ic_freq == chan->ic_freq)
c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
}
}
static void
dfs_timeout(void *arg)
{
struct ieee80211com *ic = arg;
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
struct ieee80211_channel *c;
int i, oldest, now;
IEEE80211_LOCK_ASSERT(ic);
now = oldest = ticks;
for (i = 0; i < ic->ic_nchans; i++) {
c = &ic->ic_channels[i];
if (IEEE80211_IS_CHAN_RADAR(c)) {
if (time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) {
c->ic_state &= ~IEEE80211_CHANSTATE_RADAR;
if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) {
/*
* NB: do this here so we get only one
* msg instead of one for every channel
* table entry.
*/
if_printf(ic->ic_ifp, "radar on channel"
" %u (%u MHz) cleared after timeout\n",
c->ic_ieee, c->ic_freq);
/* notify user space */
c->ic_state &=
~IEEE80211_CHANSTATE_NORADAR;
ieee80211_notify_radar(ic, c);
}
} else if (dfs->nol_event[i] < oldest)
oldest = dfs->nol_event[i];
}
}
if (oldest != now) {
/* arrange to process next channel up for a status change */
callout_schedule(&dfs->nol_timer, oldest + NOL_TIMEOUT - now);
}
}
static void
announce_radar(struct ifnet *ifp, const struct ieee80211_channel *curchan,
const struct ieee80211_channel *newchan)
{
if (newchan == NULL)
if_printf(ifp, "radar detected on channel %u (%u MHz)\n",
curchan->ic_ieee, curchan->ic_freq);
else
if_printf(ifp, "radar detected on channel %u (%u MHz), "
"moving to channel %u (%u MHz)\n",
curchan->ic_ieee, curchan->ic_freq,
newchan->ic_ieee, newchan->ic_freq);
}
/*
* Handle a radar detection event on a channel. The channel is
* added to the NOL list and we record the time of the event.
* Entries are aged out after NOL_TIMEOUT. If radar was
* detected while doing CAC we force a state/channel change.
* Otherwise radar triggers a channel switch using the CSA
* mechanism (when the channel is the bss channel).
*/
void
ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *chan)
{
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
int i, now;
IEEE80211_LOCK_ASSERT(ic);
/*
* Mark all entries with this frequency. Notify user
* space and arrange for notification when the radar
* indication is cleared. Then kick the NOL processing
* thread if not already running.
*/
now = ticks;
for (i = 0; i < ic->ic_nchans; i++) {
struct ieee80211_channel *c = &ic->ic_channels[i];
if (c->ic_freq == chan->ic_freq) {
c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
c->ic_state |= IEEE80211_CHANSTATE_RADAR;
dfs->nol_event[i] = now;
}
}
ieee80211_notify_radar(ic, chan);
chan->ic_state |= IEEE80211_CHANSTATE_NORADAR;
if (!callout_pending(&dfs->nol_timer))
callout_reset(&dfs->nol_timer, NOL_TIMEOUT, dfs_timeout, ic);
/*
* If radar is detected on the bss channel while
* doing CAC; force a state change by scheduling the
* callout to be dispatched asap. Otherwise, if this
* event is for the bss channel then we must quiet
* traffic and schedule a channel switch.
*
* Note this allows us to receive notification about
* channels other than the bss channel; not sure
* that can/will happen but it's simple to support.
*/
if (chan == ic->ic_bsschan) {
/* XXX need a way to defer to user app */
dfs->newchan = ieee80211_dfs_pickchannel(ic);
announce_radar(ic->ic_ifp, chan, dfs->newchan);
if (callout_pending(&dfs->cac_timer))
callout_schedule(&dfs->cac_timer, 0);
else if (dfs->newchan != NULL) {
/* XXX mode 1, switch count 2 */
/* XXX calculate switch count based on max
switch time and beacon interval? */
ieee80211_csa_startswitch(ic, dfs->newchan, 1, 2);
} else {
/*
* Spec says to stop all transmissions and
* wait on the current channel for an entry
* on the NOL to expire.
*/
/*XXX*/
}
} else {
/*
* Issue rate-limited console msgs.
*/
if (dfs->lastchan != chan) {
dfs->lastchan = chan;
dfs->cureps = 0;
announce_radar(ic->ic_ifp, chan, NULL);
} else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) {
announce_radar(ic->ic_ifp, chan, NULL);
}
}
}
struct ieee80211_channel *
ieee80211_dfs_pickchannel(struct ieee80211com *ic)
{
struct ieee80211_channel *c;
int i, flags;
uint16_t v;
/*
* Consult the scan cache first.
*/
flags = ic->ic_curchan->ic_flags & IEEE80211_CHAN_ALL;
/*
* XXX if curchan is HT this will never find a channel
* XXX 'cuz we scan only legacy channels
*/
c = ieee80211_scan_pickchannel(ic, flags);
if (c != NULL)
return c;
/*
* No channel found in scan cache; select a compatible
* one at random (skipping channels where radar has
* been detected).
*/
get_random_bytes(&v, sizeof(v));
v %= ic->ic_nchans;
for (i = v; i < ic->ic_nchans; i++) {
c = &ic->ic_channels[i];
if (!IEEE80211_IS_CHAN_RADAR(c) &&
(c->ic_flags & flags) == flags)
return c;
}
for (i = 0; i < v; i++) {
c = &ic->ic_channels[i];
if (!IEEE80211_IS_CHAN_RADAR(c) &&
(c->ic_flags & flags) == flags)
return c;
}
if_printf(ic->ic_ifp, "HELP, no channel located to switch to!\n");
return NULL;
}
@@ -0,0 +1,57 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_DFS_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_DFS_H_
/*
* 802.11h/DFS definitions.
*/
struct ieee80211_dfs_state {
int nol_event[IEEE80211_CHAN_MAX];
struct callout nol_timer; /* NOL list processing */
struct callout cac_timer; /* CAC timer */
struct timeval lastevent; /* time of last radar event */
int cureps; /* current events/second */
const struct ieee80211_channel *lastchan;/* chan w/ last radar event */
struct ieee80211_channel *newchan; /* chan selected next */
};
void ieee80211_dfs_attach(struct ieee80211com *);
void ieee80211_dfs_detach(struct ieee80211com *);
void ieee80211_dfs_reset(struct ieee80211com *);
void ieee80211_dfs_cac_start(struct ieee80211vap *);
void ieee80211_dfs_cac_stop(struct ieee80211vap *);
void ieee80211_dfs_cac_clear(struct ieee80211com *,
const struct ieee80211_channel *);
void ieee80211_dfs_notify_radar(struct ieee80211com *,
struct ieee80211_channel *);
struct ieee80211_channel *ieee80211_dfs_pickchannel(struct ieee80211com *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_DFS_H_ */
@@ -0,0 +1,654 @@
/*
* Copyright 2009, Colin Günther, [email protected].
* All rights reserved. Distributed under the terms of the MIT License.
*/
/*-
* Copyright (c) 2003-2009 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.
*
* 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.
*/
/*
* IEEE 802.11 support (Haiku-specific code)
*/
#include <SupportDefs.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/bus.h>
#include <sys/haiku-module.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_var.h>
#include <ieee80211_var.h>
#include <ether_driver.h>
#include <bosii_driver.h>
#include <shared.h>
#define MC_ALIGN(m, len) \
do { \
(m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1);\
} while (/* CONSTCOND */ 0)
status_t
init_wlan_stack()
{
ieee80211_phy_init();
ieee80211_auth_setup();
ieee80211_ht_init();
return B_OK;
}
void
uninit_wlan_stack()
{
}
status_t
start_wlan(device_t device)
{
int i;
int unit = device_get_unit(device);
struct ifnet *ifp;
struct ieee80211com *ic;
struct ieee80211vap *vap;
for (i = 0; i < MAX_DEVICES; i++) {
if (gDevices[i] != NULL && gDevices[i]->if_dunit == unit)
break;
}
if (i == MAX_DEVICES)
return B_ERROR;
ifp = gDevices[i];
// This ensures that the cloned device gets
// the same index assigned as the base device
// Resulting in the same device name
// e.g.: /dev/net/atheros/0 instead of
// /dev/net/atheros/1
gDevices[i] = NULL;
ic = ifp->if_l2com;
vap = ic->ic_vap_create(ic,
"wlan",
unit,
IEEE80211_M_STA, // mode
0, // flags
NULL, // BSSID
IF_LLADDR(ifp)); // MAC address
if (vap == NULL) {
gDevices[i] = ifp;
return B_ERROR;
}
// ic_vap_create() established that gDevices[i] links to vap->iv_ifp now
KASSERT(gDevices[i] == vap->iv_ifp,
"start_wlan: gDevices[i] != vap->iv_ifp");
vap->iv_ifp->scan_done_sem = create_sem(0, "wlan scan done");
// We aren't connected to a WLAN, yet.
if_link_state_change(vap->iv_ifp, LINK_STATE_DOWN);
dprintf("%s: wlan started.\n", __func__);
return B_OK;
}
status_t
stop_wlan(device_t device)
{
int i;
int unit = device_get_unit(device);
struct ifnet *ifp;
struct ieee80211com *ic;
struct ieee80211vap *vap;
for (i = 0; i < MAX_DEVICES; i++) {
if (gDevices[i] != NULL && gDevices[i]->if_dunit == unit)
break;
}
if (i == MAX_DEVICES)
return B_ERROR;
ifp = gDevices[i];
if (ifp->if_type == IFT_IEEE80211)
// This happens when there was an error
// in starting the wlan before, resulting
// in never creating a clone device
return B_OK;
delete_sem(ifp->scan_done_sem);
vap = ifp->if_softc;
ic = vap->iv_ic;
ic->ic_vap_delete(vap);
// ic_vap_delete freed gDevices[i]
KASSERT(gDevices[i] == NULL, "stop_wlan: gDevices[i] != NULL");
// assign the base device ifp again
gDevices[i] = ic->ic_ifp;
return B_OK;
}
status_t
wlan_control(void *cookie, uint32 op, void *arg, size_t length)
{
struct ifnet* ifp = cookie;
switch (op) {
case BOSII_DEVICE:
return B_OK;
case BOSII_DETECT_NETWORKS:
{
struct ieee80211req request;
struct ieee80211_scan_req scanRequest;
if_printf(ifp, "%s: BOSII_DETECT_NETWORKS\n", __func__);
memset(&scanRequest, 0, sizeof(scanRequest));
scanRequest.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
| IEEE80211_IOC_SCAN_NOPICK
| IEEE80211_IOC_SCAN_ONCE;
scanRequest.sr_duration = 10000; // 10 s
scanRequest.sr_nssid = 0;
memset(&request, 0, sizeof(request));
request.i_type = IEEE80211_IOC_SCAN_REQ;
request.i_data = &scanRequest;
request.i_len = sizeof(scanRequest);
ifp->if_ioctl(ifp, SIOCS80211, (caddr_t)&request);
acquire_sem_etc(ifp->scan_done_sem, 1, B_RELATIVE_TIMEOUT,
10000000); // 10 s
return B_OK;
}
case BOSII_GET_DETECTED_NETWORKS:
{
struct ieee80211req request;
struct ifreq ifRequest;
struct route_entry* networkRequest = &ifRequest.ifr_route;
if_printf(ifp, "%s: BOSII_GET_DETECTED_NETWORKS\n", __func__);
if (length < sizeof(struct ieee80211req_scan_result))
return B_BAD_VALUE;
if (user_memcpy(&ifRequest, arg, sizeof(ifRequest)) < B_OK)
return B_BAD_ADDRESS;
memset(&request, 0, sizeof(request));
request.i_type = IEEE80211_IOC_SCAN_RESULTS;
request.i_len = length;
request.i_data = networkRequest->destination;
// After return value of request.i_data is copied into user
// space, already.
if (ifp->if_ioctl(ifp, SIOCG80211, (caddr_t)&request) < B_OK)
return B_BAD_ADDRESS;
// Tell the user space how much data was copied
networkRequest->mtu = request.i_len;
if (user_memcpy(arg + offsetof(struct ifreq, ifr_route.mtu),
&networkRequest->mtu, sizeof(networkRequest->mtu)) < B_OK)
return B_BAD_ADDRESS;
return B_OK;
}
case BOSII_JOIN_NETWORK:
{
struct ieee80211req request;
struct ifreq ifRequest;
struct route_entry* networkRequest = &ifRequest.ifr_route;
struct ieee80211req_scan_result network;
if_printf(ifp, "%s: BOSII_JOIN_NETWORK\n", __func__);
if (length < sizeof(struct ifreq))
return B_BAD_VALUE;
if (user_memcpy(&ifRequest, arg, sizeof(ifRequest)) < B_OK)
return B_BAD_ADDRESS;
if (user_memcpy(&network, networkRequest->source,
sizeof(network)) < B_OK)
return B_BAD_ADDRESS;
memset(&request, 0, sizeof(request));
request.i_type = IEEE80211_IOC_SSID;
request.i_val = 0;
request.i_len = network.isr_ssid_len;
request.i_data = ((void*) networkRequest->source)
+ network.isr_ie_off;
if (ifp->if_ioctl(ifp, SIOCS80211, (caddr_t)&request) < B_OK)
return B_ERROR;
// wait for network join
return B_OK;
}
case BOSII_GET_ASSOCIATED_NETWORK:
{
struct ieee80211req request;
struct ifreq ifRequest;
struct route_entry* networkRequest = &ifRequest.ifr_route;
if_printf(ifp, "%s: BOSII_GET_ASSOCIATED_NETWORK\n", __func__);
if (length < sizeof(struct ieee80211req_sta_req))
return B_BAD_VALUE;
if (user_memcpy(&ifRequest, arg, sizeof(ifRequest)) < B_OK)
return B_BAD_ADDRESS;
// Only want station information about associated network.
memset(&request, 0, sizeof(request));
request.i_type = IEEE80211_IOC_BSSID;
request.i_len = IEEE80211_ADDR_LEN;
request.i_data = ((struct ieee80211req_sta_req*)networkRequest->
destination)->is_u.macaddr;
if (ifp->if_ioctl(ifp, SIOCG80211, (caddr_t)&request) < B_OK)
return B_BAD_ADDRESS;
request.i_type = IEEE80211_IOC_STA_INFO;
request.i_len = length;
request.i_data = networkRequest->destination;
// After return value of request.i_data is copied into user
// space, already.
if (ifp->if_ioctl(ifp, SIOCG80211, (caddr_t)&request) < B_OK)
return B_BAD_ADDRESS;
// Tell the user space how much data was copied
networkRequest->mtu = request.i_len;
if (user_memcpy(arg + offsetof(struct ifreq, ifr_route.mtu),
&networkRequest->mtu, sizeof(networkRequest->mtu)) < B_OK)
return B_BAD_ADDRESS;
return B_OK;
}
}
return B_BAD_VALUE;
}
status_t
wlan_close(void *cookie)
{
struct ifnet* ifp = cookie;
ifp->if_flags &= ~IFF_UP;
ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
return release_sem_etc(ifp->scan_done_sem, 1, B_RELEASE_ALL);
}
status_t
wlan_if_l2com_alloc(void *data)
{
struct ifnet* ifp = data;
ifp->if_l2com = _kernel_malloc(sizeof(struct ieee80211com), M_ZERO);
((struct ieee80211com *)(ifp->if_l2com))->ic_ifp = ifp;
if (ifp->if_l2com == NULL)
return B_NO_MEMORY;
return B_OK;
}
void
get_random_bytes(void *p, size_t n)
{
uint8_t *dp = p;
while (n > 0) {
uint32_t v = arc4random();
size_t nb = n > sizeof(uint32_t) ? sizeof(uint32_t) : n;
bcopy(&v, dp, n > sizeof(uint32_t) ? sizeof(uint32_t) : n);
dp += sizeof(uint32_t), n -= nb;
}
}
struct mbuf *
ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen)
{
struct mbuf *m;
u_int len;
len = roundup2(headroom + pktlen, 4);
KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
if (len < MINCLSIZE) {
m = m_gethdr(M_NOWAIT, MT_DATA);
if (m != NULL)
MH_ALIGN(m, len);
} else {
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
if (m != NULL)
MC_ALIGN(m, len);
}
if (m != NULL) {
m->m_data += headroom;
*frm = m->m_data;
}
return m;
}
/*
* Decrements the reference-counter and
* tests whether it became zero.
* NB: The negation is not atomar. Must work until sth. better is found.
*
* @return 1 reference-counter became zero
* @return 0 reference-counter didn't became zero
*/
int
ieee80211_node_dectestref(struct ieee80211_node *ni)
{
/* XXX need equivalent of atomic_dec_and_test */
atomic_add((vint32 *)&ni->ni_refcnt, -1);
return !atomic_add((vint32 *)&ni->ni_refcnt, 0);
}
void
ieee80211_drain_ifq(struct ifqueue *ifq)
{
struct ieee80211_node *ni;
struct mbuf *m;
for (;;) {
IF_DEQUEUE(ifq, m);
if (m == NULL)
break;
ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
KASSERT(ni != NULL, ("frame w/o node"));
ieee80211_free_node(ni);
m->m_pkthdr.rcvif = NULL;
m_freem(m);
}
}
void
ieee80211_flush_ifq(struct ifqueue *ifq, struct ieee80211vap *vap)
{
struct ieee80211_node *ni;
struct mbuf *m, **mprev;
IF_LOCK(ifq);
mprev = &ifq->ifq_head;
while ((m = *mprev) != NULL) {
ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
if (ni != NULL && ni->ni_vap == vap) {
*mprev = m->m_nextpkt; /* remove from list */
ifq->ifq_len--;
m_freem(m);
ieee80211_free_node(ni); /* reclaim ref */
} else
mprev = &m->m_nextpkt;
}
/* recalculate tail ptr */
m = ifq->ifq_head;
for (; m != NULL && m->m_nextpkt != NULL; m = m->m_nextpkt)
;
ifq->ifq_tail = m;
IF_UNLOCK(ifq);
}
int
ieee80211_add_callback(struct mbuf *m,
void (*func)(struct ieee80211_node *, void *, int), void *arg)
{
struct m_tag *mtag;
struct ieee80211_cb *cb;
mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_CALLBACK,
sizeof(struct ieee80211_cb), M_NOWAIT);
if (mtag == NULL)
return 0;
cb = (struct ieee80211_cb *)(mtag+1);
cb->func = func;
cb->arg = arg;
m_tag_prepend(m, mtag);
m->m_flags |= M_TXCB;
return 1;
}
void
ieee80211_process_callback(struct ieee80211_node *ni,
struct mbuf *m, int status)
{
struct m_tag *mtag;
mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_CALLBACK, NULL);
if (mtag != NULL) {
struct ieee80211_cb *cb = (struct ieee80211_cb *)(mtag+1);
cb->func(ni, cb->arg, status);
}
}
void
ieee80211_sysctl_vattach(struct ieee80211vap *vap)
{
vap->iv_debug = IEEE80211_MSG_XRATE
| IEEE80211_MSG_ELEMID
| IEEE80211_MSG_NODE
| IEEE80211_MSG_ASSOC
| IEEE80211_MSG_AUTH
| IEEE80211_MSG_SCAN
| IEEE80211_MSG_STATE
| IEEE80211_MSG_POWER
| IEEE80211_MSG_WME
| IEEE80211_MSG_DOTH
| IEEE80211_MSG_INACT
| IEEE80211_MSG_ROAM
| IEEE80211_MSG_RATECTL
;
}
void
ieee80211_sysctl_vdetach(struct ieee80211vap *vap)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_vap_destroy(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
ic->ic_vap_delete(vap);
dprintf("%s: done.\n", __func__);
}
void
ieee80211_load_module(const char *modname)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ifnet *ifp = vap->iv_ifp;
if (ni == vap->iv_bss) {
if_link_state_change(ifp, LINK_STATE_UP);
}
}
void
ieee80211_notify_node_leave(struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ifnet *ifp = vap->iv_ifp;
if (ni == vap->iv_bss) {
if_link_state_change(ifp, LINK_STATE_DOWN);
}
}
void
ieee80211_notify_scan_done(struct ieee80211vap *vap)
{
release_sem_etc(vap->iv_ifp->scan_done_sem, 1,
B_DO_NOT_RESCHEDULE | B_RELEASE_ALL);
}
void
ieee80211_notify_replay_failure(struct ieee80211vap *vap,
const struct ieee80211_frame *wh, const struct ieee80211_key *k,
u_int64_t rsc, int tid)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_notify_michael_failure(struct ieee80211vap *vap,
const struct ieee80211_frame *wh, u_int keyix)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_notify_wds_discover(struct ieee80211_node *ni)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_notify_csa(struct ieee80211com *ic,
const struct ieee80211_channel *c, int mode, int count)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_notify_radar(struct ieee80211com *ic,
const struct ieee80211_channel *c)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_notify_cac(struct ieee80211com *ic,
const struct ieee80211_channel *c, enum ieee80211_notify_cac_event type)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_notify_node_deauth(struct ieee80211_node *ni)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_notify_node_auth(struct ieee80211_node *ni)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_notify_country(struct ieee80211vap *vap,
const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2])
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_notify_radio(struct ieee80211com *ic, int state)
{
dprintf("%s not implemented, yet.\n", __func__);
}
void
ieee80211_sysctl_attach(struct ieee80211com *ic)
{
}
void
ieee80211_sysctl_detach(struct ieee80211com *ic)
{
}
@@ -0,0 +1,321 @@
/*-
* Copyright (c) 2003-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_HAIKU_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_HAIKU_H_
#ifdef _KERNEL
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <sys/taskqueue.h>
/*
* Common state locking definitions.
*/
typedef struct {
char name[16]; /* e.g. "ath0_com_lock" */
struct mtx mtx;
} ieee80211_com_lock_t;
#define IEEE80211_LOCK_INIT(_ic, _name) do { \
ieee80211_com_lock_t *cl = &(_ic)->ic_comlock; \
snprintf(cl->name, sizeof(cl->name), "%s_com_lock", _name); \
mtx_init(&cl->mtx, cl->name, NULL, MTX_DEF | MTX_RECURSE); \
} while (0)
#define IEEE80211_LOCK_OBJ(_ic) (&(_ic)->ic_comlock.mtx)
#define IEEE80211_LOCK_DESTROY(_ic) mtx_destroy(IEEE80211_LOCK_OBJ(_ic))
#define IEEE80211_LOCK(_ic) mtx_lock(IEEE80211_LOCK_OBJ(_ic))
#define IEEE80211_UNLOCK(_ic) mtx_unlock(IEEE80211_LOCK_OBJ(_ic))
#define IEEE80211_LOCK_ASSERT(_ic) \
mtx_assert(IEEE80211_LOCK_OBJ(_ic), MA_OWNED)
/*
* Node locking definitions.
*/
typedef struct {
char name[16]; /* e.g. "ath0_node_lock" */
struct mtx mtx;
} ieee80211_node_lock_t;
#define IEEE80211_NODE_LOCK_INIT(_nt, _name) do { \
ieee80211_node_lock_t *nl = &(_nt)->nt_nodelock; \
snprintf(nl->name, sizeof(nl->name), "%s_node_lock", _name); \
mtx_init(&nl->mtx, nl->name, NULL, MTX_DEF | MTX_RECURSE); \
} while (0)
#define IEEE80211_NODE_LOCK_OBJ(_nt) (&(_nt)->nt_nodelock.mtx)
#define IEEE80211_NODE_LOCK_DESTROY(_nt) \
mtx_destroy(IEEE80211_NODE_LOCK_OBJ(_nt))
#define IEEE80211_NODE_LOCK(_nt) \
mtx_lock(IEEE80211_NODE_LOCK_OBJ(_nt))
#define IEEE80211_NODE_IS_LOCKED(_nt) \
mtx_owned(IEEE80211_NODE_LOCK_OBJ(_nt))
#define IEEE80211_NODE_UNLOCK(_nt) \
mtx_unlock(IEEE80211_NODE_LOCK_OBJ(_nt))
#define IEEE80211_NODE_LOCK_ASSERT(_nt) \
mtx_assert(IEEE80211_NODE_LOCK_OBJ(_nt), MA_OWNED)
/*
* Node table iteration locking definitions; this protects the
* scan generation # used to iterate over the station table
* while grabbing+releasing the node lock.
*/
typedef struct {
char name[16]; /* e.g. "ath0_scan_lock" */
struct mtx mtx;
} ieee80211_scan_lock_t;
#define IEEE80211_NODE_ITERATE_LOCK_INIT(_nt, _name) do { \
ieee80211_scan_lock_t *sl = &(_nt)->nt_scanlock; \
snprintf(sl->name, sizeof(sl->name), "%s_scan_lock", _name); \
mtx_init(&sl->mtx, sl->name, NULL, MTX_DEF); \
} while (0)
#define IEEE80211_NODE_ITERATE_LOCK_OBJ(_nt) (&(_nt)->nt_scanlock.mtx)
#define IEEE80211_NODE_ITERATE_LOCK_DESTROY(_nt) \
mtx_destroy(IEEE80211_NODE_ITERATE_LOCK_OBJ(_nt))
#define IEEE80211_NODE_ITERATE_LOCK(_nt) \
mtx_lock(IEEE80211_NODE_ITERATE_LOCK_OBJ(_nt))
#define IEEE80211_NODE_ITERATE_UNLOCK(_nt) \
mtx_unlock(IEEE80211_NODE_ITERATE_LOCK_OBJ(_nt))
/*
* Power-save queue definitions.
*/
typedef struct mtx ieee80211_psq_lock_t;
#define IEEE80211_PSQ_INIT(_psq, _name) \
mtx_init(&(_psq)->psq_lock, _name, "802.11 ps q", MTX_DEF)
#define IEEE80211_PSQ_DESTROY(_psq) mtx_destroy(&(_psq)->psq_lock)
#define IEEE80211_PSQ_LOCK(_psq) mtx_lock(&(_psq)->psq_lock)
#define IEEE80211_PSQ_UNLOCK(_psq) mtx_unlock(&(_psq)->psq_lock)
#ifndef IF_PREPEND_LIST
#define _IF_PREPEND_LIST(ifq, mhead, mtail, mcount) do { \
(mtail)->m_nextpkt = (ifq)->ifq_head; \
if ((ifq)->ifq_tail == NULL) \
(ifq)->ifq_tail = (mtail); \
(ifq)->ifq_head = (mhead); \
(ifq)->ifq_len += (mcount); \
} while (0)
#define IF_PREPEND_LIST(ifq, mhead, mtail, mcount) do { \
IF_LOCK(ifq); \
_IF_PREPEND_LIST(ifq, mhead, mtail, mcount); \
IF_UNLOCK(ifq); \
} while (0)
#endif /* IF_PREPEND_LIST */
/*
* Age queue definitions.
*/
typedef struct mtx ieee80211_ageq_lock_t;
#define IEEE80211_AGEQ_INIT(_aq, _name) \
mtx_init(&(_aq)->aq_lock, _name, "802.11 age q", MTX_DEF)
#define IEEE80211_AGEQ_DESTROY(_aq) mtx_destroy(&(_aq)->aq_lock)
#define IEEE80211_AGEQ_LOCK(_aq) mtx_lock(&(_aq)->aq_lock)
#define IEEE80211_AGEQ_UNLOCK(_aq) mtx_unlock(&(_aq)->aq_lock)
/*
* Node reference counting definitions.
*
* ieee80211_node_initref initialize the reference count to 1
* ieee80211_node_incref add a reference
* ieee80211_node_decref remove a reference
* ieee80211_node_dectestref remove a reference and return 1 if this
* is the last reference, otherwise 0
* ieee80211_node_refcnt reference count for printing (only)
*/
#include <machine/atomic.h>
#define ieee80211_node_initref(_ni) \
do { ((_ni)->ni_refcnt = 1); } while (0)
#define ieee80211_node_incref(_ni) \
atomic_add_int(&(_ni)->ni_refcnt, 1)
#define ieee80211_node_decref(_ni) \
atomic_subtract_int(&(_ni)->ni_refcnt, 1)
struct ieee80211_node;
int ieee80211_node_dectestref(struct ieee80211_node *ni);
#define ieee80211_node_refcnt(_ni) (_ni)->ni_refcnt
struct ifqueue;
struct ieee80211vap;
void ieee80211_drain_ifq(struct ifqueue *);
void ieee80211_flush_ifq(struct ifqueue *, struct ieee80211vap *);
void ieee80211_vap_destroy(struct ieee80211vap *);
#define IFNET_IS_UP_RUNNING(_ifp) \
(((_ifp)->if_flags & IFF_UP) && \
((_ifp)->if_drv_flags & IFF_DRV_RUNNING))
#define msecs_to_ticks(ms) (((ms)*hz)/1000)
#define ticks_to_msecs(t) (1000*(t) / hz)
#define ticks_to_secs(t) ((t) / hz)
#define time_after(a,b) ((long long)(b) - (long long)(a) < 0)
#define time_before(a,b) time_after(b,a)
#define time_after_eq(a,b) ((long long)(a) - (long long)(b) >= 0)
#define time_before_eq(a,b) time_after_eq(b,a)
struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
/* tx path usage */
#define M_ENCAP M_PROTO1 /* 802.11 encap done */
#define M_EAPOL M_PROTO3 /* PAE/EAPOL frame */
#define M_PWR_SAV M_PROTO4 /* bypass PS handling */
#define M_MORE_DATA M_PROTO5 /* more data frames to follow */
#define M_FF M_PROTO6 /* fast frame */
#define M_TXCB M_PROTO7 /* do tx complete callback */
#define M_AMPDU_MPDU M_PROTO8 /* ok for A-MPDU aggregation */
#define M_80211_TX \
(M_FRAG|M_FIRSTFRAG|M_LASTFRAG|M_ENCAP|M_EAPOL|M_PWR_SAV|\
M_MORE_DATA|M_FF|M_TXCB|M_AMPDU_MPDU)
/* rx path usage */
#define M_AMPDU M_PROTO1 /* A-MPDU subframe */
#define M_WEP M_PROTO2 /* WEP done by hardware */
#if 0
#define M_AMPDU_MPDU M_PROTO8 /* A-MPDU re-order done */
#endif
#define M_80211_RX (M_AMPDU|M_WEP|M_AMPDU_MPDU)
/*
* Store WME access control bits in the vlan tag.
* This is safe since it's done after the packet is classified
* (where we use any previous tag) and because it's passed
* directly in to the driver and there's no chance someone
* else will clobber them on us.
*/
#define M_WME_SETAC(m, ac) \
((m)->m_pkthdr.ether_vtag = (ac))
#define M_WME_GETAC(m) ((m)->m_pkthdr.ether_vtag)
/*
* Mbufs on the power save queue are tagged with an age and
* timed out. We reuse the hardware checksum field in the
* mbuf packet header to store this data.
*/
#define M_AGE_SET(m,v) (m->m_pkthdr.csum_data = v)
#define M_AGE_GET(m) (m->m_pkthdr.csum_data)
#define M_AGE_SUB(m,adj) (m->m_pkthdr.csum_data -= adj)
/*
* Store the sequence number.
*/
#define M_SEQNO_SET(m, seqno) \
((m)->m_pkthdr.tso_segsz = (seqno))
#define M_SEQNO_GET(m) ((m)->m_pkthdr.tso_segsz)
#define MTAG_ABI_NET80211 1132948340 /* net80211 ABI */
struct ieee80211_cb {
void (*func)(struct ieee80211_node *, void *, int status);
void *arg;
};
#define NET80211_TAG_CALLBACK 0 /* xmit complete callback */
int ieee80211_add_callback(struct mbuf *m,
void (*func)(struct ieee80211_node *, void *, int), void *arg);
void ieee80211_process_callback(struct ieee80211_node *, struct mbuf *, int);
void get_random_bytes(void *, size_t);
struct ieee80211com;
void ieee80211_sysctl_attach(struct ieee80211com *);
void ieee80211_sysctl_detach(struct ieee80211com *);
void ieee80211_sysctl_vattach(struct ieee80211vap *);
void ieee80211_sysctl_vdetach(struct ieee80211vap *);
void ieee80211_load_module(const char *);
/*
* A "policy module" is an adjunct module to net80211 that provides
* functionality that typically includes policy decisions. This
* modularity enables extensibility and vendor-supplied functionality.
*/
#define _IEEE80211_POLICY_MODULE(policy, name, version) \
typedef void (*policy##_setup)(int); \
SET_DECLARE(policy##_set, policy##_setup); \
/*
* Scanner modules provide scanning policy.
*/
#define IEEE80211_SCANNER_MODULE(name, version) \
_IEEE80211_POLICY_MODULE(scanner, name, version)
#define IEEE80211_SCANNER_ALG(name, alg, v) \
static void \
name##_modevent(int type) \
{ \
if (type == MOD_LOAD) \
ieee80211_scanner_register(alg, &v); \
else \
ieee80211_scanner_unregister(alg, &v); \
} \
TEXT_SET(scanner_set, name##_modevent); \
struct ieee80211req;
typedef int ieee80211_ioctl_getfunc(struct ieee80211vap *,
struct ieee80211req *);
SET_DECLARE(ieee80211_ioctl_getset, ieee80211_ioctl_getfunc);
#define IEEE80211_IOCTL_GET(_name, _get) TEXT_SET(ieee80211_ioctl_getset, _get)
typedef int ieee80211_ioctl_setfunc(struct ieee80211vap *,
struct ieee80211req *);
SET_DECLARE(ieee80211_ioctl_setset, ieee80211_ioctl_setfunc);
#define IEEE80211_IOCTL_SET(_name, _set) TEXT_SET(ieee80211_ioctl_setset, _set)
#endif /* _KERNEL */
/*
* Structure prepended to raw packets sent through the bpf
* interface when set to DLT_IEEE802_11_RADIO. This allows
* user applications to specify pretty much everything in
* an Atheros tx descriptor. XXX need to generalize.
*
* XXX cannot be more than 14 bytes as it is copied to a sockaddr's
* XXX sa_data area.
*/
struct ieee80211_bpf_params {
uint8_t ibp_vers; /* version */
#define IEEE80211_BPF_VERSION 0
uint8_t ibp_len; /* header length in bytes */
uint8_t ibp_flags;
#define IEEE80211_BPF_SHORTPRE 0x01 /* tx with short preamble */
#define IEEE80211_BPF_NOACK 0x02 /* tx with no ack */
#define IEEE80211_BPF_CRYPTO 0x04 /* tx with h/w encryption */
#define IEEE80211_BPF_FCS 0x10 /* frame incldues FCS */
#define IEEE80211_BPF_DATAPAD 0x20 /* frame includes data padding */
#define IEEE80211_BPF_RTS 0x40 /* tx with RTS/CTS */
#define IEEE80211_BPF_CTS 0x80 /* tx with CTS only */
uint8_t ibp_pri; /* WME/WMM AC+tx antenna */
uint8_t ibp_try0; /* series 1 try count */
uint8_t ibp_rate0; /* series 1 IEEE tx rate */
uint8_t ibp_power; /* tx power (device units) */
uint8_t ibp_ctsrate; /* IEEE tx rate for CTS */
uint8_t ibp_try1; /* series 2 try count */
uint8_t ibp_rate1; /* series 2 IEEE tx rate */
uint8_t ibp_try2; /* series 3 try count */
uint8_t ibp_rate2; /* series 3 IEEE tx rate */
uint8_t ibp_try3; /* series 4 try count */
uint8_t ibp_rate3; /* series 4 IEEE tx rate */
};
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_HAIKU_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,35 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_HOSTAP_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_HOSTAP_H_
/*
* Hostap implementation definitions.
*/
void ieee80211_hostap_attach(struct ieee80211com *);
void ieee80211_hostap_detach(struct ieee80211com *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_HOSTAP_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,204 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_HT_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_HT_H_
/*
* 802.11n protocol implementation definitions.
*/
void ieee80211_ht_init(void);
#define IEEE80211_AGGR_BAWMAX 64 /* max block ack window size */
/* threshold for aging overlapping non-HT bss */
#define IEEE80211_NONHT_PRESENT_AGE msecs_to_ticks(60*1000)
struct ieee80211_tx_ampdu {
struct ieee80211_node *txa_ni; /* back pointer */
u_short txa_flags;
#define IEEE80211_AGGR_IMMEDIATE 0x0001 /* BA policy */
#define IEEE80211_AGGR_XCHGPEND 0x0002 /* ADDBA response pending */
#define IEEE80211_AGGR_RUNNING 0x0004 /* ADDBA response received */
#define IEEE80211_AGGR_SETUP 0x0008 /* deferred state setup */
#define IEEE80211_AGGR_NAK 0x0010 /* peer NAK'd ADDBA request */
#define IEEE80211_AGGR_BARPEND 0x0020 /* BAR response pending */
uint8_t txa_ac;
uint8_t txa_token; /* dialog token */
int txa_lastsample; /* ticks @ last traffic sample */
int txa_pkts; /* packets over last sample interval */
int txa_avgpps; /* filtered traffic over window */
int txa_qbytes; /* data queued (bytes) */
short txa_qframes; /* data queued (frames) */
ieee80211_seq txa_start; /* BA window left edge */
ieee80211_seq txa_seqpending; /* new txa_start pending BAR response */
uint16_t txa_wnd; /* BA window size */
uint8_t txa_attempts; /* # ADDBA/BAR requests w/o a response*/
int txa_nextrequest;/* soonest to make next request */
struct callout txa_timer;
void *txa_private; /* driver-private storage */
uint64_t txa_pad[4];
};
/* return non-zero if AMPDU tx for the TID is running */
#define IEEE80211_AMPDU_RUNNING(tap) \
(((tap)->txa_flags & IEEE80211_AGGR_RUNNING) != 0)
/* return non-zero if AMPDU tx for the TID is running or started */
#define IEEE80211_AMPDU_REQUESTED(tap) \
(((tap)->txa_flags & \
(IEEE80211_AGGR_RUNNING|IEEE80211_AGGR_XCHGPEND|IEEE80211_AGGR_NAK)) != 0)
#define IEEE80211_AGGR_BITS \
"\20\1IMMEDIATE\2XCHGPEND\3RUNNING\4SETUP\5NAK"
/*
* Traffic estimator support. We estimate packets/sec for
* each AC that is setup for AMPDU or will potentially be
* setup for AMPDU. The traffic rate can be used to decide
* when AMPDU should be setup (according to a threshold)
* and is available for drivers to do things like cache
* eviction when only a limited number of BA streams are
* available and more streams are requested than available.
*/
static __inline void
ieee80211_txampdu_update_pps(struct ieee80211_tx_ampdu *tap)
{
/* NB: scale factor of 2 was picked heuristically */
tap->txa_avgpps = ((tap->txa_avgpps << 2) -
tap->txa_avgpps + tap->txa_pkts) >> 2;
}
/*
* Count a packet towards the pps estimate.
*/
static __inline void
ieee80211_txampdu_count_packet(struct ieee80211_tx_ampdu *tap)
{
/* XXX bound loop/do more crude estimate? */
while (ticks - tap->txa_lastsample >= hz) {
ieee80211_txampdu_update_pps(tap);
/* reset to start new sample interval */
tap->txa_pkts = 0;
if (tap->txa_avgpps == 0) {
tap->txa_lastsample = ticks;
break;
}
tap->txa_lastsample += hz;
}
tap->txa_pkts++;
}
/*
* Get the current pps estimate. If the average is out of
* date due to lack of traffic then we decay the estimate
* to account for the idle time.
*/
static __inline int
ieee80211_txampdu_getpps(struct ieee80211_tx_ampdu *tap)
{
/* XXX bound loop/do more crude estimate? */
while (ticks - tap->txa_lastsample >= hz) {
ieee80211_txampdu_update_pps(tap);
tap->txa_pkts = 0;
if (tap->txa_avgpps == 0) {
tap->txa_lastsample = ticks;
break;
}
tap->txa_lastsample += hz;
}
return tap->txa_avgpps;
}
struct ieee80211_rx_ampdu {
int rxa_flags;
int rxa_qbytes; /* data queued (bytes) */
short rxa_qframes; /* data queued (frames) */
ieee80211_seq rxa_seqstart;
ieee80211_seq rxa_start; /* start of current BA window */
uint16_t rxa_wnd; /* BA window size */
int rxa_age; /* age of oldest frame in window */
int rxa_nframes; /* frames since ADDBA */
struct mbuf *rxa_m[IEEE80211_AGGR_BAWMAX];
uint64_t rxa_pad[4];
};
void ieee80211_ht_attach(struct ieee80211com *);
void ieee80211_ht_detach(struct ieee80211com *);
void ieee80211_ht_vattach(struct ieee80211vap *);
void ieee80211_ht_vdetach(struct ieee80211vap *);
void ieee80211_ht_announce(struct ieee80211com *);
struct ieee80211_mcs_rates {
uint16_t ht20_rate_800ns;
uint16_t ht20_rate_400ns;
uint16_t ht40_rate_800ns;
uint16_t ht40_rate_400ns;
};
extern const struct ieee80211_mcs_rates ieee80211_htrates[16];
const struct ieee80211_htrateset *ieee80211_get_suphtrates(
struct ieee80211com *, const struct ieee80211_channel *);
struct ieee80211_node;
int ieee80211_setup_htrates(struct ieee80211_node *,
const uint8_t *htcap, int flags);
void ieee80211_setup_basic_htrates(struct ieee80211_node *,
const uint8_t *htinfo);
struct mbuf *ieee80211_decap_amsdu(struct ieee80211_node *, struct mbuf *);
int ieee80211_ampdu_reorder(struct ieee80211_node *, struct mbuf *);
void ieee80211_recv_bar(struct ieee80211_node *, struct mbuf *);
void ieee80211_ht_node_init(struct ieee80211_node *);
void ieee80211_ht_node_cleanup(struct ieee80211_node *);
void ieee80211_ht_node_age(struct ieee80211_node *);
struct ieee80211_channel *ieee80211_ht_adjust_channel(struct ieee80211com *,
struct ieee80211_channel *, int);
void ieee80211_ht_wds_init(struct ieee80211_node *);
void ieee80211_ht_node_join(struct ieee80211_node *);
void ieee80211_ht_node_leave(struct ieee80211_node *);
void ieee80211_htprot_update(struct ieee80211com *, int protmode);
void ieee80211_ht_timeout(struct ieee80211com *);
void ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *);
void ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *);
void ieee80211_ht_updateparams(struct ieee80211_node *, const uint8_t *,
const uint8_t *);
void ieee80211_ht_updatehtcap(struct ieee80211_node *, const uint8_t *);
int ieee80211_ampdu_request(struct ieee80211_node *,
struct ieee80211_tx_ampdu *);
void ieee80211_ampdu_stop(struct ieee80211_node *,
struct ieee80211_tx_ampdu *, int);
int ieee80211_send_bar(struct ieee80211_node *, struct ieee80211_tx_ampdu *,
ieee80211_seq);
uint8_t *ieee80211_add_htcap(uint8_t *, struct ieee80211_node *);
uint8_t *ieee80211_add_htcap_vendor(uint8_t *, struct ieee80211_node *);
uint8_t *ieee80211_add_htinfo(uint8_t *, struct ieee80211_node *);
uint8_t *ieee80211_add_htinfo_vendor(uint8_t *, struct ieee80211_node *);
struct ieee80211_beacon_offsets;
void ieee80211_ht_update_beacon(struct ieee80211vap *,
struct ieee80211_beacon_offsets *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_HT_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,849 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002-2009 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.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/endian.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_llc.h>
#include <net/if_media.h>
#include <net/if_vlan_var.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_input.h>
#ifdef IEEE80211_SUPPORT_MESH
#include <net80211/ieee80211_mesh.h>
#endif
#include <net/bpf.h>
#ifdef INET
#include <netinet/in.h>
#include <net/ethernet.h>
#endif
int
ieee80211_input_all(struct ieee80211com *ic, struct mbuf *m, int rssi, int nf)
{
struct ieee80211vap *vap;
int type = -1;
m->m_flags |= M_BCAST; /* NB: mark for bpf tap'ing */
/* XXX locking */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
struct ieee80211_node *ni;
struct mbuf *mcopy;
/* NB: could check for IFF_UP but this is cheaper */
if (vap->iv_state == IEEE80211_S_INIT)
continue;
/*
* WDS vap's only receive directed traffic from the
* station at the ``far end''. That traffic should
* be passed through the AP vap the station is associated
* to--so don't spam them with mcast frames.
*/
if (vap->iv_opmode == IEEE80211_M_WDS)
continue;
if (TAILQ_NEXT(vap, iv_next) != NULL) {
/*
* Packet contents are changed by ieee80211_decap
* so do a deep copy of the packet.
*/
mcopy = m_dup(m, M_DONTWAIT);
if (mcopy == NULL) {
/* XXX stat+msg */
continue;
}
} else {
mcopy = m;
m = NULL;
}
ni = ieee80211_ref_node(vap->iv_bss);
type = ieee80211_input(ni, mcopy, rssi, nf);
ieee80211_free_node(ni);
}
if (m != NULL) /* no vaps, reclaim mbuf */
m_freem(m);
return type;
}
/*
* This function reassembles fragments.
*
* XXX should handle 3 concurrent reassemblies per-spec.
*/
struct mbuf *
ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
struct ieee80211_frame *lwh;
uint16_t rxseq;
uint8_t fragno;
uint8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;
struct mbuf *mfrag;
KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?"));
rxseq = le16toh(*(uint16_t *)wh->i_seq);
fragno = rxseq & IEEE80211_SEQ_FRAG_MASK;
/* Quick way out, if there's nothing to defragment */
if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL)
return m;
/*
* Remove frag to insure it doesn't get reaped by timer.
*/
if (ni->ni_table == NULL) {
/*
* 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
* should be blocked by other interlocking and/or by first
* shutting the driver down. Regardless, be defensive
* here and just bail
*/
/* XXX need msg+stat */
m_freem(m);
return NULL;
}
IEEE80211_NODE_LOCK(ni->ni_table);
mfrag = ni->ni_rxfrag[0];
ni->ni_rxfrag[0] = NULL;
IEEE80211_NODE_UNLOCK(ni->ni_table);
/*
* Validate new fragment is in order and
* related to the previous ones.
*/
if (mfrag != NULL) {
uint16_t last_rxseq;
lwh = mtod(mfrag, struct ieee80211_frame *);
last_rxseq = le16toh(*(uint16_t *)lwh->i_seq);
/* NB: check seq # and frag together */
if (rxseq != last_rxseq+1 ||
!IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) ||
!IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) {
/*
* Unrelated fragment or no space for it,
* clear current fragments.
*/
m_freem(mfrag);
mfrag = NULL;
}
}
if (mfrag == NULL) {
if (fragno != 0) { /* !first fragment, discard */
vap->iv_stats.is_rx_defrag++;
IEEE80211_NODE_STAT(ni, rx_defrag);
m_freem(m);
return NULL;
}
mfrag = m;
} else { /* concatenate */
m_adj(m, hdrspace); /* strip header */
m_cat(mfrag, m);
/* NB: m_cat doesn't update the packet header */
mfrag->m_pkthdr.len += m->m_pkthdr.len;
/* track last seqnum and fragno */
lwh = mtod(mfrag, struct ieee80211_frame *);
*(uint16_t *) lwh->i_seq = *(uint16_t *) wh->i_seq;
}
if (more_frag) { /* more to come, save */
ni->ni_rxfragstamp = ticks;
ni->ni_rxfrag[0] = mfrag;
mfrag = NULL;
}
return mfrag;
}
void
ieee80211_deliver_data(struct ieee80211vap *vap,
struct ieee80211_node *ni, struct mbuf *m)
{
struct ether_header *eh = mtod(m, struct ether_header *);
struct ifnet *ifp = vap->iv_ifp;
/* clear driver/net80211 flags before passing up */
m->m_flags &= ~(M_80211_RX | M_MCAST | M_BCAST);
/* NB: see hostap_deliver_data, this path doesn't handle hostap */
KASSERT(vap->iv_opmode != IEEE80211_M_HOSTAP, ("gack, hostap"));
/*
* Do accounting.
*/
ifp->if_ipackets++;
IEEE80211_NODE_STAT(ni, rx_data);
IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
m->m_flags |= M_MCAST; /* XXX M_BCAST? */
IEEE80211_NODE_STAT(ni, rx_mcast);
} else
IEEE80211_NODE_STAT(ni, rx_ucast);
m->m_pkthdr.rcvif = ifp;
if (ni->ni_vlan != 0) {
/* attach vlan tag */
m->m_pkthdr.ether_vtag = ni->ni_vlan;
m->m_flags |= M_VLANTAG;
}
ifp->if_input(ifp, m);
}
struct mbuf *
ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen)
{
struct ieee80211_qosframe_addr4 wh;
struct ether_header *eh;
struct llc *llc;
KASSERT(hdrlen <= sizeof(wh),
("hdrlen %d > max %zd", hdrlen, sizeof(wh)));
if (m->m_len < hdrlen + sizeof(*llc) &&
(m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) {
vap->iv_stats.is_rx_tooshort++;
/* XXX msg */
return NULL;
}
memcpy(&wh, mtod(m, caddr_t), hdrlen);
llc = (struct llc *)(mtod(m, caddr_t) + hdrlen);
if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP &&
llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 &&
llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0 &&
/* NB: preserve AppleTalk frames that have a native SNAP hdr */
!(llc->llc_snap.ether_type == htons(ETHERTYPE_AARP) ||
llc->llc_snap.ether_type == htons(ETHERTYPE_IPX))) {
m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh));
llc = NULL;
} else {
m_adj(m, hdrlen - sizeof(*eh));
}
eh = mtod(m, struct ether_header *);
switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) {
case IEEE80211_FC1_DIR_NODS:
IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
break;
case IEEE80211_FC1_DIR_TODS:
IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2);
break;
case IEEE80211_FC1_DIR_FROMDS:
IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1);
IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3);
break;
case IEEE80211_FC1_DIR_DSTODS:
IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4);
break;
}
#ifdef ALIGNED_POINTER
if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
m = ieee80211_realign(vap, m, sizeof(*eh));
if (m == NULL)
return NULL;
}
#endif /* ALIGNED_POINTER */
if (llc != NULL) {
eh = mtod(m, struct ether_header *);
eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
}
return m;
}
/*
* Decap a frame encapsulated in a fast-frame/A-MSDU.
*/
struct mbuf *
ieee80211_decap1(struct mbuf *m, int *framelen)
{
#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
struct ether_header *eh;
struct llc *llc;
/*
* The frame has an 802.3 header followed by an 802.2
* LLC header. The encapsulated frame length is in the
* first header type field; save that and overwrite it
* with the true type field found in the second. Then
* copy the 802.3 header up to where it belongs and
* adjust the mbuf contents to remove the void.
*/
if (m->m_len < FF_LLC_SIZE && (m = m_pullup(m, FF_LLC_SIZE)) == NULL)
return NULL;
eh = mtod(m, struct ether_header *); /* 802.3 header is first */
llc = (struct llc *)&eh[1]; /* 802.2 header follows */
*framelen = ntohs(eh->ether_type) /* encap'd frame size */
+ sizeof(struct ether_header) - sizeof(struct llc);
eh->ether_type = llc->llc_un.type_snap.ether_type;
ovbcopy(eh, mtod(m, uint8_t *) + sizeof(struct llc),
sizeof(struct ether_header));
m_adj(m, sizeof(struct llc));
return m;
#undef FF_LLC_SIZE
}
/*
* Install received rate set information in the node's state block.
*/
int
ieee80211_setup_rates(struct ieee80211_node *ni,
const uint8_t *rates, const uint8_t *xrates, int flags)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_rateset *rs = &ni->ni_rates;
memset(rs, 0, sizeof(*rs));
rs->rs_nrates = rates[1];
memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
if (xrates != NULL) {
uint8_t nxrates;
/*
* Tack on 11g extended supported rate element.
*/
nxrates = xrates[1];
if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE, ni,
"extended rate set too large; only using "
"%u of %u rates", nxrates, xrates[1]);
vap->iv_stats.is_rx_rstoobig++;
}
memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
rs->rs_nrates += nxrates;
}
return ieee80211_fix_rate(ni, rs, flags);
}
/*
* Send a management frame error response to the specified
* station. If ni is associated with the station then use
* it; otherwise allocate a temporary node suitable for
* transmitting the frame and then free the reference so
* it will go away as soon as the frame has been transmitted.
*/
void
ieee80211_send_error(struct ieee80211_node *ni,
const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg)
{
struct ieee80211vap *vap = ni->ni_vap;
int istmp;
if (ni == vap->iv_bss) {
if (vap->iv_state != IEEE80211_S_RUN) {
/*
* XXX hack until we get rid of this routine.
* We can be called prior to the vap reaching
* run state under certain conditions in which
* case iv_bss->ni_chan will not be setup.
* Check for this explicitly and and just ignore
* the request.
*/
return;
}
ni = ieee80211_tmp_node(vap, mac);
if (ni == NULL) {
/* XXX msg */
return;
}
istmp = 1;
} else
istmp = 0;
IEEE80211_SEND_MGMT(ni, subtype, arg);
if (istmp)
ieee80211_free_node(ni);
}
int
ieee80211_alloc_challenge(struct ieee80211_node *ni)
{
if (ni->ni_challenge == NULL)
ni->ni_challenge = (uint32_t *) malloc(IEEE80211_CHALLENGE_LEN,
M_80211_NODE, M_NOWAIT);
if (ni->ni_challenge == NULL) {
IEEE80211_NOTE(ni->ni_vap,
IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni,
"%s", "shared key challenge alloc failed");
/* XXX statistic */
}
return (ni->ni_challenge != NULL);
}
/*
* Parse a Beacon or ProbeResponse frame and return the
* useful information in an ieee80211_scanparams structure.
* Status is set to 0 if no problems were found; otherwise
* a bitmask of IEEE80211_BPARSE_* items is returned that
* describes the problems detected.
*/
int
ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
struct ieee80211_scanparams *scan)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
uint8_t *frm, *efrm;
wh = mtod(m, struct ieee80211_frame *);
frm = (uint8_t *)&wh[1];
efrm = mtod(m, uint8_t *) + m->m_len;
scan->status = 0;
/*
* beacon/probe response frame format
* [8] time stamp
* [2] beacon interval
* [2] capability information
* [tlv] ssid
* [tlv] supported rates
* [tlv] country information
* [tlv] channel switch announcement (CSA)
* [tlv] parameter set (FH/DS)
* [tlv] erp information
* [tlv] extended supported rates
* [tlv] WME
* [tlv] WPA or RSN
* [tlv] HT capabilities
* [tlv] HT information
* [tlv] Atheros capabilities
* [tlv] Mesh ID
* [tlv] Mesh Configuration
*/
IEEE80211_VERIFY_LENGTH(efrm - frm, 12,
return (scan->status = IEEE80211_BPARSE_BADIELEN));
memset(scan, 0, sizeof(*scan));
scan->tstamp = frm; frm += 8;
scan->bintval = le16toh(*(uint16_t *)frm); frm += 2;
scan->capinfo = le16toh(*(uint16_t *)frm); frm += 2;
scan->bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
scan->chan = scan->bchan;
scan->ies = frm;
scan->ies_len = efrm - frm;
while (efrm - frm > 1) {
IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2,
return (scan->status = IEEE80211_BPARSE_BADIELEN));
switch (*frm) {
case IEEE80211_ELEMID_SSID:
scan->ssid = frm;
break;
case IEEE80211_ELEMID_RATES:
scan->rates = frm;
break;
case IEEE80211_ELEMID_COUNTRY:
scan->country = frm;
break;
case IEEE80211_ELEMID_CSA:
scan->csa = frm;
break;
case IEEE80211_ELEMID_FHPARMS:
if (ic->ic_phytype == IEEE80211_T_FH) {
scan->fhdwell = LE_READ_2(&frm[2]);
scan->chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
scan->fhindex = frm[6];
}
break;
case IEEE80211_ELEMID_DSPARMS:
/*
* XXX hack this since depending on phytype
* is problematic for multi-mode devices.
*/
if (ic->ic_phytype != IEEE80211_T_FH)
scan->chan = frm[2];
break;
case IEEE80211_ELEMID_TIM:
/* XXX ATIM? */
scan->tim = frm;
scan->timoff = frm - mtod(m, uint8_t *);
break;
case IEEE80211_ELEMID_IBSSPARMS:
case IEEE80211_ELEMID_CFPARMS:
case IEEE80211_ELEMID_PWRCNSTR:
/* NB: avoid debugging complaints */
break;
case IEEE80211_ELEMID_XRATES:
scan->xrates = frm;
break;
case IEEE80211_ELEMID_ERP:
if (frm[1] != 1) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID, wh, "ERP",
"bad len %u", frm[1]);
vap->iv_stats.is_rx_elem_toobig++;
break;
}
scan->erp = frm[2] | 0x100;
break;
case IEEE80211_ELEMID_HTCAP:
scan->htcap = frm;
break;
case IEEE80211_ELEMID_RSN:
scan->rsn = frm;
break;
case IEEE80211_ELEMID_HTINFO:
scan->htinfo = frm;
break;
#ifdef IEEE80211_SUPPORT_MESH
case IEEE80211_ELEMID_MESHID:
scan->meshid = frm;
break;
case IEEE80211_ELEMID_MESHCONF:
scan->meshconf = frm;
break;
#endif
case IEEE80211_ELEMID_VENDOR:
if (iswpaoui(frm))
scan->wpa = frm;
else if (iswmeparam(frm) || iswmeinfo(frm))
scan->wme = frm;
#ifdef IEEE80211_SUPPORT_SUPERG
else if (isatherosoui(frm))
scan->ath = frm;
#endif
#ifdef IEEE80211_SUPPORT_TDMA
else if (istdmaoui(frm))
scan->tdma = frm;
#endif
else if (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) {
/*
* Accept pre-draft HT ie's if the
* standard ones have not been seen.
*/
if (ishtcapoui(frm)) {
if (scan->htcap == NULL)
scan->htcap = frm;
} else if (ishtinfooui(frm)) {
if (scan->htinfo == NULL)
scan->htcap = frm;
}
}
break;
default:
IEEE80211_DISCARD_IE(vap, IEEE80211_MSG_ELEMID,
wh, "unhandled",
"id %u, len %u", *frm, frm[1]);
vap->iv_stats.is_rx_elem_unknown++;
break;
}
frm += frm[1] + 2;
}
IEEE80211_VERIFY_ELEMENT(scan->rates, IEEE80211_RATE_MAXSIZE,
scan->status |= IEEE80211_BPARSE_RATES_INVALID);
if (scan->rates != NULL && scan->xrates != NULL) {
/*
* NB: don't process XRATES if RATES is missing. This
* avoids a potential null ptr deref and should be ok
* as the return code will already note RATES is missing
* (so callers shouldn't otherwise process the frame).
*/
IEEE80211_VERIFY_ELEMENT(scan->xrates,
IEEE80211_RATE_MAXSIZE - scan->rates[1],
scan->status |= IEEE80211_BPARSE_XRATES_INVALID);
}
IEEE80211_VERIFY_ELEMENT(scan->ssid, IEEE80211_NWID_LEN,
scan->status |= IEEE80211_BPARSE_SSID_INVALID);
if (scan->chan != scan->bchan && ic->ic_phytype != IEEE80211_T_FH) {
/*
* 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_DISCARD(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
wh, NULL, "for off-channel %u", scan->chan);
vap->iv_stats.is_rx_chanmismatch++;
scan->status |= IEEE80211_BPARSE_OFFCHAN;
}
if (!(IEEE80211_BINTVAL_MIN <= scan->bintval &&
scan->bintval <= IEEE80211_BINTVAL_MAX)) {
IEEE80211_DISCARD(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
wh, NULL, "bogus beacon interval", scan->bintval);
vap->iv_stats.is_rx_badbintval++;
scan->status |= IEEE80211_BPARSE_BINTVAL_INVALID;
}
if (scan->country != NULL) {
/*
* Validate we have at least enough data to extract
* the country code. Not sure if we should return an
* error instead of discarding the IE; consider this
* being lenient as we don't depend on the data for
* correct operation.
*/
IEEE80211_VERIFY_LENGTH(scan->country[1], 3 * sizeof(uint8_t),
scan->country = NULL);
}
if (scan->csa != NULL) {
/*
* Validate Channel Switch Announcement; this must
* be the correct length or we toss the frame.
*/
IEEE80211_VERIFY_LENGTH(scan->csa[1], 3 * sizeof(uint8_t),
scan->status |= IEEE80211_BPARSE_CSA_INVALID);
}
/*
* Process HT ie's. This is complicated by our
* accepting both the standard ie's and the pre-draft
* vendor OUI ie's that some vendors still use/require.
*/
if (scan->htcap != NULL) {
IEEE80211_VERIFY_LENGTH(scan->htcap[1],
scan->htcap[0] == IEEE80211_ELEMID_VENDOR ?
4 + sizeof(struct ieee80211_ie_htcap)-2 :
sizeof(struct ieee80211_ie_htcap)-2,
scan->htcap = NULL);
}
if (scan->htinfo != NULL) {
IEEE80211_VERIFY_LENGTH(scan->htinfo[1],
scan->htinfo[0] == IEEE80211_ELEMID_VENDOR ?
4 + sizeof(struct ieee80211_ie_htinfo)-2 :
sizeof(struct ieee80211_ie_htinfo)-2,
scan->htinfo = NULL);
}
return scan->status;
}
/*
* Parse an Action frame. Return 0 on success, non-zero on failure.
*/
int
ieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m)
{
struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_action *ia;
struct ieee80211_frame *wh;
uint8_t *frm, *efrm;
/*
* action frame format:
* [1] category
* [1] action
* [tlv] parameters
*/
wh = mtod(m, struct ieee80211_frame *);
frm = (u_int8_t *)&wh[1];
efrm = mtod(m, u_int8_t *) + m->m_len;
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action), return EINVAL);
ia = (const struct ieee80211_action *) frm;
vap->iv_stats.is_rx_action++;
IEEE80211_NODE_STAT(ni, rx_action);
/* verify frame payloads but defer processing */
/* XXX maybe push this to method */
switch (ia->ia_category) {
case IEEE80211_ACTION_CAT_BA:
switch (ia->ia_action) {
case IEEE80211_ACTION_BA_ADDBA_REQUEST:
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action_ba_addbarequest),
return EINVAL);
break;
case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action_ba_addbaresponse),
return EINVAL);
break;
case IEEE80211_ACTION_BA_DELBA:
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action_ba_delba),
return EINVAL);
break;
}
break;
case IEEE80211_ACTION_CAT_HT:
switch (ia->ia_action) {
case IEEE80211_ACTION_HT_TXCHWIDTH:
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action_ht_txchwidth),
return EINVAL);
break;
case IEEE80211_ACTION_HT_MIMOPWRSAVE:
IEEE80211_VERIFY_LENGTH(efrm - frm,
sizeof(struct ieee80211_action_ht_mimopowersave),
return EINVAL);
break;
}
break;
}
return 0;
}
#ifdef IEEE80211_DEBUG
/*
* Debugging support.
*/
void
ieee80211_ssid_mismatch(struct ieee80211vap *vap, const char *tag,
uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid)
{
printf("[%s] discard %s frame, ssid mismatch: ",
ether_sprintf(mac), tag);
ieee80211_print_essid(ssid + 2, ssid[1]);
printf("\n");
}
/*
* Return the bssid of a frame.
*/
static const uint8_t *
ieee80211_getbssid(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
{
if (vap->iv_opmode == IEEE80211_M_STA)
return wh->i_addr2;
if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS)
return wh->i_addr1;
if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
return wh->i_addr1;
return wh->i_addr3;
}
#include <machine/stdarg.h>
void
ieee80211_note(struct ieee80211vap *vap, const char *fmt, ...)
{
char buf[128]; /* XXX */
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if_printf(vap->iv_ifp, "%s", buf); /* NB: no \n */
}
void
ieee80211_note_frame(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *fmt, ...)
{
char buf[128]; /* XXX */
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if_printf(vap->iv_ifp, "[%s] %s\n",
ether_sprintf(ieee80211_getbssid(vap, wh)), buf);
}
void
ieee80211_note_mac(struct ieee80211vap *vap,
const uint8_t mac[IEEE80211_ADDR_LEN],
const char *fmt, ...)
{
char buf[128]; /* XXX */
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if_printf(vap->iv_ifp, "[%s] %s\n", ether_sprintf(mac), buf);
}
void
ieee80211_discard_frame(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *type, const char *fmt, ...)
{
va_list ap;
if_printf(vap->iv_ifp, "[%s] discard ",
ether_sprintf(ieee80211_getbssid(vap, wh)));
if (type == NULL) {
printf("%s frame, ", ieee80211_mgt_subtype_name[
(wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >>
IEEE80211_FC0_SUBTYPE_SHIFT]);
} else
printf("%s frame, ", type);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
}
void
ieee80211_discard_ie(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *type, const char *fmt, ...)
{
va_list ap;
if_printf(vap->iv_ifp, "[%s] discard ",
ether_sprintf(ieee80211_getbssid(vap, wh)));
if (type != NULL)
printf("%s information element, ", type);
else
printf("information element, ");
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
}
void
ieee80211_discard_mac(struct ieee80211vap *vap,
const uint8_t mac[IEEE80211_ADDR_LEN],
const char *type, const char *fmt, ...)
{
va_list ap;
if_printf(vap->iv_ifp, "[%s] discard ", ether_sprintf(mac));
if (type != NULL)
printf("%s frame, ", type);
else
printf("frame, ");
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
}
#endif /* IEEE80211_DEBUG */
@@ -0,0 +1,160 @@
/*-
* Copyright (c) 2007-2009 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_INPUT_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_INPUT_H_
/* Verify the existence and length of __elem or get out. */
#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen, _action) do { \
if ((__elem) == NULL) { \
IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
wh, NULL, "%s", "no " #__elem ); \
vap->iv_stats.is_rx_elem_missing++; \
_action; \
} else if ((__elem)[1] > (__maxlen)) { \
IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
wh, NULL, "bad " #__elem " len %d", (__elem)[1]); \
vap->iv_stats.is_rx_elem_toobig++; \
_action; \
} \
} while (0)
#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \
if ((_len) < (_minlen)) { \
IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
wh, NULL, "ie too short, got %d, expected %d", \
(_len), (_minlen)); \
vap->iv_stats.is_rx_elem_toosmall++; \
_action; \
} \
} while (0)
#ifdef IEEE80211_DEBUG
void ieee80211_ssid_mismatch(struct ieee80211vap *, const char *tag,
uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid);
#define IEEE80211_VERIFY_SSID(_ni, _ssid, _action) do { \
if ((_ssid)[1] != 0 && \
((_ssid)[1] != (_ni)->ni_esslen || \
memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
if (ieee80211_msg_input(vap)) \
ieee80211_ssid_mismatch(vap, \
ieee80211_mgt_subtype_name[subtype >> \
IEEE80211_FC0_SUBTYPE_SHIFT], \
wh->i_addr2, _ssid); \
vap->iv_stats.is_rx_ssidmismatch++; \
_action; \
} \
} while (0)
#else /* !IEEE80211_DEBUG */
#define IEEE80211_VERIFY_SSID(_ni, _ssid, _action) do { \
if ((_ssid)[1] != 0 && \
((_ssid)[1] != (_ni)->ni_esslen || \
memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
vap->iv_stats.is_rx_ssidmismatch++; \
_action; \
} \
} while (0)
#endif /* !IEEE80211_DEBUG */
/* unalligned little endian access */
#define LE_READ_2(p) \
((uint16_t) \
((((const uint8_t *)(p))[0] ) | \
(((const uint8_t *)(p))[1] << 8)))
#define LE_READ_4(p) \
((uint32_t) \
((((const uint8_t *)(p))[0] ) | \
(((const uint8_t *)(p))[1] << 8) | \
(((const uint8_t *)(p))[2] << 16) | \
(((const uint8_t *)(p))[3] << 24)))
static __inline int
iswpaoui(const uint8_t *frm)
{
return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
}
static __inline int
iswmeoui(const uint8_t *frm)
{
return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
}
static __inline int
iswmeparam(const uint8_t *frm)
{
return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
frm[6] == WME_PARAM_OUI_SUBTYPE;
}
static __inline int
iswmeinfo(const uint8_t *frm)
{
return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
frm[6] == WME_INFO_OUI_SUBTYPE;
}
static __inline int
isatherosoui(const uint8_t *frm)
{
return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
}
static __inline int
istdmaoui(const uint8_t *frm)
{
return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
}
static __inline int
ishtcapoui(const uint8_t *frm)
{
return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI);
}
static __inline int
ishtinfooui(const uint8_t *frm)
{
return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI);
}
void ieee80211_deliver_data(struct ieee80211vap *,
struct ieee80211_node *, struct mbuf *);
struct mbuf *ieee80211_defrag(struct ieee80211_node *,
struct mbuf *, int);
struct mbuf *ieee80211_realign(struct ieee80211vap *, struct mbuf *, size_t);
struct mbuf *ieee80211_decap(struct ieee80211vap *, struct mbuf *, int);
struct mbuf *ieee80211_decap1(struct mbuf *, int *);
int ieee80211_setup_rates(struct ieee80211_node *ni,
const uint8_t *rates, const uint8_t *xrates, int flags);
void ieee80211_send_error(struct ieee80211_node *,
const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg);
int ieee80211_alloc_challenge(struct ieee80211_node *);
int ieee80211_parse_beacon(struct ieee80211_node *, struct mbuf *,
struct ieee80211_scanparams *);
int ieee80211_parse_action(struct ieee80211_node *, struct mbuf *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_INPUT_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,857 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002-2009 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_IOCTL_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_IOCTL_H_
/*
* IEEE 802.11 ioctls.
*/
#include <net80211/_ieee80211.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_crypto.h>
/*
* Per/node (station) statistics.
*/
struct ieee80211_nodestats {
uint32_t ns_rx_data; /* rx data frames */
uint32_t ns_rx_mgmt; /* rx management frames */
uint32_t ns_rx_ctrl; /* rx control frames */
uint32_t ns_rx_ucast; /* rx unicast frames */
uint32_t ns_rx_mcast; /* rx multi/broadcast frames */
uint64_t ns_rx_bytes; /* rx data count (bytes) */
uint64_t ns_rx_beacons; /* rx beacon frames */
uint32_t ns_rx_proberesp; /* rx probe response frames */
uint32_t ns_rx_dup; /* rx discard 'cuz dup */
uint32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */
uint32_t ns_rx_wepfail; /* rx wep processing failed */
uint32_t ns_rx_demicfail; /* rx demic failed */
uint32_t ns_rx_decap; /* rx decapsulation failed */
uint32_t ns_rx_defrag; /* rx defragmentation failed */
uint32_t ns_rx_disassoc; /* rx disassociation */
uint32_t ns_rx_deauth; /* rx deauthentication */
uint32_t ns_rx_action; /* rx action */
uint32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */
uint32_t ns_rx_unauth; /* rx on unauthorized port */
uint32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */
uint32_t ns_rx_drop; /* rx discard other reason */
uint32_t ns_tx_data; /* tx data frames */
uint32_t ns_tx_mgmt; /* tx management frames */
uint32_t ns_tx_ctrl; /* tx control frames */
uint32_t ns_tx_ucast; /* tx unicast frames */
uint32_t ns_tx_mcast; /* tx multi/broadcast frames */
uint64_t ns_tx_bytes; /* tx data count (bytes) */
uint32_t ns_tx_probereq; /* tx probe request frames */
uint32_t ns_tx_novlantag; /* tx discard 'cuz no tag */
uint32_t ns_tx_vlanmismatch; /* tx discard 'cuz bad tag */
uint32_t ns_ps_discard; /* ps discard 'cuz of age */
/* MIB-related state */
uint32_t ns_tx_assoc; /* [re]associations */
uint32_t ns_tx_assoc_fail; /* [re]association failures */
uint32_t ns_tx_auth; /* [re]authentications */
uint32_t ns_tx_auth_fail; /* [re]authentication failures*/
uint32_t ns_tx_deauth; /* deauthentications */
uint32_t ns_tx_deauth_code; /* last deauth reason */
uint32_t ns_tx_disassoc; /* disassociations */
uint32_t ns_tx_disassoc_code; /* last disassociation reason */
uint32_t ns_spare[8];
};
/*
* Summary statistics.
*/
struct ieee80211_stats {
uint32_t is_rx_badversion; /* rx frame with bad version */
uint32_t is_rx_tooshort; /* rx frame too short */
uint32_t is_rx_wrongbss; /* rx from wrong bssid */
uint32_t is_rx_dup; /* rx discard 'cuz dup */
uint32_t is_rx_wrongdir; /* rx w/ wrong direction */
uint32_t is_rx_mcastecho; /* rx discard 'cuz mcast echo */
uint32_t is_rx_notassoc; /* rx discard 'cuz sta !assoc */
uint32_t is_rx_noprivacy; /* rx w/ wep but privacy off */
uint32_t is_rx_unencrypted; /* rx w/o wep and privacy on */
uint32_t is_rx_wepfail; /* rx wep processing failed */
uint32_t is_rx_decap; /* rx decapsulation failed */
uint32_t is_rx_mgtdiscard; /* rx discard mgt frames */
uint32_t is_rx_ctl; /* rx ctrl frames */
uint32_t is_rx_beacon; /* rx beacon frames */
uint32_t is_rx_rstoobig; /* rx rate set truncated */
uint32_t is_rx_elem_missing; /* rx required element missing*/
uint32_t is_rx_elem_toobig; /* rx element too big */
uint32_t is_rx_elem_toosmall; /* rx element too small */
uint32_t is_rx_elem_unknown; /* rx element unknown */
uint32_t is_rx_badchan; /* rx frame w/ invalid chan */
uint32_t is_rx_chanmismatch; /* rx frame chan mismatch */
uint32_t is_rx_nodealloc; /* rx frame dropped */
uint32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */
uint32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */
uint32_t is_rx_auth_fail; /* rx sta auth failure */
uint32_t is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */
uint32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */
uint32_t is_rx_assoc_notauth; /* rx assoc w/o auth */
uint32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */
uint32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */
uint32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */
uint32_t is_rx_deauth; /* rx deauthentication */
uint32_t is_rx_disassoc; /* rx disassociation */
uint32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/
uint32_t is_rx_nobuf; /* rx failed for lack of buf */
uint32_t is_rx_decryptcrc; /* rx decrypt failed on crc */
uint32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/
uint32_t is_rx_bad_auth; /* rx bad auth request */
uint32_t is_rx_unauth; /* rx on unauthorized port */
uint32_t is_rx_badkeyid; /* rx w/ incorrect keyid */
uint32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */
uint32_t is_rx_ccmpformat; /* rx format bad (CCMP) */
uint32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */
uint32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */
uint32_t is_rx_tkipformat; /* rx format bad (TKIP) */
uint32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */
uint32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */
uint32_t is_rx_badcipher; /* rx failed 'cuz key type */
uint32_t is_rx_nocipherctx; /* rx failed 'cuz key !setup */
uint32_t is_rx_acl; /* rx discard 'cuz acl policy */
uint32_t is_tx_nobuf; /* tx failed for lack of buf */
uint32_t is_tx_nonode; /* tx failed for no node */
uint32_t is_tx_unknownmgt; /* tx of unknown mgt frame */
uint32_t is_tx_badcipher; /* tx failed 'cuz key type */
uint32_t is_tx_nodefkey; /* tx failed 'cuz no defkey */
uint32_t is_tx_noheadroom; /* tx failed 'cuz no space */
uint32_t is_tx_fragframes; /* tx frames fragmented */
uint32_t is_tx_frags; /* tx fragments created */
uint32_t is_scan_active; /* active scans started */
uint32_t is_scan_passive; /* passive scans started */
uint32_t is_node_timeout; /* nodes timed out inactivity */
uint32_t is_crypto_nomem; /* no memory for crypto ctx */
uint32_t is_crypto_tkip; /* tkip crypto done in s/w */
uint32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */
uint32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */
uint32_t is_crypto_tkipcm; /* tkip counter measures */
uint32_t is_crypto_ccmp; /* ccmp crypto done in s/w */
uint32_t is_crypto_wep; /* wep crypto done in s/w */
uint32_t is_crypto_setkey_cipher;/* cipher rejected key */
uint32_t is_crypto_setkey_nokey; /* no key index for setkey */
uint32_t is_crypto_delkey; /* driver key delete failed */
uint32_t is_crypto_badcipher; /* unknown cipher */
uint32_t is_crypto_nocipher; /* cipher not available */
uint32_t is_crypto_attachfail; /* cipher attach failed */
uint32_t is_crypto_swfallback; /* cipher fallback to s/w */
uint32_t is_crypto_keyfail; /* driver key alloc failed */
uint32_t is_crypto_enmicfail; /* en-MIC failed */
uint32_t is_ibss_capmismatch; /* merge failed-cap mismatch */
uint32_t is_ibss_norate; /* merge failed-rate mismatch */
uint32_t is_ps_unassoc; /* ps-poll for unassoc. sta */
uint32_t is_ps_badaid; /* ps-poll w/ incorrect aid */
uint32_t is_ps_qempty; /* ps-poll w/ nothing to send */
uint32_t is_ff_badhdr; /* fast frame rx'd w/ bad hdr */
uint32_t is_ff_tooshort; /* fast frame rx decap error */
uint32_t is_ff_split; /* fast frame rx split error */
uint32_t is_ff_decap; /* fast frames decap'd */
uint32_t is_ff_encap; /* fast frames encap'd for tx */
uint32_t is_rx_badbintval; /* rx frame w/ bogus bintval */
uint32_t is_rx_demicfail; /* rx demic failed */
uint32_t is_rx_defrag; /* rx defragmentation failed */
uint32_t is_rx_mgmt; /* rx management frames */
uint32_t is_rx_action; /* rx action mgt frames */
uint32_t is_amsdu_tooshort; /* A-MSDU rx decap error */
uint32_t is_amsdu_split; /* A-MSDU rx split error */
uint32_t is_amsdu_decap; /* A-MSDU decap'd */
uint32_t is_amsdu_encap; /* A-MSDU encap'd for tx */
uint32_t is_ampdu_bar_bad; /* A-MPDU BAR out of window */
uint32_t is_ampdu_bar_oow; /* A-MPDU BAR before ADDBA */
uint32_t is_ampdu_bar_move; /* A-MPDU BAR moved window */
uint32_t is_ampdu_bar_rx; /* A-MPDU BAR frames handled */
uint32_t is_ampdu_rx_flush; /* A-MPDU frames flushed */
uint32_t is_ampdu_rx_oor; /* A-MPDU frames out-of-order */
uint32_t is_ampdu_rx_copy; /* A-MPDU frames copied down */
uint32_t is_ampdu_rx_drop; /* A-MPDU frames dropped */
uint32_t is_tx_badstate; /* tx discard state != RUN */
uint32_t is_tx_notassoc; /* tx failed, sta not assoc */
uint32_t is_tx_classify; /* tx classification failed */
uint32_t is_dwds_mcast; /* discard mcast over dwds */
uint32_t is_dwds_qdrop; /* dwds pending frame q full */
uint32_t is_ht_assoc_nohtcap; /* non-HT sta rejected */
uint32_t is_ht_assoc_downgrade; /* HT sta forced to legacy */
uint32_t is_ht_assoc_norate; /* HT assoc w/ rate mismatch */
uint32_t is_ampdu_rx_age; /* A-MPDU sent up 'cuz of age */
uint32_t is_ampdu_rx_move; /* A-MPDU MSDU moved window */
uint32_t is_addba_reject; /* ADDBA reject 'cuz disabled */
uint32_t is_addba_norequest; /* ADDBA response w/o ADDBA */
uint32_t is_addba_badtoken; /* ADDBA response w/ wrong
dialogtoken */
uint32_t is_addba_badpolicy; /* ADDBA resp w/ wrong policy */
uint32_t is_ampdu_stop; /* A-MPDU stream stopped */
uint32_t is_ampdu_stop_failed; /* A-MPDU stream not running */
uint32_t is_ampdu_rx_reorder; /* A-MPDU held for rx reorder */
uint32_t is_scan_bg; /* background scans started */
uint8_t is_rx_deauth_code; /* last rx'd deauth reason */
uint8_t is_rx_disassoc_code; /* last rx'd disassoc reason */
uint8_t is_rx_authfail_code; /* last rx'd auth fail reason */
uint32_t is_beacon_miss; /* beacon miss notification */
uint32_t is_rx_badstate; /* rx discard state != RUN */
uint32_t is_ff_flush; /* ff's flush'd from stageq */
uint32_t is_tx_ctl; /* tx ctrl frames */
uint32_t is_ampdu_rexmt; /* A-MPDU frames rexmt ok */
uint32_t is_ampdu_rexmt_fail; /* A-MPDU frames rexmt fail */
uint32_t is_mesh_wrongmesh; /* dropped 'cuz not mesh sta*/
uint32_t is_mesh_nolink; /* dropped 'cuz link not estab*/
uint32_t is_mesh_fwd_ttl; /* mesh not fwd'd 'cuz ttl 0 */
uint32_t is_mesh_fwd_nobuf; /* mesh not fwd'd 'cuz no mbuf*/
uint32_t is_mesh_fwd_tooshort; /* mesh not fwd'd 'cuz no hdr */
uint32_t is_mesh_fwd_disabled; /* mesh not fwd'd 'cuz disabled */
uint32_t is_mesh_fwd_nopath; /* mesh not fwd'd 'cuz path unknown */
uint32_t is_hwmp_wrongseq; /* wrong hwmp seq no. */
uint32_t is_hwmp_rootreqs; /* root PREQs sent */
uint32_t is_hwmp_rootrann; /* root RANNs sent */
uint32_t is_mesh_badae; /* dropped 'cuz invalid AE */
uint32_t is_mesh_rtaddfailed; /* route add failed */
uint32_t is_mesh_notproxy; /* dropped 'cuz not proxying */
uint32_t is_rx_badalign; /* dropped 'cuz misaligned */
uint32_t is_hwmp_proxy; /* PREP for proxy route */
uint32_t is_spare[11];
};
/*
* Max size of optional information elements. We artificially
* constrain this; it's limited only by the max frame size (and
* the max parameter size of the wireless extensions).
*/
#define IEEE80211_MAX_OPT_IE 256
/*
* WPA/RSN get/set key request. Specify the key/cipher
* type and whether the key is to be used for sending and/or
* receiving. The key index should be set only when working
* with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
* Otherwise a unicast/pairwise key is specified by the bssid
* (on a station) or mac address (on an ap). They key length
* must include any MIC key data; otherwise it should be no
* more than IEEE80211_KEYBUF_SIZE.
*/
struct ieee80211req_key {
uint8_t ik_type; /* key/cipher type */
uint8_t ik_pad;
uint16_t ik_keyix; /* key index */
uint8_t ik_keylen; /* key length in bytes */
uint8_t ik_flags;
/* NB: IEEE80211_KEY_XMIT and IEEE80211_KEY_RECV defined elsewhere */
#define IEEE80211_KEY_DEFAULT 0x80 /* default xmit key */
uint8_t ik_macaddr[IEEE80211_ADDR_LEN];
uint64_t ik_keyrsc; /* key receive sequence counter */
uint64_t ik_keytsc; /* key transmit sequence counter */
uint8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
};
/*
* Delete a key either by index or address. Set the index
* to IEEE80211_KEYIX_NONE when deleting a unicast key.
*/
struct ieee80211req_del_key {
uint8_t idk_keyix; /* key index */
uint8_t idk_macaddr[IEEE80211_ADDR_LEN];
};
/*
* MLME state manipulation request. IEEE80211_MLME_ASSOC
* only makes sense when operating as a station. The other
* requests can be used when operating as a station or an
* ap (to effect a station).
*/
struct ieee80211req_mlme {
uint8_t im_op; /* operation to perform */
#define IEEE80211_MLME_ASSOC 1 /* associate station */
#define IEEE80211_MLME_DISASSOC 2 /* disassociate station */
#define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */
#define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */
#define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */
#define IEEE80211_MLME_AUTH 6 /* authenticate station */
uint8_t im_ssid_len; /* length of optional ssid */
uint16_t im_reason; /* 802.11 reason code */
uint8_t im_macaddr[IEEE80211_ADDR_LEN];
uint8_t im_ssid[IEEE80211_NWID_LEN];
};
/*
* MAC ACL operations.
*/
enum {
IEEE80211_MACCMD_POLICY_OPEN = 0, /* set policy: no ACL's */
IEEE80211_MACCMD_POLICY_ALLOW = 1, /* set policy: allow traffic */
IEEE80211_MACCMD_POLICY_DENY = 2, /* set policy: deny traffic */
IEEE80211_MACCMD_FLUSH = 3, /* flush ACL database */
IEEE80211_MACCMD_DETACH = 4, /* detach ACL policy */
IEEE80211_MACCMD_POLICY = 5, /* get ACL policy */
IEEE80211_MACCMD_LIST = 6, /* get ACL database */
IEEE80211_MACCMD_POLICY_RADIUS = 7, /* set policy: RADIUS managed */
};
struct ieee80211req_maclist {
uint8_t ml_macaddr[IEEE80211_ADDR_LEN];
} __packed;
/*
* Mesh Routing Table Operations.
*/
enum {
IEEE80211_MESH_RTCMD_LIST = 0, /* list HWMP routing table */
IEEE80211_MESH_RTCMD_FLUSH = 1, /* flush HWMP routing table */
IEEE80211_MESH_RTCMD_ADD = 2, /* add entry to the table */
IEEE80211_MESH_RTCMD_DELETE = 3, /* delete an entry from the table */
};
struct ieee80211req_mesh_route {
uint8_t imr_flags;
#define IEEE80211_MESHRT_FLAGS_VALID 0x01
#define IEEE80211_MESHRT_FLAGS_PROXY 0x02
uint8_t imr_dest[IEEE80211_ADDR_LEN];
uint8_t imr_nexthop[IEEE80211_ADDR_LEN];
uint16_t imr_nhops;
uint8_t imr_pad;
uint32_t imr_metric;
uint32_t imr_lifetime;
uint32_t imr_lastmseq;
};
/*
* HWMP root modes
*/
enum {
IEEE80211_HWMP_ROOTMODE_DISABLED = 0, /* disabled */
IEEE80211_HWMP_ROOTMODE_NORMAL = 1, /* normal PREPs */
IEEE80211_HWMP_ROOTMODE_PROACTIVE = 2, /* proactive PREPS */
IEEE80211_HWMP_ROOTMODE_RANN = 3, /* use RANN elemid */
};
/*
* Set the active channel list by IEEE channel #: each channel
* to be marked active is set in a bit vector. Note this list is
* intersected with the available channel list in calculating
* the set of channels actually used in scanning.
*/
struct ieee80211req_chanlist {
uint8_t ic_channels[32]; /* NB: can be variable length */
};
/*
* Get the active channel list info.
*/
struct ieee80211req_chaninfo {
u_int ic_nchans;
struct ieee80211_channel ic_chans[1]; /* NB: variable length */
};
#define IEEE80211_CHANINFO_SIZE(_nchan) \
(sizeof(struct ieee80211req_chaninfo) + \
(((_nchan)-1) * sizeof(struct ieee80211_channel)))
#define IEEE80211_CHANINFO_SPACE(_ci) \
IEEE80211_CHANINFO_SIZE((_ci)->ic_nchans)
/*
* Retrieve the WPA/RSN information element for an associated station.
*/
struct ieee80211req_wpaie { /* old version w/ only one ie */
uint8_t wpa_macaddr[IEEE80211_ADDR_LEN];
uint8_t wpa_ie[IEEE80211_MAX_OPT_IE];
};
struct ieee80211req_wpaie2 {
uint8_t wpa_macaddr[IEEE80211_ADDR_LEN];
uint8_t wpa_ie[IEEE80211_MAX_OPT_IE];
uint8_t rsn_ie[IEEE80211_MAX_OPT_IE];
};
/*
* Retrieve per-node statistics.
*/
struct ieee80211req_sta_stats {
union {
/* NB: explicitly force 64-bit alignment */
uint8_t macaddr[IEEE80211_ADDR_LEN];
uint64_t pad;
} is_u;
struct ieee80211_nodestats is_stats;
};
/*
* Station information block; the mac address is used
* to retrieve other data like stats, unicast key, etc.
*/
struct ieee80211req_sta_info {
uint16_t isi_len; /* total length (mult of 4) */
uint16_t isi_ie_off; /* offset to IE data */
uint16_t isi_ie_len; /* IE length */
#ifdef __HAIKU__
struct ieee80211_channel isi_chan; /* Handing out the conmplete channel info */
#else
uint16_t isi_freq; /* MHz */
uint32_t isi_flags; /* channel flags */
#endif
uint32_t isi_state; /* state flags */
uint8_t isi_authmode; /* authentication algorithm */
int8_t isi_rssi; /* receive signal strength */
int8_t isi_noise; /* noise floor */
uint8_t isi_capinfo; /* capabilities */
uint8_t isi_erp; /* ERP element */
uint8_t isi_macaddr[IEEE80211_ADDR_LEN];
uint8_t isi_nrates;
/* negotiated rates */
uint8_t isi_rates[IEEE80211_RATE_MAXSIZE];
uint8_t isi_txrate; /* legacy/IEEE rate or MCS */
uint16_t isi_associd; /* assoc response */
uint16_t isi_txpower; /* current tx power */
uint16_t isi_vlan; /* vlan tag */
/* NB: [IEEE80211_NONQOS_TID] holds seq#'s for non-QoS stations */
uint16_t isi_txseqs[IEEE80211_TID_SIZE];/* tx seq #/TID */
uint16_t isi_rxseqs[IEEE80211_TID_SIZE];/* rx seq#/TID */
uint16_t isi_inact; /* inactivity timer */
uint16_t isi_txmbps; /* current tx rate in .5 Mb/s */
uint16_t isi_pad;
uint32_t isi_jointime; /* time of assoc/join */
struct ieee80211_mimo_info isi_mimo; /* MIMO info for 11n sta's */
/* 11s info */
uint16_t isi_peerid;
uint16_t isi_localid;
uint8_t isi_peerstate;
/* XXX frag state? */
/* variable length IE data */
};
/*
* Retrieve per-station information; to retrieve all
* specify a mac address of ff:ff:ff:ff:ff:ff.
*/
struct ieee80211req_sta_req {
union {
/* NB: explicitly force 64-bit alignment */
uint8_t macaddr[IEEE80211_ADDR_LEN];
uint64_t pad;
} is_u;
struct ieee80211req_sta_info info[1]; /* variable length */
};
/*
* Get/set per-station tx power cap.
*/
struct ieee80211req_sta_txpow {
uint8_t it_macaddr[IEEE80211_ADDR_LEN];
uint8_t it_txpow;
};
/*
* WME parameters manipulated with IEEE80211_IOC_WME_CWMIN
* through IEEE80211_IOC_WME_ACKPOLICY are set and return
* using i_val and i_len. i_val holds the value itself.
* i_len specifies the AC and, as appropriate, then high bit
* specifies whether the operation is to be applied to the
* BSS or ourself.
*/
#define IEEE80211_WMEPARAM_SELF 0x0000 /* parameter applies to self */
#define IEEE80211_WMEPARAM_BSS 0x8000 /* parameter applies to BSS */
#define IEEE80211_WMEPARAM_VAL 0x7fff /* parameter value */
/*
* Application Information Elements can be appended to a variety
* of frames with the IEE80211_IOC_APPIE request. This request
* piggybacks on a normal ieee80211req; the frame type is passed
* in i_val as the 802.11 FC0 bytes and the length of the IE data
* is passed in i_len. The data is referenced in i_data. If i_len
* is zero then any previously configured IE data is removed. At
* most IEEE80211_MAX_APPIE data be appened. Note that multiple
* IE's can be supplied; the data is treated opaquely.
*/
#define IEEE80211_MAX_APPIE 1024 /* max app IE data */
/*
* Hack: the WPA authenticator uses this mechanism to specify WPA
* ie's that are used instead of the ones normally constructed using
* the cipher state setup with separate ioctls. This avoids issues
* like the authenticator ordering ie data differently than the
* net80211 layer and needing to keep separate state for WPA and RSN.
*/
#define IEEE80211_APPIE_WPA \
(IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON | \
IEEE80211_FC0_SUBTYPE_PROBE_RESP)
/*
* Station mode roaming parameters. These are maintained
* per band/mode and control the roaming algorithm.
*/
struct ieee80211_roamparams_req {
struct ieee80211_roamparam params[IEEE80211_MODE_MAX];
};
/*
* Transmit parameters. These can be used to set fixed transmit
* rate for each operating mode when operating as client or on a
* per-client basis according to the capabilities of the client
* (e.g. an 11b client associated to an 11g ap) when operating as
* an ap.
*
* MCS are distinguished from legacy rates by or'ing in 0x80.
*/
struct ieee80211_txparams_req {
struct ieee80211_txparam params[IEEE80211_MODE_MAX];
};
/*
* Set regulatory domain state with IEEE80211_IOC_REGDOMAIN.
* Note this is both the regulatory description and the channel
* list. The get request for IEEE80211_IOC_REGDOMAIN returns
* only the regdomain info; the channel list is obtained
* separately with IEEE80211_IOC_CHANINFO.
*/
struct ieee80211_regdomain_req {
struct ieee80211_regdomain rd;
struct ieee80211req_chaninfo chaninfo;
};
#define IEEE80211_REGDOMAIN_SIZE(_nchan) \
(sizeof(struct ieee80211_regdomain_req) + \
(((_nchan)-1) * sizeof(struct ieee80211_channel)))
#define IEEE80211_REGDOMAIN_SPACE(_req) \
IEEE80211_REGDOMAIN_SIZE((_req)->chaninfo.ic_nchans)
/*
* Get driver capabilities. Driver, hardware crypto, and
* HT/802.11n capabilities, and a table that describes what
* the radio can do.
*/
struct ieee80211_devcaps_req {
uint32_t dc_drivercaps; /* general driver caps */
uint32_t dc_cryptocaps; /* hardware crypto support */
uint32_t dc_htcaps; /* HT/802.11n support */
struct ieee80211req_chaninfo dc_chaninfo;
};
#define IEEE80211_DEVCAPS_SIZE(_nchan) \
(sizeof(struct ieee80211_devcaps_req) + \
(((_nchan)-1) * sizeof(struct ieee80211_channel)))
#define IEEE80211_DEVCAPS_SPACE(_dc) \
IEEE80211_DEVCAPS_SIZE((_dc)->dc_chaninfo.ic_nchans)
struct ieee80211_chanswitch_req {
struct ieee80211_channel csa_chan; /* new channel */
int csa_mode; /* CSA mode */
int csa_count; /* beacon count to switch */
};
/*
* Get/set per-station vlan tag.
*/
struct ieee80211req_sta_vlan {
uint8_t sv_macaddr[IEEE80211_ADDR_LEN];
uint16_t sv_vlan;
};
#if defined(__FreeBSD__) || defined(__HAIKU__)
/*
* FreeBSD-style ioctls.
*/
/* the first member must be matched with struct ifreq */
struct ieee80211req {
char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
uint16_t i_type; /* req type */
int16_t i_val; /* Index or simple value */
int16_t i_len; /* Index or simple value */
void *i_data; /* Extra data */
};
#define SIOCS80211 _IOW('i', 234, struct ieee80211req)
#define SIOCG80211 _IOWR('i', 235, struct ieee80211req)
#define SIOCG80211STATS _IOWR('i', 236, struct ifreq)
#define IEEE80211_IOC_SSID 1
#define IEEE80211_IOC_NUMSSIDS 2
#define IEEE80211_IOC_WEP 3
#define IEEE80211_WEP_NOSUP -1
#define IEEE80211_WEP_OFF 0
#define IEEE80211_WEP_ON 1
#define IEEE80211_WEP_MIXED 2
#define IEEE80211_IOC_WEPKEY 4
#define IEEE80211_IOC_NUMWEPKEYS 5
#define IEEE80211_IOC_WEPTXKEY 6
#define IEEE80211_IOC_AUTHMODE 7
#define IEEE80211_IOC_STATIONNAME 8
#define IEEE80211_IOC_CHANNEL 9
#define IEEE80211_IOC_POWERSAVE 10
#define IEEE80211_POWERSAVE_NOSUP -1
#define IEEE80211_POWERSAVE_OFF 0
#define IEEE80211_POWERSAVE_CAM 1
#define IEEE80211_POWERSAVE_PSP 2
#define IEEE80211_POWERSAVE_PSP_CAM 3
#define IEEE80211_POWERSAVE_ON IEEE80211_POWERSAVE_CAM
#define IEEE80211_IOC_POWERSAVESLEEP 11
#define IEEE80211_IOC_RTSTHRESHOLD 12
#define IEEE80211_IOC_PROTMODE 13
#define IEEE80211_PROTMODE_OFF 0
#define IEEE80211_PROTMODE_CTS 1
#define IEEE80211_PROTMODE_RTSCTS 2
#define IEEE80211_IOC_TXPOWER 14 /* global tx power limit */
#define IEEE80211_IOC_BSSID 15
#define IEEE80211_IOC_ROAMING 16 /* roaming mode */
#define IEEE80211_IOC_PRIVACY 17 /* privacy invoked */
#define IEEE80211_IOC_DROPUNENCRYPTED 18 /* discard unencrypted frames */
#define IEEE80211_IOC_WPAKEY 19
#define IEEE80211_IOC_DELKEY 20
#define IEEE80211_IOC_MLME 21
/* 22 was IEEE80211_IOC_OPTIE, replaced by IEEE80211_IOC_APPIE */
/* 23 was IEEE80211_IOC_SCAN_REQ */
/* 24 was IEEE80211_IOC_SCAN_RESULTS */
#define IEEE80211_IOC_COUNTERMEASURES 25 /* WPA/TKIP countermeasures */
#define IEEE80211_IOC_WPA 26 /* WPA mode (0,1,2) */
#define IEEE80211_IOC_CHANLIST 27 /* channel list */
#define IEEE80211_IOC_WME 28 /* WME mode (on, off) */
#define IEEE80211_IOC_HIDESSID 29 /* hide SSID mode (on, off) */
#define IEEE80211_IOC_APBRIDGE 30 /* AP inter-sta bridging */
/* 31-35,37-38 were for WPA authenticator settings */
/* 36 was IEEE80211_IOC_DRIVER_CAPS */
#define IEEE80211_IOC_WPAIE 39 /* WPA information element */
#define IEEE80211_IOC_STA_STATS 40 /* per-station statistics */
#define IEEE80211_IOC_MACCMD 41 /* MAC ACL operation */
#define IEEE80211_IOC_CHANINFO 42 /* channel info list */
#define IEEE80211_IOC_TXPOWMAX 43 /* max tx power for channel */
#define IEEE80211_IOC_STA_TXPOW 44 /* per-station tx power limit */
/* 45 was IEEE80211_IOC_STA_INFO */
#define IEEE80211_IOC_WME_CWMIN 46 /* WME: ECWmin */
#define IEEE80211_IOC_WME_CWMAX 47 /* WME: ECWmax */
#define IEEE80211_IOC_WME_AIFS 48 /* WME: AIFSN */
#define IEEE80211_IOC_WME_TXOPLIMIT 49 /* WME: txops limit */
#define IEEE80211_IOC_WME_ACM 50 /* WME: ACM (bss only) */
#define IEEE80211_IOC_WME_ACKPOLICY 51 /* WME: ACK policy (!bss only)*/
#define IEEE80211_IOC_DTIM_PERIOD 52 /* DTIM period (beacons) */
#define IEEE80211_IOC_BEACON_INTERVAL 53 /* beacon interval (ms) */
#define IEEE80211_IOC_ADDMAC 54 /* add sta to MAC ACL table */
#define IEEE80211_IOC_DELMAC 55 /* del sta from MAC ACL table */
#define IEEE80211_IOC_PUREG 56 /* pure 11g (no 11b stations) */
#define IEEE80211_IOC_FF 57 /* ATH fast frames (on, off) */
#define IEEE80211_IOC_TURBOP 58 /* ATH turbo' (on, off) */
#define IEEE80211_IOC_BGSCAN 59 /* bg scanning (on, off) */
#define IEEE80211_IOC_BGSCAN_IDLE 60 /* bg scan idle threshold */
#define IEEE80211_IOC_BGSCAN_INTERVAL 61 /* bg scan interval */
#define IEEE80211_IOC_SCANVALID 65 /* scan cache valid threshold */
/* 66-72 were IEEE80211_IOC_ROAM_* and IEEE80211_IOC_MCAST_RATE */
#define IEEE80211_IOC_FRAGTHRESHOLD 73 /* tx fragmentation threshold */
#define IEEE80211_IOC_BURST 75 /* packet bursting */
#define IEEE80211_IOC_SCAN_RESULTS 76 /* get scan results */
#define IEEE80211_IOC_BMISSTHRESHOLD 77 /* beacon miss threshold */
#define IEEE80211_IOC_STA_INFO 78 /* station/neighbor info */
#define IEEE80211_IOC_WPAIE2 79 /* WPA+RSN info elements */
#define IEEE80211_IOC_CURCHAN 80 /* current channel */
#define IEEE80211_IOC_SHORTGI 81 /* 802.11n half GI */
#define IEEE80211_IOC_AMPDU 82 /* 802.11n A-MPDU (on, off) */
#define IEEE80211_IOC_AMPDU_LIMIT 83 /* A-MPDU length limit */
#define IEEE80211_IOC_AMPDU_DENSITY 84 /* A-MPDU density */
#define IEEE80211_IOC_AMSDU 85 /* 802.11n A-MSDU (on, off) */
#define IEEE80211_IOC_AMSDU_LIMIT 86 /* A-MSDU length limit */
#define IEEE80211_IOC_PUREN 87 /* pure 11n (no legacy sta's) */
#define IEEE80211_IOC_DOTH 88 /* 802.11h (on, off) */
/* 89-91 were regulatory items */
#define IEEE80211_IOC_HTCOMPAT 92 /* support pre-D1.10 HT ie's */
#define IEEE80211_IOC_DWDS 93 /* DWDS/4-address handling */
#define IEEE80211_IOC_INACTIVITY 94 /* sta inactivity handling */
#define IEEE80211_IOC_APPIE 95 /* application IE's */
#define IEEE80211_IOC_WPS 96 /* WPS operation */
#define IEEE80211_IOC_TSN 97 /* TSN operation */
#define IEEE80211_IOC_DEVCAPS 98 /* driver+device capabilities */
#define IEEE80211_IOC_CHANSWITCH 99 /* start 11h channel switch */
#define IEEE80211_IOC_DFS 100 /* DFS (on, off) */
#define IEEE80211_IOC_DOTD 101 /* 802.11d (on, off) */
#define IEEE80211_IOC_HTPROTMODE 102 /* HT protection (off, rts) */
#define IEEE80211_IOC_SCAN_REQ 103 /* scan w/ specified params */
#define IEEE80211_IOC_SCAN_CANCEL 104 /* cancel ongoing scan */
#define IEEE80211_IOC_HTCONF 105 /* HT config (off, HT20, HT40)*/
#define IEEE80211_IOC_REGDOMAIN 106 /* regulatory domain info */
#define IEEE80211_IOC_ROAM 107 /* roaming params en masse */
#define IEEE80211_IOC_TXPARAMS 108 /* tx parameters */
#define IEEE80211_IOC_STA_VLAN 109 /* per-station vlan tag */
#define IEEE80211_IOC_SMPS 110 /* MIMO power save */
#define IEEE80211_IOC_RIFS 111 /* RIFS config (on, off) */
#define IEEE80211_IOC_GREENFIELD 112 /* Greenfield (on, off) */
#define IEEE80211_IOC_STBC 113 /* STBC Tx/RX (on, off) */
#define IEEE80211_IOC_MESH_ID 170 /* mesh identifier */
#define IEEE80211_IOC_MESH_AP 171 /* accepting peerings */
#define IEEE80211_IOC_MESH_FWRD 172 /* forward frames */
#define IEEE80211_IOC_MESH_PROTO 173 /* mesh protocols */
#define IEEE80211_IOC_MESH_TTL 174 /* mesh TTL */
#define IEEE80211_IOC_MESH_RTCMD 175 /* mesh routing table commands*/
#define IEEE80211_IOC_MESH_PR_METRIC 176 /* mesh metric protocol */
#define IEEE80211_IOC_MESH_PR_PATH 177 /* mesh path protocol */
#define IEEE80211_IOC_MESH_PR_SIG 178 /* mesh sig protocol */
#define IEEE80211_IOC_MESH_PR_CC 179 /* mesh congestion protocol */
#define IEEE80211_IOC_MESH_PR_AUTH 180 /* mesh auth protocol */
#define IEEE80211_IOC_HWMP_ROOTMODE 190 /* HWMP root mode */
#define IEEE80211_IOC_HWMP_MAXHOPS 191 /* number of hops before drop */
#define IEEE80211_IOC_HWMP_TTL 192 /* HWMP TTL */
#define IEEE80211_IOC_TDMA_SLOT 201 /* TDMA: assigned slot */
#define IEEE80211_IOC_TDMA_SLOTCNT 202 /* TDMA: slots in bss */
#define IEEE80211_IOC_TDMA_SLOTLEN 203 /* TDMA: slot length (usecs) */
#define IEEE80211_IOC_TDMA_BINTERVAL 204 /* TDMA: beacon intvl (slots) */
/*
* Parameters for controlling a scan requested with
* IEEE80211_IOC_SCAN_REQ.
*
* Active scans cause ProbeRequest frames to be issued for each
* specified ssid and, by default, a broadcast ProbeRequest frame.
* The set of ssid's is specified in the request.
*
* By default the scan will cause a BSS to be joined (in station/adhoc
* mode) or a channel to be selected for operation (hostap mode).
* To disable that specify IEEE80211_IOC_SCAN_NOPICK and if the
*
* If the station is currently associated to an AP then a scan request
* will cause the station to leave the current channel and potentially
* miss frames from the AP. Alternatively the station may notify the
* AP that it is going into power save mode before it leaves the channel.
* This ensures frames for the station are buffered by the AP. This is
* termed a ``bg scan'' and is requested with the IEEE80211_IOC_SCAN_BGSCAN
* flag. Background scans may take longer than foreground scans and may
* be preempted by traffic. If a station is not associated to an AP
* then a request for a background scan is automatically done in the
* foreground.
*
* The results of the scan request are cached by the system. This
* information is aged out and/or invalidated based on events like not
* being able to associated to an AP. To flush the current cache
* contents before doing a scan the IEEE80211_IOC_SCAN_FLUSH flag may
* be specified.
*
* By default the scan will be done until a suitable AP is located
* or a channel is found for use. A scan can also be constrained
* to be done once (IEEE80211_IOC_SCAN_ONCE) or to last for no more
* than a specified duration.
*/
struct ieee80211_scan_req {
int sr_flags;
#define IEEE80211_IOC_SCAN_NOPICK 0x00001 /* scan only, no selection */
#define IEEE80211_IOC_SCAN_ACTIVE 0x00002 /* active scan (probe req) */
#define IEEE80211_IOC_SCAN_PICK1ST 0x00004 /* ``hey sailor'' mode */
#define IEEE80211_IOC_SCAN_BGSCAN 0x00008 /* bg scan, exit ps at end */
#define IEEE80211_IOC_SCAN_ONCE 0x00010 /* do one complete pass */
#define IEEE80211_IOC_SCAN_NOBCAST 0x00020 /* don't send bcast probe req */
#define IEEE80211_IOC_SCAN_NOJOIN 0x00040 /* no auto-sequencing */
#define IEEE80211_IOC_SCAN_FLUSH 0x10000 /* flush scan cache first */
#define IEEE80211_IOC_SCAN_CHECK 0x20000 /* check scan cache first */
u_int sr_duration; /* duration (ms) */
#define IEEE80211_IOC_SCAN_DURATION_MIN 1
#define IEEE80211_IOC_SCAN_DURATION_MAX 0x7fffffff
#define IEEE80211_IOC_SCAN_FOREVER IEEE80211_IOC_SCAN_DURATION_MAX
u_int sr_mindwell; /* min channel dwelltime (ms) */
u_int sr_maxdwell; /* max channel dwelltime (ms) */
int sr_nssid;
#define IEEE80211_IOC_SCAN_MAX_SSID 3
struct {
int len; /* length in bytes */
uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
} sr_ssid[IEEE80211_IOC_SCAN_MAX_SSID];
};
/*
* Scan result data returned for IEEE80211_IOC_SCAN_RESULTS.
* Each result is a fixed size structure followed by a variable
* length SSID and one or more variable length information elements.
* The size of each variable length item is found in the fixed
* size structure and the entire length of the record is specified
* in isr_len. Result records are rounded to a multiple of 4 bytes.
*/
struct ieee80211req_scan_result {
uint16_t isr_len; /* total length (mult of 4) */
uint16_t isr_ie_off; /* offset to SSID+IE data */
uint16_t isr_ie_len; /* IE length */
#ifdef __HAIKU__
struct ieee80211_channel isr_chan; /* Handing out the conmplete channel info */
#else
uint16_t isr_freq; /* MHz */
uint16_t isr_flags; /* channel flags */
#endif
int8_t isr_noise;
int8_t isr_rssi;
uint8_t isr_intval; /* beacon interval */
uint8_t isr_capinfo; /* capabilities */
uint8_t isr_erp; /* ERP element */
uint8_t isr_bssid[IEEE80211_ADDR_LEN];
uint8_t isr_nrates;
uint8_t isr_rates[IEEE80211_RATE_MAXSIZE];
uint8_t isr_ssid_len; /* SSID length */
uint8_t isr_meshid_len; /* MESH ID length */
/* variable length SSID, followed by variable length MESH ID,
followed by IE data */
};
/*
* Virtual AP cloning parameters. The parent device must
* be a vap-capable device. All parameters specified with
* the clone request are fixed for the lifetime of the vap.
*
* There are two flavors of WDS vaps: legacy and dynamic.
* Legacy WDS operation implements a static binding between
* two stations encapsulating traffic in 4-address frames.
* Dynamic WDS vaps are created when a station associates to
* an AP and sends a 4-address frame. If the AP vap is
* configured to support WDS then this will generate an
* event to user programs listening on the routing socket
* and a Dynamic WDS vap will be created to handle traffic
* to/from that station. In both cases the bssid of the
* peer must be specified when creating the vap.
*
* By default a vap will inherit the mac address/bssid of
* the underlying device. To request a unique address the
* IEEE80211_CLONE_BSSID flag should be supplied. This is
* meaningless for WDS vaps as they share the bssid of an
* AP vap that must otherwise exist. Note that some devices
* may not be able to support multiple addresses.
*
* Station mode vap's normally depend on the device to notice
* when the AP stops sending beacon frames. If IEEE80211_CLONE_NOBEACONS
* is specified the net80211 layer will do this in s/w. This
* is mostly useful when setting up a WDS repeater/extender where
* an AP vap is combined with a sta vap and the device isn't able
* to track beacon frames in hardware.
*/
struct ieee80211_clone_params {
char icp_parent[IFNAMSIZ]; /* parent device */
uint16_t icp_opmode; /* operating mode */
uint16_t icp_flags; /* see below */
uint8_t icp_bssid[IEEE80211_ADDR_LEN]; /* for WDS links */
uint8_t icp_macaddr[IEEE80211_ADDR_LEN];/* local address */
};
#define IEEE80211_CLONE_BSSID 0x0001 /* allocate unique mac/bssid */
#define IEEE80211_CLONE_NOBEACONS 0x0002 /* don't setup beacon timers */
#define IEEE80211_CLONE_WDSLEGACY 0x0004 /* legacy WDS processing */
#define IEEE80211_CLONE_MACADDR 0x0008 /* use specified mac addr */
#define IEEE80211_CLONE_TDMA 0x0010 /* operate in TDMA mode */
#endif /* __FreeBSD__ */
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_IOCTL_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,500 @@
/*-
* Copyright (c) 2009 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Rui Paulo under sponsorship from the
* FreeBSD Foundation.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_MESH_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_MESH_H_
#define IEEE80211_MESH_DEFAULT_TTL 31
/*
* NB: all structures are __packed so sizeof works on arm, et. al.
*/
/*
* 802.11s Information Elements.
*/
/* Mesh Configuration */
struct ieee80211_meshconf_ie {
uint8_t conf_ie; /* IEEE80211_ELEMID_MESHCONF */
uint8_t conf_len;
uint8_t conf_pselid; /* Active Path Sel. Proto. ID */
uint8_t conf_pmetid; /* Active Metric Identifier */
uint8_t conf_ccid; /* Congestion Control Mode ID */
uint8_t conf_syncid; /* Sync. Protocol ID */
uint8_t conf_authid; /* Auth. Protocol ID */
uint8_t conf_form; /* Formation Information */
uint8_t conf_cap;
} __packed;
/* Hybrid Wireless Mesh Protocol */
#define IEEE80211_MESHCONF_PATH_HWMP 0x00
/* Airtime Link Metric */
#define IEEE80211_MESHCONF_METRIC_AIRTIME 0x00
/* Congestion Control */
#define IEEE80211_MESHCONF_CC_DISABLED 0x00
#define IEEE80211_MESHCONF_CC_SIG 0x01
/* Neighbour Offset */
#define IEEE80211_MESHCONF_SYNC_NEIGHOFF 0x00
#define IEEE80211_MESHCONF_AUTH_DISABLED 0x00
/* Simultaneous Authenticaction of Equals */
#define IEEE80211_MESHCONF_AUTH_SAE 0x01
#define IEEE80211_MESHCONF_FORM_MP 0x01 /* Connected to Portal */
#define IEEE80211_MESHCONF_FORM_NNEIGH_MASK 0x04 /* Number of Neighbours */
#define IEEE80211_MESHCONF_CAP_AP 0x01 /* Accepting Peers */
#define IEEE80211_MESHCONF_CAP_MCCAS 0x02 /* MCCA supported */
#define IEEE80211_MESHCONF_CAP_MCCAE 0x04 /* MCCA enabled */
#define IEEE80211_MESHCONF_CAP_FWRD 0x08 /* forwarding enabled */
#define IEEE80211_MESHCONF_CAP_BTR 0x10 /* Beacon Timing Report Enab */
#define IEEE80211_MESHCONF_CAP_TBTTA 0x20 /* TBTT Adj. Enabled */
#define IEEE80211_MESHCONF_CAP_PSL 0x40 /* Power Save Level */
/* Mesh Identifier */
struct ieee80211_meshid_ie {
uint8_t id_ie; /* IEEE80211_ELEMID_MESHID */
uint8_t id_len;
} __packed;
/* Link Metric Report */
struct ieee80211_meshlmetric_ie {
uint8_t lm_ie; /* IEEE80211_ELEMID_MESHLINK */
uint8_t lm_len;
uint32_t lm_metric;
#define IEEE80211_MESHLMETRIC_INITIALVAL 0
} __packed;
/* Congestion Notification */
struct ieee80211_meshcngst_ie {
uint8_t cngst_ie; /* IEEE80211_ELEMID_MESHCNGST */
uint8_t cngst_len;
uint16_t cngst_timer[4]; /* Expiration Timers: AC_BK,
AC_BE, AC_VI, AC_VO */
} __packed;
/* Peer Link Management */
struct ieee80211_meshpeer_ie {
uint8_t peer_ie; /* IEEE80211_ELEMID_MESHPEER */
uint8_t peer_len;
uint8_t peer_proto[4]; /* Peer Management Protocol */
uint16_t peer_llinkid; /* Local Link ID */
uint16_t peer_linkid; /* Peer Link ID */
uint16_t peer_rcode;
} __packed;
enum {
IEEE80211_MESH_PEER_LINK_OPEN = 0,
IEEE80211_MESH_PEER_LINK_CONFIRM = 1,
IEEE80211_MESH_PEER_LINK_CLOSE = 2,
/* values 3-255 are reserved */
};
/* Mesh Peering Management Protocol */
#define IEEE80211_MESH_PEER_PROTO_OUI 0x00, 0x0f, 0xac
#define IEEE80211_MESH_PEER_PROTO_VALUE 0x2a
#define IEEE80211_MESH_PEER_PROTO { IEEE80211_MESH_PEER_PROTO_OUI, \
IEEE80211_MESH_PEER_PROTO_VALUE }
/* Abbreviated Handshake Protocol */
#define IEEE80211_MESH_PEER_PROTO_AH_OUI 0x00, 0x0f, 0xac
#define IEEE80211_MESH_PEER_PROTO_AH_VALUE 0x2b
#define IEEE80211_MESH_PEER_PROTO_AH { IEEE80211_MESH_PEER_PROTO_AH_OUI, \
IEEE80211_MESH_PEER_PROTO_AH_VALUE }
#ifdef notyet
/* Mesh Channel Switch Annoucement */
struct ieee80211_meshcsa_ie {
uint8_t csa_ie; /* IEEE80211_ELEMID_MESHCSA */
uint8_t csa_len;
uint8_t csa_mode;
uint8_t csa_newclass; /* New Regulatory Class */
uint8_t csa_newchan;
uint8_t csa_precvalue; /* Precedence Value */
uint8_t csa_count;
} __packed;
/* Mesh TIM */
/* Equal to the non Mesh version */
/* Mesh Awake Window */
struct ieee80211_meshawakew_ie {
uint8_t awakew_ie; /* IEEE80211_ELEMID_MESHAWAKEW */
uint8_t awakew_len;
uint8_t awakew_windowlen; /* in TUs */
} __packed;
/* Mesh Beacon Timing */
struct ieee80211_meshbeacont_ie {
uint8_t beacont_ie; /* IEEE80211_ELEMID_MESHBEACONT */
uint8_t beacont_len;
struct {
uint8_t mp_aid; /* Least Octet of AID */
uint16_t mp_btime; /* Beacon Time */
uint16_t mp_bint; /* Beacon Interval */
} __packed mp[1]; /* NB: variable size */
} __packed;
#endif
/* Portal (MP) Annoucement */
struct ieee80211_meshpann_ie {
uint8_t pann_ie; /* IEEE80211_ELEMID_MESHPANN */
uint8_t pann_len;
uint8_t pann_flags;
uint8_t pann_hopcount;
uint8_t pann_ttl;
uint8_t pann_addr[IEEE80211_ADDR_LEN];
uint8_t pann_seq; /* PANN Sequence Number */
} __packed;
/* Root (MP) Annoucement */
struct ieee80211_meshrann_ie {
uint8_t rann_ie; /* IEEE80211_ELEMID_MESHRANN */
uint8_t rann_len;
uint8_t rann_flags;
#define IEEE80211_MESHRANN_FLAGS_PR 0x01 /* Portal Role */
uint8_t rann_hopcount;
uint8_t rann_ttl;
uint8_t rann_addr[IEEE80211_ADDR_LEN];
uint32_t rann_seq; /* HWMP Sequence Number */
uint32_t rann_metric;
} __packed;
/* Mesh Path Request */
struct ieee80211_meshpreq_ie {
uint8_t preq_ie; /* IEEE80211_ELEMID_MESHPREQ */
uint8_t preq_len;
uint8_t preq_flags;
#define IEEE80211_MESHPREQ_FLAGS_PR 0x01 /* Portal Role */
#define IEEE80211_MESHPREQ_FLAGS_AM 0x02 /* 0 = ucast / 1 = bcast */
#define IEEE80211_MESHPREQ_FLAGS_PP 0x04 /* Proactive PREP */
#define IEEE80211_MESHPREQ_FLAGS_AE 0x40 /* Address Extension */
uint8_t preq_hopcount;
uint8_t preq_ttl;
uint32_t preq_id;
uint8_t preq_origaddr[IEEE80211_ADDR_LEN];
uint32_t preq_origseq; /* HWMP Sequence Number */
/* NB: may have Originator Proxied Address */
uint32_t preq_lifetime;
uint32_t preq_metric;
uint8_t preq_tcount; /* target count */
struct {
uint8_t target_flags;
#define IEEE80211_MESHPREQ_TFLAGS_TO 0x01 /* Target Only */
#define IEEE80211_MESHPREQ_TFLAGS_RF 0x02 /* Reply and Forward */
#define IEEE80211_MESHPREQ_TFLAGS_USN 0x04 /* Unknown HWMP seq number */
uint8_t target_addr[IEEE80211_ADDR_LEN];
uint32_t target_seq; /* HWMP Sequence Number */
} __packed preq_targets[1]; /* NB: variable size */
} __packed;
/* Mesh Path Reply */
struct ieee80211_meshprep_ie {
uint8_t prep_ie; /* IEEE80211_ELEMID_MESHPREP */
uint8_t prep_len;
uint8_t prep_flags;
uint8_t prep_hopcount;
uint8_t prep_ttl;
uint8_t prep_targetaddr[IEEE80211_ADDR_LEN];
uint32_t prep_targetseq;
/* NB: May have Target Proxied Address */
uint32_t prep_lifetime;
uint32_t prep_metric;
uint8_t prep_origaddr[IEEE80211_ADDR_LEN];
uint32_t prep_origseq; /* HWMP Sequence Number */
} __packed;
/* Mesh Path Error */
struct ieee80211_meshperr_ie {
uint8_t perr_ie; /* IEEE80211_ELEMID_MESHPERR */
uint8_t perr_len;
uint8_t perr_ttl;
uint8_t perr_ndests; /* Number of Destinations */
struct {
uint8_t dest_flags;
#define IEEE80211_MESHPERR_DFLAGS_USN 0x01
#define IEEE80211_MESHPERR_DFLAGS_RC 0x02
uint8_t dest_addr[IEEE80211_ADDR_LEN];
uint32_t dest_seq; /* HWMP Sequence Number */
uint16_t dest_rcode;
} __packed perr_dests[1]; /* NB: variable size */
} __packed;
#ifdef notyet
/* Mesh Proxy Update */
struct ieee80211_meshpu_ie {
uint8_t pu_ie; /* IEEE80211_ELEMID_MESHPU */
uint8_t pu_len;
uint8_t pu_flags;
#define IEEE80211_MESHPU_FLAGS_MASK 0x1
#define IEEE80211_MESHPU_FLAGS_DEL 0x0
#define IEEE80211_MESHPU_FLAGS_ADD 0x1
uint8_t pu_seq; /* PU Sequence Number */
uint8_t pu_addr[IEEE80211_ADDR_LEN];
uint8_t pu_naddr; /* Number of Proxied Addresses */
/* NB: proxied address follows */
} __packed;
/* Mesh Proxy Update Confirmation */
struct ieee80211_meshpuc_ie {
uint8_t puc_ie; /* IEEE80211_ELEMID_MESHPUC */
uint8_t puc_len;
uint8_t puc_flags;
uint8_t puc_seq; /* PU Sequence Number */
uint8_t puc_daddr[IEEE80211_ADDR_LEN];
} __packed;
#endif
/*
* 802.11s Action Frames
*/
#define IEEE80211_ACTION_CAT_MESHPEERING 30 /* XXX Linux */
#define IEEE80211_ACTION_CAT_MESHLMETRIC 13
#define IEEE80211_ACTION_CAT_MESHPATH 32 /* XXX Linux */
#define IEEE80211_ACTION_CAT_INTERWORK 15
#define IEEE80211_ACTION_CAT_RESOURCE 16
#define IEEE80211_ACTION_CAT_PROXY 17
/*
* Mesh Peering Action codes.
*/
enum {
IEEE80211_ACTION_MESHPEERING_OPEN = 0,
IEEE80211_ACTION_MESHPEERING_CONFIRM = 1,
IEEE80211_ACTION_MESHPEERING_CLOSE = 2,
/* 3-255 reserved */
};
/*
* Mesh Path Selection Action code.
*/
enum {
IEEE80211_ACTION_MESHPATH_SEL = 0,
/* 1-255 reserved */
};
/*
* Mesh Link Metric Action codes.
*/
enum {
IEEE80211_ACTION_MESHLMETRIC_REQ = 0, /* Link Metric Request */
IEEE80211_ACTION_MESHLMETRIC_REP = 1, /* Link Metric Report */
/* 2-255 reserved */
};
/*
* Mesh Portal Annoucement Action codes.
*/
enum {
IEEE80211_ACTION_MESHPANN = 0,
/* 1-255 reserved */
};
/*
* Different mesh control structures based on the AE
* (Address Extension) bits.
*/
struct ieee80211_meshcntl {
uint8_t mc_flags; /* Address Extension 00 */
uint8_t mc_ttl; /* TTL */
uint8_t mc_seq[4]; /* Sequence No. */
/* NB: more addresses may follow */
} __packed;
struct ieee80211_meshcntl_ae01 {
uint8_t mc_flags; /* Address Extension 01 */
uint8_t mc_ttl; /* TTL */
uint8_t mc_seq[4]; /* Sequence No. */
uint8_t mc_addr4[IEEE80211_ADDR_LEN];
} __packed;
struct ieee80211_meshcntl_ae10 {
uint8_t mc_flags; /* Address Extension 10 */
uint8_t mc_ttl; /* TTL */
uint8_t mc_seq[4]; /* Sequence No. */
uint8_t mc_addr4[IEEE80211_ADDR_LEN];
uint8_t mc_addr5[IEEE80211_ADDR_LEN];
} __packed;
struct ieee80211_meshcntl_ae11 {
uint8_t mc_flags; /* Address Extension 11 */
uint8_t mc_ttl; /* TTL */
uint8_t mc_seq[4]; /* Sequence No. */
uint8_t mc_addr4[IEEE80211_ADDR_LEN];
uint8_t mc_addr5[IEEE80211_ADDR_LEN];
uint8_t mc_addr6[IEEE80211_ADDR_LEN];
} __packed;
#ifdef _KERNEL
struct ieee80211_mesh_route {
TAILQ_ENTRY(ieee80211_mesh_route) rt_next;
int rt_crtime; /* creation time */
uint8_t rt_dest[IEEE80211_ADDR_LEN];
uint8_t rt_nexthop[IEEE80211_ADDR_LEN];
uint32_t rt_metric; /* path metric */
uint16_t rt_nhops; /* number of hops */
uint16_t rt_flags;
#define IEEE80211_MESHRT_FLAGS_VALID 0x01 /* patch discovery complete */
#define IEEE80211_MESHRT_FLAGS_PROXY 0x02 /* proxy entry */
uint32_t rt_lifetime;
uint32_t rt_lastmseq; /* last seq# seen dest */
void *rt_priv; /* private data */
};
#define IEEE80211_MESH_ROUTE_PRIV(rt, cast) ((cast *)rt->rt_priv)
#define IEEE80211_MESH_PROTO_DSZ 12 /* description size */
/*
* Mesh Path Selection Protocol.
*/
enum ieee80211_state;
struct ieee80211_mesh_proto_path {
uint8_t mpp_active;
char mpp_descr[IEEE80211_MESH_PROTO_DSZ];
uint8_t mpp_ie;
struct ieee80211_node *
(*mpp_discover)(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN],
struct mbuf *);
void (*mpp_peerdown)(struct ieee80211_node *);
void (*mpp_vattach)(struct ieee80211vap *);
void (*mpp_vdetach)(struct ieee80211vap *);
int (*mpp_newstate)(struct ieee80211vap *,
enum ieee80211_state, int);
const size_t mpp_privlen; /* size required in the routing table
for private data */
int mpp_inact; /* inact. timeout for invalid routes
(ticks) */
};
/*
* Mesh Link Metric Report Protocol.
*/
struct ieee80211_mesh_proto_metric {
uint8_t mpm_active;
char mpm_descr[IEEE80211_MESH_PROTO_DSZ];
uint8_t mpm_ie;
uint32_t (*mpm_metric)(struct ieee80211_node *);
};
#ifdef notyet
/*
* Mesh Authentication Protocol.
*/
struct ieee80211_mesh_proto_auth {
uint8_t mpa_ie[4];
};
struct ieee80211_mesh_proto_congestion {
};
struct ieee80211_mesh_proto_sync {
};
#endif
typedef uint32_t ieee80211_mesh_seq;
#define IEEE80211_MESH_SEQ_LEQ(a, b) ((int32_t)((a)-(b)) <= 0)
#define IEEE80211_MESH_SEQ_GEQ(a, b) ((int32_t)((a)-(b)) >= 0)
struct ieee80211_mesh_state {
int ms_idlen;
uint8_t ms_id[IEEE80211_MESHID_LEN];
ieee80211_mesh_seq ms_seq; /* seq no for meshcntl */
uint16_t ms_neighbors;
uint8_t ms_ttl; /* mesh ttl set in packets */
#define IEEE80211_MESHFLAGS_AP 0x01 /* accept peers */
#define IEEE80211_MESHFLAGS_PORTAL 0x02 /* mesh portal role */
#define IEEE80211_MESHFLAGS_FWD 0x04 /* forward packets */
uint8_t ms_flags;
struct mtx ms_rt_lock;
struct callout ms_cleantimer;
TAILQ_HEAD(, ieee80211_mesh_route) ms_routes;
struct ieee80211_mesh_proto_metric *ms_pmetric;
struct ieee80211_mesh_proto_path *ms_ppath;
};
void ieee80211_mesh_attach(struct ieee80211com *);
void ieee80211_mesh_detach(struct ieee80211com *);
struct ieee80211_mesh_route *
ieee80211_mesh_rt_find(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN]);
struct ieee80211_mesh_route *
ieee80211_mesh_rt_add(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN]);
void ieee80211_mesh_rt_del(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN]);
void ieee80211_mesh_rt_flush(struct ieee80211vap *);
void ieee80211_mesh_rt_flush_peer(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN]);
void ieee80211_mesh_proxy_check(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN]);
int ieee80211_mesh_register_proto_path(const
struct ieee80211_mesh_proto_path *);
int ieee80211_mesh_register_proto_metric(const
struct ieee80211_mesh_proto_metric *);
uint8_t * ieee80211_add_meshid(uint8_t *, struct ieee80211vap *);
uint8_t * ieee80211_add_meshconf(uint8_t *, struct ieee80211vap *);
uint8_t * ieee80211_add_meshpeer(uint8_t *, uint8_t, uint16_t, uint16_t,
uint16_t);
uint8_t * ieee80211_add_meshlmetric(uint8_t *, uint32_t);
void ieee80211_mesh_node_init(struct ieee80211vap *,
struct ieee80211_node *);
void ieee80211_mesh_node_cleanup(struct ieee80211_node *);
void ieee80211_parse_meshid(struct ieee80211_node *,
const uint8_t *);
struct ieee80211_scanparams;
void ieee80211_mesh_init_neighbor(struct ieee80211_node *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
/*
* Return non-zero if proxy operation is enabled.
*/
static __inline int
ieee80211_mesh_isproxyena(struct ieee80211vap *vap)
{
struct ieee80211_mesh_state *ms = vap->iv_mesh;
return (ms->ms_flags &
(IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_PORTAL)) != 0;
}
/*
* Process an outbound frame: if a path is known to the
* destination then return a reference to the next hop
* for immediate transmission. Otherwise initiate path
* discovery and, if possible queue the packet to be
* sent when path discovery completes.
*/
static __inline struct ieee80211_node *
ieee80211_mesh_discover(struct ieee80211vap *vap,
const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
{
struct ieee80211_mesh_state *ms = vap->iv_mesh;
return ms->ms_ppath->mpp_discover(vap, dest, m);
}
#endif /* _KERNEL */
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_MESH_H_ */
@@ -0,0 +1,138 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
#ifdef __FreeBSD__
__FBSDID("$FreeBSD$");
#endif
/*
* IEEE 802.11 Monitor mode support.
*/
#include "opt_inet.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/endian.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_llc.h>
#include <net/ethernet.h>
#include <net/bpf.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_monitor.h>
static void monitor_vattach(struct ieee80211vap *);
static int monitor_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static int monitor_input(struct ieee80211_node *ni, struct mbuf *m,
int rssi, int nf);
void
ieee80211_monitor_attach(struct ieee80211com *ic)
{
ic->ic_vattach[IEEE80211_M_MONITOR] = monitor_vattach;
}
void
ieee80211_monitor_detach(struct ieee80211com *ic)
{
}
static void
monitor_vdetach(struct ieee80211vap *vap)
{
}
static void
monitor_vattach(struct ieee80211vap *vap)
{
vap->iv_newstate = monitor_newstate;
vap->iv_input = monitor_input;
vap->iv_opdetach = monitor_vdetach;
}
/*
* IEEE80211_M_MONITOR vap state machine handler.
*/
static int
monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct ieee80211com *ic = vap->iv_ic;
enum ieee80211_state ostate;
IEEE80211_LOCK_ASSERT(ic);
ostate = vap->iv_state;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
__func__, ieee80211_state_name[ostate],
ieee80211_state_name[nstate], arg);
vap->iv_state = nstate; /* state transition */
if (nstate == IEEE80211_S_RUN) {
switch (ostate) {
case IEEE80211_S_INIT:
ieee80211_create_ibss(vap, ic->ic_curchan);
break;
default:
break;
}
/*
* NB: this shouldn't be here but many people use
* monitor mode for raw packets; once we switch
* them over to adhoc demo mode remove this.
*/
ieee80211_node_authorize(vap->iv_bss);
}
return 0;
}
/*
* Process a received frame in monitor mode.
*/
static int
monitor_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ifnet *ifp = vap->iv_ifp;
ifp->if_ipackets++;
if (ieee80211_radiotap_active_vap(vap))
ieee80211_radiotap_rx(vap, m);
m_freem(m);
return -1;
}
@@ -0,0 +1,35 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_MONITOR_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_MONITOR_H_
/*
* Monitor implementation definitions.
*/
void ieee80211_monitor_attach(struct ieee80211com *);
void ieee80211_monitor_detach(struct ieee80211com *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_MONITOR_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,453 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002-2009 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_NODE_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_NODE_H_
#include <net80211/ieee80211_ioctl.h> /* for ieee80211_nodestats */
#include <net80211/ieee80211_ht.h> /* for aggregation state */
/*
* Each ieee80211com instance has a single timer that fires every
* IEEE80211_INACT_WAIT seconds to handle "inactivity processing".
* This is used to do node inactivity processing when operating
* as an AP, adhoc or mesh mode. For inactivity processing each node
* has a timeout set in it's ni_inact field that is decremented
* on each timeout and the node is reclaimed when the counter goes
* to zero. We use different inactivity timeout values depending
* on whether the node is associated and authorized (either by
* 802.1x or open/shared key authentication) or associated but yet
* to be authorized. The latter timeout is shorter to more aggressively
* reclaim nodes that leave part way through the 802.1x exchange.
*/
#define IEEE80211_INACT_WAIT 15 /* inactivity interval (secs) */
#define IEEE80211_INACT_INIT (30/IEEE80211_INACT_WAIT) /* initial */
#define IEEE80211_INACT_AUTH (180/IEEE80211_INACT_WAIT) /* associated but not authorized */
#define IEEE80211_INACT_RUN (300/IEEE80211_INACT_WAIT) /* authorized */
#define IEEE80211_INACT_PROBE (30/IEEE80211_INACT_WAIT) /* probe */
#define IEEE80211_INACT_SCAN (300/IEEE80211_INACT_WAIT) /* scanned */
#define IEEE80211_TRANS_WAIT 2 /* mgt frame tx timer (secs) */
/* threshold for aging overlapping non-ERP bss */
#define IEEE80211_NONERP_PRESENT_AGE msecs_to_ticks(60*1000)
#define IEEE80211_NODE_HASHSIZE 32 /* NB: hash size must be pow2 */
/* simple hash is enough for variation of macaddr */
#define IEEE80211_NODE_HASH(ic, addr) \
(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
IEEE80211_NODE_HASHSIZE)
struct ieee80211_node_table;
struct ieee80211com;
struct ieee80211vap;
/*
* Information element ``blob''. We use this structure
* to capture management frame payloads that need to be
* retained. Information elements within the payload that
* we need to consult have references recorded.
*/
struct ieee80211_ies {
/* the following are either NULL or point within data */
uint8_t *wpa_ie; /* captured WPA ie */
uint8_t *rsn_ie; /* captured RSN ie */
uint8_t *wme_ie; /* captured WME ie */
uint8_t *ath_ie; /* captured Atheros ie */
uint8_t *htcap_ie; /* captured HTCAP ie */
uint8_t *htinfo_ie; /* captured HTINFO ie */
uint8_t *tdma_ie; /* captured TDMA ie */
uint8_t *meshid_ie; /* captured MESH ID ie */
uint8_t *spare[4];
/* NB: these must be the last members of this structure */
uint8_t *data; /* frame data > 802.11 header */
int len; /* data size in bytes */
};
/*
* 802.11s (Mesh) Peer Link FSM state.
*/
enum ieee80211_mesh_mlstate {
IEEE80211_NODE_MESH_IDLE = 0,
IEEE80211_NODE_MESH_OPENSNT = 1, /* open frame sent */
IEEE80211_NODE_MESH_OPENRCV = 2, /* open frame received */
IEEE80211_NODE_MESH_CONFIRMRCV = 3, /* confirm frame received */
IEEE80211_NODE_MESH_ESTABLISHED = 4, /* link established */
IEEE80211_NODE_MESH_HOLDING = 5, /* link closing */
};
#define IEEE80211_MESH_MLSTATE_BITS \
"\20\1IDLE\2OPENSNT\2OPENRCV\3CONFIRMRCV\4ESTABLISHED\5HOLDING"
/*
* Node specific information. Note that drivers are expected
* to derive from this structure to add device-specific per-node
* state. This is done by overriding the ic_node_* methods in
* the ieee80211com structure.
*/
struct ieee80211_node {
struct ieee80211vap *ni_vap; /* associated vap */
struct ieee80211com *ni_ic; /* copy from vap to save deref*/
struct ieee80211_node_table *ni_table; /* NB: may be NULL */
TAILQ_ENTRY(ieee80211_node) ni_list; /* list of all nodes */
LIST_ENTRY(ieee80211_node) ni_hash; /* hash collision list */
u_int ni_refcnt; /* count of held references */
u_int ni_scangen; /* gen# for timeout scan */
u_int ni_flags;
#define IEEE80211_NODE_AUTH 0x000001 /* authorized for data */
#define IEEE80211_NODE_QOS 0x000002 /* QoS enabled */
#define IEEE80211_NODE_ERP 0x000004 /* ERP enabled */
/* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */
#define IEEE80211_NODE_PWR_MGT 0x000010 /* power save mode enabled */
#define IEEE80211_NODE_AREF 0x000020 /* authentication ref held */
#define IEEE80211_NODE_HT 0x000040 /* HT enabled */
#define IEEE80211_NODE_HTCOMPAT 0x000080 /* HT setup w/ vendor OUI's */
#define IEEE80211_NODE_WPS 0x000100 /* WPS association */
#define IEEE80211_NODE_TSN 0x000200 /* TSN association */
#define IEEE80211_NODE_AMPDU_RX 0x000400 /* AMPDU rx enabled */
#define IEEE80211_NODE_AMPDU_TX 0x000800 /* AMPDU tx enabled */
#define IEEE80211_NODE_MIMO_PS 0x001000 /* MIMO power save enabled */
#define IEEE80211_NODE_MIMO_RTS 0x002000 /* send RTS in MIMO PS */
#define IEEE80211_NODE_RIFS 0x004000 /* RIFS enabled */
#define IEEE80211_NODE_SGI20 0x008000 /* Short GI in HT20 enabled */
#define IEEE80211_NODE_SGI40 0x010000 /* Short GI in HT40 enabled */
#define IEEE80211_NODE_ASSOCID 0x020000 /* xmit requires associd */
#define IEEE80211_NODE_AMSDU_RX 0x040000 /* AMSDU rx enabled */
#define IEEE80211_NODE_AMSDU_TX 0x080000 /* AMSDU tx enabled */
uint16_t ni_associd; /* association ID */
uint16_t ni_vlan; /* vlan tag */
uint16_t ni_txpower; /* current transmit power */
uint8_t ni_authmode; /* authentication algorithm */
uint8_t ni_ath_flags; /* Atheros feature flags */
/* NB: These must have the same values as IEEE80211_ATHC_* */
#define IEEE80211_NODE_TURBOP 0x0001 /* Turbo prime enable */
#define IEEE80211_NODE_COMP 0x0002 /* Compresssion enable */
#define IEEE80211_NODE_FF 0x0004 /* Fast Frame capable */
#define IEEE80211_NODE_XR 0x0008 /* Atheros WME enable */
#define IEEE80211_NODE_AR 0x0010 /* AR capable */
#define IEEE80211_NODE_BOOST 0x0080 /* Dynamic Turbo boosted */
uint16_t ni_ath_defkeyix;/* Atheros def key index */
const struct ieee80211_txparam *ni_txparms;
uint32_t ni_jointime; /* time of join (secs) */
uint32_t *ni_challenge; /* shared-key challenge */
struct ieee80211_ies ni_ies; /* captured ie's */
/* tx seq per-tid */
ieee80211_seq ni_txseqs[IEEE80211_TID_SIZE];
/* rx seq previous per-tid*/
ieee80211_seq ni_rxseqs[IEEE80211_TID_SIZE];
uint32_t ni_rxfragstamp; /* time stamp of last rx frag */
struct mbuf *ni_rxfrag[3]; /* rx frag reassembly */
struct ieee80211_key ni_ucastkey; /* unicast key */
/* hardware */
uint32_t ni_avgrssi; /* recv ssi state */
int8_t ni_noise; /* noise floor */
/* header */
uint8_t ni_macaddr[IEEE80211_ADDR_LEN];
uint8_t ni_bssid[IEEE80211_ADDR_LEN];
/* beacon, probe response */
union {
uint8_t data[8];
u_int64_t tsf;
} ni_tstamp; /* from last rcv'd beacon */
uint16_t ni_intval; /* beacon interval */
uint16_t ni_capinfo; /* capabilities */
uint8_t ni_esslen;
uint8_t ni_essid[IEEE80211_NWID_LEN];
struct ieee80211_rateset ni_rates; /* negotiated rate set */
struct ieee80211_channel *ni_chan;
uint16_t ni_fhdwell; /* FH only */
uint8_t ni_fhindex; /* FH only */
uint16_t ni_erp; /* ERP from beacon/probe resp */
uint16_t ni_timoff; /* byte offset to TIM ie */
uint8_t ni_dtim_period; /* DTIM period */
uint8_t ni_dtim_count; /* DTIM count for last bcn */
/* 11s state */
uint8_t ni_meshidlen;
uint8_t ni_meshid[IEEE80211_MESHID_LEN];
enum ieee80211_mesh_mlstate ni_mlstate; /* peering management state */
uint16_t ni_mllid; /* link local ID */
uint16_t ni_mlpid; /* link peer ID */
struct callout ni_mltimer; /* link mesh timer */
uint8_t ni_mlrcnt; /* link mesh retry counter */
uint8_t ni_mltval; /* link mesh timer value */
/* 11n state */
uint16_t ni_htcap; /* HT capabilities */
uint8_t ni_htparam; /* HT params */
uint8_t ni_htctlchan; /* HT control channel */
uint8_t ni_ht2ndchan; /* HT 2nd channel */
uint8_t ni_htopmode; /* HT operating mode */
uint8_t ni_htstbc; /* HT */
uint8_t ni_chw; /* negotiated channel width */
struct ieee80211_htrateset ni_htrates; /* negotiated ht rate set */
struct ieee80211_tx_ampdu ni_tx_ampdu[WME_NUM_AC];
struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID];
/* others */
short ni_inact; /* inactivity mark count */
short ni_inact_reload;/* inactivity reload value */
int ni_txrate; /* legacy rate/MCS */
struct ieee80211_psq ni_psq; /* power save queue */
struct ieee80211_nodestats ni_stats; /* per-node statistics */
struct ieee80211vap *ni_wdsvap; /* associated WDS vap */
uint64_t ni_spare[4];
};
#define IEEE80211_NODE_ATH (IEEE80211_NODE_FF | IEEE80211_NODE_TURBOP)
#define IEEE80211_NODE_AMPDU \
(IEEE80211_NODE_AMPDU_RX | IEEE80211_NODE_AMPDU_TX)
#define IEEE80211_NODE_AMSDU \
(IEEE80211_NODE_AMSDU_RX | IEEE80211_NODE_AMSDU_TX)
#define IEEE80211_NODE_HT_ALL \
(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT | \
IEEE80211_NODE_AMPDU | IEEE80211_NODE_AMSDU | \
IEEE80211_NODE_MIMO_PS | IEEE80211_NODE_MIMO_RTS | \
IEEE80211_NODE_RIFS | IEEE80211_NODE_SGI20 | IEEE80211_NODE_SGI40)
#define IEEE80211_NODE_BITS \
"\20\1AUTH\2QOS\3ERP\5PWR_MGT\6AREF\7HT\10HTCOMPAT\11WPS\12TSN" \
"\13AMPDU_RX\14AMPDU_TX\15MIMO_PS\16MIMO_RTS\17RIFS\20SGI20\21SGI40" \
"\22ASSOCID"
#define IEEE80211_NODE_AID(ni) IEEE80211_AID(ni->ni_associd)
#define IEEE80211_NODE_STAT(ni,stat) (ni->ni_stats.ns_##stat++)
#define IEEE80211_NODE_STAT_ADD(ni,stat,v) (ni->ni_stats.ns_##stat += v)
#define IEEE80211_NODE_STAT_SET(ni,stat,v) (ni->ni_stats.ns_##stat = v)
/*
* Filtered rssi calculation support. The receive rssi is maintained
* as an average over the last 10 frames received using a low pass filter
* (all frames for now, possibly need to be more selective). Calculations
* are designed such that a good compiler can optimize them. The avg
* rssi state should be initialized to IEEE80211_RSSI_DUMMY_MARKER and
* each sample incorporated with IEEE80211_RSSI_LPF. Use IEEE80211_RSSI_GET
* to extract the current value.
*
* Note that we assume rssi data are in the range [-127..127] and we
* discard values <-20. This is consistent with assumptions throughout
* net80211 that signal strength data are in .5 dBm units relative to
* the current noise floor (linear, not log).
*/
#define IEEE80211_RSSI_LPF_LEN 10
#define IEEE80211_RSSI_DUMMY_MARKER 127
/* NB: pow2 to optimize out * and / */
#define IEEE80211_RSSI_EP_MULTIPLIER (1<<7)
#define IEEE80211_RSSI_IN(x) ((x) * IEEE80211_RSSI_EP_MULTIPLIER)
#define _IEEE80211_RSSI_LPF(x, y, len) \
(((x) != IEEE80211_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
#define IEEE80211_RSSI_LPF(x, y) do { \
if ((y) >= -20) { \
x = _IEEE80211_RSSI_LPF((x), IEEE80211_RSSI_IN((y)), \
IEEE80211_RSSI_LPF_LEN); \
} \
} while (0)
#define IEEE80211_RSSI_EP_RND(x, mul) \
((((x) % (mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
#define IEEE80211_RSSI_GET(x) \
IEEE80211_RSSI_EP_RND(x, IEEE80211_RSSI_EP_MULTIPLIER)
static __inline struct ieee80211_node *
ieee80211_ref_node(struct ieee80211_node *ni)
{
ieee80211_node_incref(ni);
return ni;
}
static __inline void
ieee80211_unref_node(struct ieee80211_node **ni)
{
ieee80211_node_decref(*ni);
*ni = NULL; /* guard against use */
}
struct ieee80211com;
void ieee80211_node_attach(struct ieee80211com *);
void ieee80211_node_lateattach(struct ieee80211com *);
void ieee80211_node_detach(struct ieee80211com *);
void ieee80211_node_vattach(struct ieee80211vap *);
void ieee80211_node_latevattach(struct ieee80211vap *);
void ieee80211_node_vdetach(struct ieee80211vap *);
static __inline int
ieee80211_node_is_authorized(const struct ieee80211_node *ni)
{
return (ni->ni_flags & IEEE80211_NODE_AUTH);
}
void ieee80211_node_authorize(struct ieee80211_node *);
void ieee80211_node_unauthorize(struct ieee80211_node *);
void ieee80211_node_setuptxparms(struct ieee80211_node *);
void ieee80211_node_set_chan(struct ieee80211_node *,
struct ieee80211_channel *);
void ieee80211_create_ibss(struct ieee80211vap*, struct ieee80211_channel *);
void ieee80211_reset_bss(struct ieee80211vap *);
void ieee80211_sync_curchan(struct ieee80211com *);
void ieee80211_setupcurchan(struct ieee80211com *,
struct ieee80211_channel *);
void ieee80211_setcurchan(struct ieee80211com *, struct ieee80211_channel *);
int ieee80211_ibss_merge(struct ieee80211_node *);
struct ieee80211_scan_entry;
int ieee80211_sta_join(struct ieee80211vap *, struct ieee80211_channel *,
const struct ieee80211_scan_entry *);
void ieee80211_sta_leave(struct ieee80211_node *);
void ieee80211_node_deauth(struct ieee80211_node *, int);
int ieee80211_ies_init(struct ieee80211_ies *, const uint8_t *, int);
void ieee80211_ies_cleanup(struct ieee80211_ies *);
void ieee80211_ies_expand(struct ieee80211_ies *);
#define ieee80211_ies_setie(_ies, _ie, _off) do { \
(_ies)._ie = (_ies).data + (_off); \
} while (0)
/*
* Table of ieee80211_node instances. Each ieee80211com
* has one that holds association stations (when operating
* as an ap) or neighbors (in ibss mode).
*
* XXX embed this in ieee80211com instead of indirect?
*/
struct ieee80211_node_table {
struct ieee80211com *nt_ic; /* back reference */
ieee80211_node_lock_t nt_nodelock; /* on node table */
TAILQ_HEAD(, ieee80211_node) nt_node; /* information of all nodes */
LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE];
struct ieee80211_node **nt_keyixmap; /* key ix -> node map */
int nt_keyixmax; /* keyixmap size */
const char *nt_name; /* table name for debug msgs */
ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */
u_int nt_scangen; /* gen# for iterators */
int nt_inact_init; /* initial node inact setting */
};
struct ieee80211_node *ieee80211_alloc_node(struct ieee80211_node_table *,
struct ieee80211vap *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node *ieee80211_tmp_node(struct ieee80211vap *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node *ieee80211_dup_bss(struct ieee80211vap *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node *ieee80211_node_create_wds(struct ieee80211vap *,
const uint8_t bssid[IEEE80211_ADDR_LEN],
struct ieee80211_channel *);
#ifdef IEEE80211_DEBUG_REFCNT
void ieee80211_free_node_debug(struct ieee80211_node *,
const char *func, int line);
struct ieee80211_node *ieee80211_find_node_locked_debug(
struct ieee80211_node_table *,
const uint8_t macaddr[IEEE80211_ADDR_LEN],
const char *func, int line);
struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *,
const uint8_t macaddr[IEEE80211_ADDR_LEN],
const char *func, int line);
struct ieee80211_node *ieee80211_find_vap_node_locked_debug(
struct ieee80211_node_table *,
const struct ieee80211vap *vap,
const uint8_t macaddr[IEEE80211_ADDR_LEN],
const char *func, int line);
struct ieee80211_node *ieee80211_find_vap_node_debug(
struct ieee80211_node_table *,
const struct ieee80211vap *vap,
const uint8_t macaddr[IEEE80211_ADDR_LEN],
const char *func, int line);
struct ieee80211_node * ieee80211_find_rxnode_debug(struct ieee80211com *,
const struct ieee80211_frame_min *,
const char *func, int line);
struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
struct ieee80211com *,
const struct ieee80211_frame_min *, uint16_t keyix,
const char *func, int line);
struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211vap *,
const uint8_t *,
const char *func, int line);
#define ieee80211_free_node(ni) \
ieee80211_free_node_debug(ni, __func__, __LINE__)
#define ieee80211_find_node_locked(nt, mac) \
ieee80211_find_node_locked_debug(nt, mac, __func__, __LINE__)
#define ieee80211_find_node(nt, mac) \
ieee80211_find_node_debug(nt, mac, __func__, __LINE__)
#define ieee80211_find_vap_node_locked(nt, vap, mac) \
ieee80211_find_vap_node_locked_debug(nt, vap, mac, __func__, __LINE__)
#define ieee80211_find_vap_node(nt, vap, mac) \
ieee80211_find_vap_node_debug(nt, vap, mac, __func__, __LINE__)
#define ieee80211_find_rxnode(ic, wh) \
ieee80211_find_rxnode_debug(ic, wh, __func__, __LINE__)
#define ieee80211_find_rxnode_withkey(ic, wh, keyix) \
ieee80211_find_rxnode_withkey_debug(ic, wh, keyix, __func__, __LINE__)
#define ieee80211_find_txnode(vap, mac) \
ieee80211_find_txnode_debug(vap, mac, __func__, __LINE__)
#else
void ieee80211_free_node(struct ieee80211_node *);
struct ieee80211_node *ieee80211_find_node_locked(struct ieee80211_node_table *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node *ieee80211_find_node(struct ieee80211_node_table *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node *ieee80211_find_vap_node_locked(
struct ieee80211_node_table *, const struct ieee80211vap *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node *ieee80211_find_vap_node(
struct ieee80211_node_table *, const struct ieee80211vap *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node * ieee80211_find_rxnode(struct ieee80211com *,
const struct ieee80211_frame_min *);
struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *,
const struct ieee80211_frame_min *, uint16_t keyix);
struct ieee80211_node *ieee80211_find_txnode(struct ieee80211vap *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
#endif
int ieee80211_node_delucastkey(struct ieee80211_node *);
void ieee80211_node_timeout(void *arg);
typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
void ieee80211_iterate_nodes(struct ieee80211_node_table *,
ieee80211_iter_func *, void *);
void ieee80211_notify_erp(struct ieee80211com *);
void ieee80211_dump_node(struct ieee80211_node_table *,
struct ieee80211_node *);
void ieee80211_dump_nodes(struct ieee80211_node_table *);
struct ieee80211_node *ieee80211_fakeup_adhoc_node(struct ieee80211vap *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_scanparams;
void ieee80211_init_neighbor(struct ieee80211_node *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211vap *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
void ieee80211_node_join(struct ieee80211_node *,int);
void ieee80211_node_leave(struct ieee80211_node *);
int8_t ieee80211_getrssi(struct ieee80211vap *);
void ieee80211_getsignal(struct ieee80211vap *, int8_t *, int8_t *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_NODE_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,465 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11 PHY-related support.
*/
#include "opt_inet.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_phy.h>
#ifdef notyet
struct ieee80211_ds_plcp_hdr {
uint8_t i_signal;
uint8_t i_service;
uint16_t i_length;
uint16_t i_crc;
} __packed;
#endif /* notyet */
/* shorthands to compact tables for readability */
#define OFDM IEEE80211_T_OFDM
#define CCK IEEE80211_T_CCK
#define TURBO IEEE80211_T_TURBO
#define HALF IEEE80211_T_OFDM_HALF
#define QUART IEEE80211_T_OFDM_QUARTER
#define PBCC (IEEE80211_T_OFDM_QUARTER+1) /* XXX */
#define B(r) (0x80 | r)
#define Mb(x) (x*1000)
static struct ieee80211_rate_table ieee80211_11b_table = {
.rateCount = 4, /* XXX no PBCC */
.info = {
/* short ctrl */
/* Preamble dot11Rate Rate */
[0] = { .phy = CCK, 1000, 0x00, B(2), 0 },/* 1 Mb */
[1] = { .phy = CCK, 2000, 0x04, B(4), 1 },/* 2 Mb */
[2] = { .phy = CCK, 5500, 0x04, B(11), 1 },/* 5.5 Mb */
[3] = { .phy = CCK, 11000, 0x04, B(22), 1 },/* 11 Mb */
[4] = { .phy = PBCC, 22000, 0x04, 44, 3 } /* 22 Mb */
},
};
static struct ieee80211_rate_table ieee80211_11g_table = {
.rateCount = 12,
.info = {
/* short ctrl */
/* Preamble dot11Rate Rate */
[0] = { .phy = CCK, 1000, 0x00, B(2), 0 },
[1] = { .phy = CCK, 2000, 0x04, B(4), 1 },
[2] = { .phy = CCK, 5500, 0x04, B(11), 2 },
[3] = { .phy = CCK, 11000, 0x04, B(22), 3 },
[4] = { .phy = OFDM, 6000, 0x00, 12, 4 },
[5] = { .phy = OFDM, 9000, 0x00, 18, 4 },
[6] = { .phy = OFDM, 12000, 0x00, 24, 6 },
[7] = { .phy = OFDM, 18000, 0x00, 36, 6 },
[8] = { .phy = OFDM, 24000, 0x00, 48, 8 },
[9] = { .phy = OFDM, 36000, 0x00, 72, 8 },
[10] = { .phy = OFDM, 48000, 0x00, 96, 8 },
[11] = { .phy = OFDM, 54000, 0x00, 108, 8 }
},
};
static struct ieee80211_rate_table ieee80211_11a_table = {
.rateCount = 8,
.info = {
/* short ctrl */
/* Preamble dot11Rate Rate */
[0] = { .phy = OFDM, 6000, 0x00, B(12), 0 },
[1] = { .phy = OFDM, 9000, 0x00, 18, 0 },
[2] = { .phy = OFDM, 12000, 0x00, B(24), 2 },
[3] = { .phy = OFDM, 18000, 0x00, 36, 2 },
[4] = { .phy = OFDM, 24000, 0x00, B(48), 4 },
[5] = { .phy = OFDM, 36000, 0x00, 72, 4 },
[6] = { .phy = OFDM, 48000, 0x00, 96, 4 },
[7] = { .phy = OFDM, 54000, 0x00, 108, 4 }
},
};
static struct ieee80211_rate_table ieee80211_half_table = {
.rateCount = 8,
.info = {
/* short ctrl */
/* Preamble dot11Rate Rate */
[0] = { .phy = HALF, 3000, 0x00, B(6), 0 },
[1] = { .phy = HALF, 4500, 0x00, 9, 0 },
[2] = { .phy = HALF, 6000, 0x00, B(12), 2 },
[3] = { .phy = HALF, 9000, 0x00, 18, 2 },
[4] = { .phy = HALF, 12000, 0x00, B(24), 4 },
[5] = { .phy = HALF, 18000, 0x00, 36, 4 },
[6] = { .phy = HALF, 24000, 0x00, 48, 4 },
[7] = { .phy = HALF, 27000, 0x00, 54, 4 }
},
};
static struct ieee80211_rate_table ieee80211_quarter_table = {
.rateCount = 8,
.info = {
/* short ctrl */
/* Preamble dot11Rate Rate */
[0] = { .phy = QUART, 1500, 0x00, B(3), 0 },
[1] = { .phy = QUART, 2250, 0x00, 4, 0 },
[2] = { .phy = QUART, 3000, 0x00, B(9), 2 },
[3] = { .phy = QUART, 4500, 0x00, 9, 2 },
[4] = { .phy = QUART, 6000, 0x00, B(12), 4 },
[5] = { .phy = QUART, 9000, 0x00, 18, 4 },
[6] = { .phy = QUART, 12000, 0x00, 24, 4 },
[7] = { .phy = QUART, 13500, 0x00, 27, 4 }
},
};
static struct ieee80211_rate_table ieee80211_turbog_table = {
.rateCount = 7,
.info = {
/* short ctrl */
/* Preamble dot11Rate Rate */
[0] = { .phy = TURBO, 12000, 0x00, B(12), 0 },
[1] = { .phy = TURBO, 24000, 0x00, B(24), 1 },
[2] = { .phy = TURBO, 36000, 0x00, 36, 1 },
[3] = { .phy = TURBO, 48000, 0x00, B(48), 3 },
[4] = { .phy = TURBO, 72000, 0x00, 72, 3 },
[5] = { .phy = TURBO, 96000, 0x00, 96, 3 },
[6] = { .phy = TURBO, 108000, 0x00, 108, 3 }
},
};
static struct ieee80211_rate_table ieee80211_turboa_table = {
.rateCount = 8,
.info = {
/* short ctrl */
/* Preamble dot11Rate Rate */
[0] = { .phy = TURBO, 12000, 0x00, B(12), 0 },
[1] = { .phy = TURBO, 18000, 0x00, 18, 0 },
[2] = { .phy = TURBO, 24000, 0x00, B(24), 2 },
[3] = { .phy = TURBO, 36000, 0x00, 36, 2 },
[4] = { .phy = TURBO, 48000, 0x00, B(48), 4 },
[5] = { .phy = TURBO, 72000, 0x00, 72, 4 },
[6] = { .phy = TURBO, 96000, 0x00, 96, 4 },
[7] = { .phy = TURBO, 108000, 0x00, 108, 4 }
},
};
#undef Mb
#undef B
#undef OFDM
#undef HALF
#undef QUART
#undef CCK
#undef TURBO
#undef XR
/*
* Setup a rate table's reverse lookup table and fill in
* ack durations. The reverse lookup tables are assumed
* to be initialized to zero (or at least the first entry).
* We use this as a key that indicates whether or not
* we've previously setup the reverse lookup table.
*
* XXX not reentrant, but shouldn't matter
*/
static void
ieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
{
#define N(a) (sizeof(a)/sizeof(a[0]))
#define WLAN_CTRL_FRAME_SIZE \
(sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
int i;
for (i = 0; i < N(rt->rateCodeToIndex); i++)
rt->rateCodeToIndex[i] = (uint8_t) -1;
for (i = 0; i < rt->rateCount; i++) {
uint8_t code = rt->info[i].dot11Rate;
uint8_t cix = rt->info[i].ctlRateIndex;
uint8_t ctl_rate = rt->info[cix].dot11Rate;
rt->rateCodeToIndex[code] = i;
if (code & IEEE80211_RATE_BASIC) {
/*
* Map w/o basic rate bit too.
*/
code &= IEEE80211_RATE_VAL;
rt->rateCodeToIndex[code] = i;
}
/*
* XXX for 11g the control rate to use for 5.5 and 11 Mb/s
* depends on whether they are marked as basic rates;
* the static tables are setup with an 11b-compatible
* 2Mb/s rate which will work but is suboptimal
*
* NB: Control rate is always less than or equal to the
* current rate, so control rate's reverse lookup entry
* has been installed and following call is safe.
*/
rt->info[i].lpAckDuration = ieee80211_compute_duration(rt,
WLAN_CTRL_FRAME_SIZE, ctl_rate, 0);
rt->info[i].spAckDuration = ieee80211_compute_duration(rt,
WLAN_CTRL_FRAME_SIZE, ctl_rate, IEEE80211_F_SHPREAMBLE);
}
#undef WLAN_CTRL_FRAME_SIZE
#undef N
}
/* Setup all rate tables */
void
ieee80211_phy_init(void)
{
#define N(arr) (int)(sizeof(arr) / sizeof(arr[0]))
static struct ieee80211_rate_table * const ratetables[] = {
&ieee80211_half_table,
&ieee80211_quarter_table,
&ieee80211_11a_table,
&ieee80211_11g_table,
&ieee80211_turbog_table,
&ieee80211_turboa_table,
&ieee80211_turboa_table,
&ieee80211_11a_table,
&ieee80211_11g_table,
&ieee80211_11b_table
};
int i;
for (i = 0; i < N(ratetables); ++i)
ieee80211_setup_ratetable(ratetables[i]);
#undef N
}
SYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL);
const struct ieee80211_rate_table *
ieee80211_get_ratetable(struct ieee80211_channel *c)
{
const struct ieee80211_rate_table *rt;
/* XXX HT */
if (IEEE80211_IS_CHAN_HALF(c))
rt = &ieee80211_half_table;
else if (IEEE80211_IS_CHAN_QUARTER(c))
rt = &ieee80211_quarter_table;
else if (IEEE80211_IS_CHAN_HTA(c))
rt = &ieee80211_11a_table; /* XXX */
else if (IEEE80211_IS_CHAN_HTG(c))
rt = &ieee80211_11g_table; /* XXX */
else if (IEEE80211_IS_CHAN_108G(c))
rt = &ieee80211_turbog_table;
else if (IEEE80211_IS_CHAN_ST(c))
rt = &ieee80211_turboa_table;
else if (IEEE80211_IS_CHAN_TURBO(c))
rt = &ieee80211_turboa_table;
else if (IEEE80211_IS_CHAN_A(c))
rt = &ieee80211_11a_table;
else if (IEEE80211_IS_CHAN_ANYG(c))
rt = &ieee80211_11g_table;
else if (IEEE80211_IS_CHAN_B(c))
rt = &ieee80211_11b_table;
else {
/* NB: should not get here */
panic("%s: no rate table for channel; freq %u flags 0x%x\n",
__func__, c->ic_freq, c->ic_flags);
}
return rt;
}
/*
* Convert PLCP signal/rate field to 802.11 rate (.5Mbits/s)
*
* Note we do no parameter checking; this routine is mainly
* used to derive an 802.11 rate for constructing radiotap
* header data for rx frames.
*
* XXX might be a candidate for inline
*/
uint8_t
ieee80211_plcp2rate(uint8_t plcp, enum ieee80211_phytype type)
{
if (type == IEEE80211_T_OFDM) {
static const uint8_t ofdm_plcp2rate[16] = {
[0xb] = 12,
[0xf] = 18,
[0xa] = 24,
[0xe] = 36,
[0x9] = 48,
[0xd] = 72,
[0x8] = 96,
[0xc] = 108
};
return ofdm_plcp2rate[plcp & 0xf];
}
if (type == IEEE80211_T_CCK) {
static const uint8_t cck_plcp2rate[16] = {
[0xa] = 2, /* 0x0a */
[0x4] = 4, /* 0x14 */
[0x7] = 11, /* 0x37 */
[0xe] = 22, /* 0x6e */
[0xc] = 44, /* 0xdc , actually PBCC */
};
return cck_plcp2rate[plcp & 0xf];
}
return 0;
}
/*
* Covert 802.11 rate to PLCP signal.
*/
uint8_t
ieee80211_rate2plcp(int rate, enum ieee80211_phytype type)
{
/* XXX ignore type for now since rates are unique */
switch (rate) {
/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
case 12: return 0xb;
case 18: return 0xf;
case 24: return 0xa;
case 36: return 0xe;
case 48: return 0x9;
case 72: return 0xd;
case 96: return 0x8;
case 108: return 0xc;
/* CCK rates (IEEE Std 802.11b-1999 page 15, subclause 18.2.3.3) */
case 2: return 10;
case 4: return 20;
case 11: return 55;
case 22: return 110;
/* IEEE Std 802.11g-2003 page 19, subclause 19.3.2.1 */
case 44: return 220;
}
return 0; /* XXX unsupported/unknown rate */
}
#define CCK_SIFS_TIME 10
#define CCK_PREAMBLE_BITS 144
#define CCK_PLCP_BITS 48
#define OFDM_SIFS_TIME 16
#define OFDM_PREAMBLE_TIME 20
#define OFDM_PLCP_BITS 22
#define OFDM_SYMBOL_TIME 4
#define OFDM_HALF_SIFS_TIME 32
#define OFDM_HALF_PREAMBLE_TIME 40
#define OFDM_HALF_PLCP_BITS 22
#define OFDM_HALF_SYMBOL_TIME 8
#define OFDM_QUARTER_SIFS_TIME 64
#define OFDM_QUARTER_PREAMBLE_TIME 80
#define OFDM_QUARTER_PLCP_BITS 22
#define OFDM_QUARTER_SYMBOL_TIME 16
#define TURBO_SIFS_TIME 8
#define TURBO_PREAMBLE_TIME 14
#define TURBO_PLCP_BITS 22
#define TURBO_SYMBOL_TIME 4
/*
* Compute the time to transmit a frame of length frameLen bytes
* using the specified rate, phy, and short preamble setting.
* SIFS is included.
*/
uint16_t
ieee80211_compute_duration(const struct ieee80211_rate_table *rt,
uint32_t frameLen, uint16_t rate, int isShortPreamble)
{
uint8_t rix = rt->rateCodeToIndex[rate];
uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
uint32_t kbps;
KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
kbps = rt->info[rix].rateKbps;
if (kbps == 0) /* XXX bandaid for channel changes */
return 0;
switch (rt->info[rix].phy) {
case IEEE80211_T_CCK:
phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
if (isShortPreamble && rt->info[rix].shortPreamble)
phyTime >>= 1;
numBits = frameLen << 3;
txTime = CCK_SIFS_TIME + phyTime
+ ((numBits * 1000)/kbps);
break;
case IEEE80211_T_OFDM:
bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
KASSERT(bitsPerSymbol != 0, ("full rate bps"));
numBits = OFDM_PLCP_BITS + (frameLen << 3);
numSymbols = howmany(numBits, bitsPerSymbol);
txTime = OFDM_SIFS_TIME
+ OFDM_PREAMBLE_TIME
+ (numSymbols * OFDM_SYMBOL_TIME);
break;
case IEEE80211_T_OFDM_HALF:
bitsPerSymbol = (kbps * OFDM_HALF_SYMBOL_TIME) / 1000;
KASSERT(bitsPerSymbol != 0, ("1/4 rate bps"));
numBits = OFDM_PLCP_BITS + (frameLen << 3);
numSymbols = howmany(numBits, bitsPerSymbol);
txTime = OFDM_HALF_SIFS_TIME
+ OFDM_HALF_PREAMBLE_TIME
+ (numSymbols * OFDM_HALF_SYMBOL_TIME);
break;
case IEEE80211_T_OFDM_QUARTER:
bitsPerSymbol = (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000;
KASSERT(bitsPerSymbol != 0, ("1/2 rate bps"));
numBits = OFDM_PLCP_BITS + (frameLen << 3);
numSymbols = howmany(numBits, bitsPerSymbol);
txTime = OFDM_QUARTER_SIFS_TIME
+ OFDM_QUARTER_PREAMBLE_TIME
+ (numSymbols * OFDM_QUARTER_SYMBOL_TIME);
break;
case IEEE80211_T_TURBO:
/* we still save OFDM rates in kbps - so double them */
bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000;
KASSERT(bitsPerSymbol != 0, ("turbo bps"));
numBits = TURBO_PLCP_BITS + (frameLen << 3);
numSymbols = howmany(numBits, bitsPerSymbol);
txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME
+ (numSymbols * TURBO_SYMBOL_TIME);
break;
default:
panic("%s: unknown phy %u (rate %u)\n", __func__,
rt->info[rix].phy, rate);
break;
}
return txTime;
}
@@ -0,0 +1,157 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_PHY_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_PHY_H_
#ifdef _KERNEL
/*
* IEEE 802.11 PHY-related definitions.
*/
void ieee80211_phy_init(void);
/*
* Contention window (slots).
*/
#define IEEE80211_CW_MAX 1023 /* aCWmax */
#define IEEE80211_CW_MIN_0 31 /* DS/CCK aCWmin, ERP aCWmin(0) */
#define IEEE80211_CW_MIN_1 15 /* OFDM aCWmin, ERP aCWmin(1) */
/*
* SIFS (microseconds).
*/
#define IEEE80211_DUR_SIFS 10 /* DS/CCK/ERP SIFS */
#define IEEE80211_DUR_OFDM_SIFS 16 /* OFDM SIFS */
/*
* Slot time (microseconds).
*/
#define IEEE80211_DUR_SLOT 20 /* DS/CCK slottime, ERP long slottime */
#define IEEE80211_DUR_SHSLOT 9 /* ERP short slottime */
#define IEEE80211_DUR_OFDM_SLOT 9 /* OFDM slottime */
/*
* DIFS (microseconds).
*/
#define IEEE80211_DUR_DIFS(sifs, slot) ((sifs) + 2 * (slot))
struct ieee80211_channel;
struct ieee80211_rate_table {
int rateCount; /* NB: for proper padding */
uint8_t rateCodeToIndex[256]; /* back mapping */
struct {
uint8_t phy; /* CCK/OFDM/TURBO */
uint32_t rateKbps; /* transfer rate in kbs */
uint8_t shortPreamble; /* mask for enabling short
* preamble in CCK rate code */
uint8_t dot11Rate; /* value for supported rates
* info element of MLME */
uint8_t ctlRateIndex; /* index of next lower basic
* rate; used for dur. calcs */
uint16_t lpAckDuration; /* long preamble ACK dur. */
uint16_t spAckDuration; /* short preamble ACK dur. */
} info[32];
};
const struct ieee80211_rate_table *ieee80211_get_ratetable(
struct ieee80211_channel *);
static __inline__ uint8_t
ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
{
uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
return rt->info[cix].dot11Rate;
}
static __inline__ uint8_t
ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
{
uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
return rt->info[cix].dot11Rate;
}
static __inline__ enum ieee80211_phytype
ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate)
{
uint8_t rix = rt->rateCodeToIndex[rate];
KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
return rt->info[rix].phy;
}
static __inline__ int
ieee80211_isratevalid(const struct ieee80211_rate_table *rt, uint8_t rate)
{
return rt->rateCodeToIndex[rate] != (uint8_t)-1;
}
/*
* Calculate ACK field for
* o non-fragment data frames
* o management frames
* sent using rate, phy and short preamble setting.
*/
static __inline__ uint16_t
ieee80211_ack_duration(const struct ieee80211_rate_table *rt,
uint8_t rate, int isShortPreamble)
{
uint8_t rix = rt->rateCodeToIndex[rate];
KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
if (isShortPreamble) {
KASSERT(rt->info[rix].spAckDuration != 0,
("shpreamble ack dur is not computed!\n"));
return rt->info[rix].spAckDuration;
} else {
KASSERT(rt->info[rix].lpAckDuration != 0,
("lgpreamble ack dur is not computed!\n"));
return rt->info[rix].lpAckDuration;
}
}
/*
* Compute the time to transmit a frame of length frameLen bytes
* using the specified 802.11 rate code, phy, and short preamble
* setting.
*
* NB: SIFS is included.
*/
uint16_t ieee80211_compute_duration(const struct ieee80211_rate_table *,
uint32_t frameLen, uint16_t rate, int isShortPreamble);
/*
* Convert PLCP signal/rate field to 802.11 rate code (.5Mbits/s)
*/
uint8_t ieee80211_plcp2rate(uint8_t, enum ieee80211_phytype);
/*
* Convert 802.11 rate code to PLCP signal.
*/
uint8_t ieee80211_rate2plcp(int, enum ieee80211_phytype);
#endif /* _KERNEL */
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_PHY_H_ */
@@ -0,0 +1,525 @@
/*-
* 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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11 power save support.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net/bpf.h>
static void ieee80211_update_ps(struct ieee80211vap *, int);
static int ieee80211_set_tim(struct ieee80211_node *, int);
void
ieee80211_power_attach(struct ieee80211com *ic)
{
}
void
ieee80211_power_detach(struct ieee80211com *ic)
{
}
void
ieee80211_power_vattach(struct ieee80211vap *vap)
{
if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
vap->iv_opmode == IEEE80211_M_IBSS) {
/* NB: driver should override */
vap->iv_update_ps = ieee80211_update_ps;
vap->iv_set_tim = ieee80211_set_tim;
}
}
void
ieee80211_power_latevattach(struct ieee80211vap *vap)
{
/*
* Allocate these only if needed. Beware that we
* know adhoc mode doesn't support ATIM yet...
*/
if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
vap->iv_tim_len = howmany(vap->iv_max_aid,8) * sizeof(uint8_t);
vap->iv_tim_bitmap = (uint8_t *) malloc(vap->iv_tim_len,
M_80211_POWER, M_NOWAIT | M_ZERO);
if (vap->iv_tim_bitmap == NULL) {
printf("%s: no memory for TIM bitmap!\n", __func__);
/* XXX good enough to keep from crashing? */
vap->iv_tim_len = 0;
}
}
}
void
ieee80211_power_vdetach(struct ieee80211vap *vap)
{
if (vap->iv_tim_bitmap != NULL) {
free(vap->iv_tim_bitmap, M_80211_POWER);
vap->iv_tim_bitmap = NULL;
}
}
void
ieee80211_psq_init(struct ieee80211_psq *psq, const char *name)
{
memset(psq, 0, sizeof(psq));
psq->psq_maxlen = IEEE80211_PS_MAX_QUEUE;
IEEE80211_PSQ_INIT(psq, name); /* OS-dependent setup */
}
void
ieee80211_psq_cleanup(struct ieee80211_psq *psq)
{
#if 0
psq_drain(psq); /* XXX should not be needed? */
#else
KASSERT(psq->psq_len == 0, ("%d frames on ps q", psq->psq_len));
#endif
IEEE80211_PSQ_DESTROY(psq); /* OS-dependent cleanup */
}
/*
* Return the highest priority frame in the ps queue.
*/
struct mbuf *
ieee80211_node_psq_dequeue(struct ieee80211_node *ni, int *qlen)
{
struct ieee80211_psq *psq = &ni->ni_psq;
struct ieee80211_psq_head *qhead;
struct mbuf *m;
IEEE80211_PSQ_LOCK(psq);
qhead = &psq->psq_head[0];
again:
if ((m = qhead->head) != NULL) {
if ((qhead->head = m->m_nextpkt) == NULL)
qhead->tail = NULL;
KASSERT(qhead->len > 0, ("qhead len %d", qhead->len));
qhead->len--;
KASSERT(psq->psq_len > 0, ("psq len %d", psq->psq_len));
psq->psq_len--;
m->m_nextpkt = NULL;
}
if (m == NULL && qhead == &psq->psq_head[0]) {
/* Algol-68 style for loop */
qhead = &psq->psq_head[1];
goto again;
}
if (qlen != NULL)
*qlen = psq->psq_len;
IEEE80211_PSQ_UNLOCK(psq);
return m;
}
/*
* Reclaim an mbuf from the ps q. If marked with M_ENCAP
* we assume there is a node reference that must be relcaimed.
*/
static void
psq_mfree(struct mbuf *m)
{
if (m->m_flags & M_ENCAP) {
struct ieee80211_node *ni = (void *) m->m_pkthdr.rcvif;
ieee80211_free_node(ni);
}
m->m_nextpkt = NULL;
m_freem(m);
}
/*
* Clear any frames queued in the power save queue.
* The number of frames that were present is returned.
*/
static int
psq_drain(struct ieee80211_psq *psq)
{
struct ieee80211_psq_head *qhead;
struct mbuf *m;
int qlen;
IEEE80211_PSQ_LOCK(psq);
qlen = psq->psq_len;
qhead = &psq->psq_head[0];
again:
while ((m = qhead->head) != NULL) {
qhead->head = m->m_nextpkt;
psq_mfree(m);
}
qhead->tail = NULL;
qhead->len = 0;
if (qhead == &psq->psq_head[0]) { /* Algol-68 style for loop */
qhead = &psq->psq_head[1];
goto again;
}
psq->psq_len = 0;
IEEE80211_PSQ_UNLOCK(psq);
return qlen;
}
/*
* Clear any frames queued in the power save queue.
* The number of frames that were present is returned.
*/
int
ieee80211_node_psq_drain(struct ieee80211_node *ni)
{
return psq_drain(&ni->ni_psq);
}
/*
* Age frames on the power save queue. The aging interval is
* 4 times the listen interval specified by the station. This
* number is factored into the age calculations when the frame
* is placed on the queue. We store ages as time differences
* so we can check and/or adjust only the head of the list.
* If a frame's age exceeds the threshold then discard it.
* The number of frames discarded is returned so the caller
* can check if it needs to adjust the tim.
*/
int
ieee80211_node_psq_age(struct ieee80211_node *ni)
{
struct ieee80211_psq *psq = &ni->ni_psq;
int discard = 0;
if (psq->psq_len != 0) {
#ifdef IEEE80211_DEBUG
struct ieee80211vap *vap = ni->ni_vap;
#endif
struct ieee80211_psq_head *qhead;
struct mbuf *m;
IEEE80211_PSQ_LOCK(psq);
qhead = &psq->psq_head[0];
again:
while ((m = qhead->head) != NULL &&
M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"discard frame, age %u", M_AGE_GET(m));
if ((qhead->head = m->m_nextpkt) == NULL)
qhead->tail = NULL;
KASSERT(qhead->len > 0, ("qhead len %d", qhead->len));
qhead->len--;
KASSERT(psq->psq_len > 0, ("psq len %d", psq->psq_len));
psq->psq_len--;
psq_mfree(m);
discard++;
}
if (qhead == &psq->psq_head[0]) { /* Algol-68 style for loop */
qhead = &psq->psq_head[1];
goto again;
}
if (m != NULL)
M_AGE_SUB(m, IEEE80211_INACT_WAIT);
IEEE80211_PSQ_UNLOCK(psq);
IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"discard %u frames for age", discard);
IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard);
}
return discard;
}
/*
* Handle a change in the PS station occupancy.
*/
static void
ieee80211_update_ps(struct ieee80211vap *vap, int nsta)
{
KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP ||
vap->iv_opmode == IEEE80211_M_IBSS,
("operating mode %u", vap->iv_opmode));
}
/*
* Indicate whether there are frames queued for a station in power-save mode.
*/
static int
ieee80211_set_tim(struct ieee80211_node *ni, int set)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
uint16_t aid;
int changed;
KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP ||
vap->iv_opmode == IEEE80211_M_IBSS,
("operating mode %u", vap->iv_opmode));
aid = IEEE80211_AID(ni->ni_associd);
KASSERT(aid < vap->iv_max_aid,
("bogus aid %u, max %u", aid, vap->iv_max_aid));
IEEE80211_LOCK(ic);
changed = (set != (isset(vap->iv_tim_bitmap, aid) != 0));
if (changed) {
if (set) {
setbit(vap->iv_tim_bitmap, aid);
vap->iv_ps_pending++;
} else {
clrbit(vap->iv_tim_bitmap, aid);
vap->iv_ps_pending--;
}
/* NB: we know vap is in RUN state so no need to check */
vap->iv_update_beacon(vap, IEEE80211_BEACON_TIM);
}
IEEE80211_UNLOCK(ic);
return changed;
}
/*
* Save an outbound packet for a node in power-save sleep state.
* The new packet is placed on the node's saved queue, and the TIM
* is changed, if necessary.
*/
int
ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
{
struct ieee80211_psq *psq = &ni->ni_psq;
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_psq_head *qhead;
int qlen, age;
IEEE80211_PSQ_LOCK(psq);
if (psq->psq_len >= psq->psq_maxlen) {
psq->psq_drops++;
IEEE80211_PSQ_UNLOCK(psq);
IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
"pwr save q overflow, drops %d (size %d)",
psq->psq_drops, psq->psq_len);
#ifdef IEEE80211_DEBUG
if (ieee80211_msg_dumppkts(vap))
ieee80211_dump_pkt(ni->ni_ic, mtod(m, caddr_t),
m->m_len, -1, -1);
#endif
psq_mfree(m);
return ENOSPC;
}
/*
* Tag the frame with it's expiry time and insert it in
* the appropriate queue. The aging interval is 4 times
* the listen interval specified by the station. Frames
* that sit around too long are reclaimed using this
* information.
*/
/* TU -> secs. XXX handle overflow? */
age = IEEE80211_TU_TO_MS((ni->ni_intval * ic->ic_bintval) << 2) / 1000;
/*
* Encapsulated frames go on the high priority queue,
* other stuff goes on the low priority queue. We use
* this to order frames returned out of the driver
* ahead of frames we collect in ieee80211_start.
*/
if (m->m_flags & M_ENCAP)
qhead = &psq->psq_head[0];
else
qhead = &psq->psq_head[1];
if (qhead->tail == NULL) {
struct mbuf *mh;
qhead->head = m;
/*
* Take care to adjust age when inserting the first
* frame of a queue and the other queue already has
* frames. We need to preserve the age difference
* relationship so ieee80211_node_psq_age works.
*/
if (qhead == &psq->psq_head[1]) {
mh = psq->psq_head[0].head;
if (mh != NULL)
age-= M_AGE_GET(mh);
} else {
mh = psq->psq_head[1].head;
if (mh != NULL) {
int nage = M_AGE_GET(mh) - age;
/* XXX is clamping to zero good 'nuf? */
M_AGE_SET(mh, nage < 0 ? 0 : nage);
}
}
} else {
qhead->tail->m_nextpkt = m;
age -= M_AGE_GET(qhead->head);
}
KASSERT(age >= 0, ("age %d", age));
M_AGE_SET(m, age);
m->m_nextpkt = NULL;
qhead->tail = m;
qhead->len++;
qlen = ++(psq->psq_len);
IEEE80211_PSQ_UNLOCK(psq);
IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"save frame with age %d, %u now queued", age, qlen);
if (qlen == 1 && vap->iv_set_tim != NULL)
vap->iv_set_tim(ni, 1);
return 0;
}
/*
* Move frames from the ps q to the vap's send queue
* and/or the driver's send queue; and kick the start
* method for each, as appropriate. Note we're careful
* to preserve packet ordering here.
*/
static void
pwrsave_flushq(struct ieee80211_node *ni)
{
struct ieee80211_psq *psq = &ni->ni_psq;
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_psq_head *qhead;
struct ifnet *parent, *ifp;
IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"flush ps queue, %u packets queued", psq->psq_len);
IEEE80211_PSQ_LOCK(psq);
qhead = &psq->psq_head[0]; /* 802.11 frames */
if (qhead->head != NULL) {
/* XXX could dispatch through vap and check M_ENCAP */
parent = vap->iv_ic->ic_ifp;
/* XXX need different driver interface */
/* XXX bypasses q max and OACTIVE */
IF_PREPEND_LIST(&parent->if_snd, qhead->head, qhead->tail,
qhead->len);
qhead->head = qhead->tail = NULL;
qhead->len = 0;
} else
parent = NULL;
qhead = &psq->psq_head[1]; /* 802.3 frames */
if (qhead->head != NULL) {
ifp = vap->iv_ifp;
/* XXX need different driver interface */
/* XXX bypasses q max and OACTIVE */
IF_PREPEND_LIST(&ifp->if_snd, qhead->head, qhead->tail,
qhead->len);
qhead->head = qhead->tail = NULL;
qhead->len = 0;
} else
ifp = NULL;
psq->psq_len = 0;
IEEE80211_PSQ_UNLOCK(psq);
/* NB: do this outside the psq lock */
/* XXX packets might get reordered if parent is OACTIVE */
if (parent != NULL)
if_start(parent);
if (ifp != NULL)
if_start(ifp);
}
/*
* Handle station power-save state change.
*/
void
ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
{
struct ieee80211vap *vap = ni->ni_vap;
int update;
update = 0;
if (enable) {
if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
vap->iv_ps_sta++;
update = 1;
}
ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"power save mode on, %u sta's in ps mode", vap->iv_ps_sta);
if (update)
vap->iv_update_ps(vap, vap->iv_ps_sta);
} else {
if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
vap->iv_ps_sta--;
update = 1;
}
ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"power save mode off, %u sta's in ps mode", vap->iv_ps_sta);
/* NB: order here is intentional so TIM is clear before flush */
if (vap->iv_set_tim != NULL)
vap->iv_set_tim(ni, 0);
if (update) {
/* NB if no sta's in ps, driver should flush mc q */
vap->iv_update_ps(vap, vap->iv_ps_sta);
}
if (ni->ni_psq.psq_len != 0)
pwrsave_flushq(ni);
}
}
/*
* Handle power-save state change in station mode.
*/
void
ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable)
{
struct ieee80211_node *ni = vap->iv_bss;
if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0)))
return;
IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"sta power save mode %s", enable ? "on" : "off");
if (!enable) {
ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
ieee80211_send_nulldata(ieee80211_ref_node(ni));
/*
* Flush any queued frames; we can do this immediately
* because we know they'll be queued behind the null
* data frame we send the ap.
* XXX can we use a data frame to take us out of ps?
*/
if (ni->ni_psq.psq_len != 0)
pwrsave_flushq(ni);
} else {
ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
ieee80211_send_nulldata(ieee80211_ref_node(ni));
}
}
@@ -0,0 +1,79 @@
/*-
* 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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_POWER_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_POWER_H_
struct ieee80211com;
struct ieee80211vap;
struct ieee80211_node;
struct mbuf;
/*
* Power save packet queues. There are two queues, one
* for frames coming from the net80211 layer and the other
* for frames that come from the driver. Frames from the
* driver are expected to have M_ENCAP marked to indicate
* they have already been encapsulated and are treated as
* higher priority: they are sent first when flushing the
* queue on a power save state change or in response to a
* ps-poll frame.
*
* Note that frames sent from the high priority queue are
* fed directly to the driver without going through
* ieee80211_start again; drivers that send up encap'd
* frames are required to handle them when they come back.
*/
struct ieee80211_psq {
ieee80211_psq_lock_t psq_lock;
int psq_len;
int psq_maxlen;
int psq_drops;
struct ieee80211_psq_head {
struct mbuf *head;
struct mbuf *tail;
int len;
} psq_head[2]; /* 2 priorities */
};
void ieee80211_psq_init(struct ieee80211_psq *, const char *);
void ieee80211_psq_cleanup(struct ieee80211_psq *);
void ieee80211_power_attach(struct ieee80211com *);
void ieee80211_power_detach(struct ieee80211com *);
void ieee80211_power_vattach(struct ieee80211vap *);
void ieee80211_power_vdetach(struct ieee80211vap *);
void ieee80211_power_latevattach(struct ieee80211vap *);
struct mbuf *ieee80211_node_psq_dequeue(struct ieee80211_node *ni, int *qlen);
int ieee80211_node_psq_drain(struct ieee80211_node *);
int ieee80211_node_psq_age(struct ieee80211_node *);
int ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *);
void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
void ieee80211_sta_pwrsave(struct ieee80211vap *, int enable);
void ieee80211_power_poll(struct ieee80211com *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_POWER_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,387 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002-2009 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_PROTO_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_PROTO_H_
/*
* 802.11 protocol implementation definitions.
*/
void ieee80211_auth_setup(void);
enum ieee80211_state {
IEEE80211_S_INIT = 0, /* default state */
IEEE80211_S_SCAN = 1, /* scanning */
IEEE80211_S_AUTH = 2, /* try to authenticate */
IEEE80211_S_ASSOC = 3, /* try to assoc */
IEEE80211_S_CAC = 4, /* doing channel availability check */
IEEE80211_S_RUN = 5, /* operational (e.g. associated) */
IEEE80211_S_CSA = 6, /* channel switch announce pending */
IEEE80211_S_SLEEP = 7, /* power save */
};
#define IEEE80211_S_MAX (IEEE80211_S_SLEEP+1)
#define IEEE80211_SEND_MGMT(_ni,_type,_arg) \
((*(_ni)->ni_ic->ic_send_mgmt)(_ni, _type, _arg))
extern const char *ieee80211_mgt_subtype_name[];
extern const char *ieee80211_phymode_name[IEEE80211_MODE_MAX];
extern const int ieee80211_opcap[IEEE80211_OPMODE_MAX];
void ieee80211_proto_attach(struct ieee80211com *);
void ieee80211_proto_detach(struct ieee80211com *);
void ieee80211_proto_vattach(struct ieee80211vap *);
void ieee80211_proto_vdetach(struct ieee80211vap *);
void ieee80211_syncifflag_locked(struct ieee80211com *, int flag);
void ieee80211_syncflag(struct ieee80211vap *, int flag);
void ieee80211_syncflag_ht(struct ieee80211vap *, int flag);
void ieee80211_syncflag_ext(struct ieee80211vap *, int flag);
#define ieee80211_input(ni, m, rssi, nf) \
((ni)->ni_vap->iv_input(ni, m, rssi, nf))
int ieee80211_input_all(struct ieee80211com *, struct mbuf *, int, int);
struct ieee80211_bpf_params;
int ieee80211_mgmt_output(struct ieee80211_node *, struct mbuf *, int,
struct ieee80211_bpf_params *);
int ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
int ieee80211_output(struct ifnet *, struct mbuf *,
struct sockaddr *, struct route *ro);
void ieee80211_send_setup(struct ieee80211_node *, struct mbuf *, int, int,
const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN],
const uint8_t [IEEE80211_ADDR_LEN]);
void ieee80211_start(struct ifnet *);
int ieee80211_send_nulldata(struct ieee80211_node *);
int ieee80211_classify(struct ieee80211_node *, struct mbuf *m);
struct mbuf *ieee80211_mbuf_adjust(struct ieee80211vap *, int,
struct ieee80211_key *, struct mbuf *);
struct mbuf *ieee80211_encap(struct ieee80211vap *, struct ieee80211_node *,
struct mbuf *);
int ieee80211_send_mgmt(struct ieee80211_node *, int, int);
struct ieee80211_appie;
int ieee80211_send_probereq(struct ieee80211_node *ni,
const uint8_t sa[IEEE80211_ADDR_LEN],
const uint8_t da[IEEE80211_ADDR_LEN],
const uint8_t bssid[IEEE80211_ADDR_LEN],
const uint8_t *ssid, size_t ssidlen);
/*
* The formation of ProbeResponse frames requires guidance to
* deal with legacy clients. When the client is identified as
* "legacy 11b" ieee80211_send_proberesp is passed this token.
*/
#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */
#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */
#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */
struct mbuf *ieee80211_alloc_proberesp(struct ieee80211_node *, int);
int ieee80211_send_proberesp(struct ieee80211vap *,
const uint8_t da[IEEE80211_ADDR_LEN], int);
struct mbuf *ieee80211_alloc_rts(struct ieee80211com *ic,
const uint8_t [IEEE80211_ADDR_LEN],
const uint8_t [IEEE80211_ADDR_LEN], uint16_t);
struct mbuf *ieee80211_alloc_cts(struct ieee80211com *,
const uint8_t [IEEE80211_ADDR_LEN], uint16_t);
uint8_t *ieee80211_add_rates(uint8_t *, const struct ieee80211_rateset *);
uint8_t *ieee80211_add_xrates(uint8_t *, const struct ieee80211_rateset *);
uint16_t ieee80211_getcapinfo(struct ieee80211vap *,
struct ieee80211_channel *);
void ieee80211_reset_erp(struct ieee80211com *);
void ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
int ieee80211_iserp_rateset(const struct ieee80211_rateset *);
void ieee80211_setbasicrates(struct ieee80211_rateset *,
enum ieee80211_phymode);
void ieee80211_addbasicrates(struct ieee80211_rateset *,
enum ieee80211_phymode);
/*
* Return the size of the 802.11 header for a management or data frame.
*/
static __inline int
ieee80211_hdrsize(const void *data)
{
const struct ieee80211_frame *wh = data;
int size = sizeof(struct ieee80211_frame);
/* NB: we don't handle control frames */
KASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL,
("%s: control frame", __func__));
if (IEEE80211_IS_DSTODS(wh))
size += IEEE80211_ADDR_LEN;
if (IEEE80211_QOS_HAS_SEQ(wh))
size += sizeof(uint16_t);
return size;
}
/*
* Like ieee80211_hdrsize, but handles any type of frame.
*/
static __inline int
ieee80211_anyhdrsize(const void *data)
{
const struct ieee80211_frame *wh = data;
if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
case IEEE80211_FC0_SUBTYPE_CTS:
case IEEE80211_FC0_SUBTYPE_ACK:
return sizeof(struct ieee80211_frame_ack);
case IEEE80211_FC0_SUBTYPE_BAR:
return sizeof(struct ieee80211_frame_bar);
}
return sizeof(struct ieee80211_frame_min);
} else
return ieee80211_hdrsize(data);
}
/*
* Template for an in-kernel authenticator. Authenticators
* register with the protocol code and are typically loaded
* as separate modules as needed. One special authenticator
* is xauth; it intercepts requests so that protocols like
* WPA can be handled in user space.
*/
struct ieee80211_authenticator {
const char *ia_name; /* printable name */
int (*ia_attach)(struct ieee80211vap *);
void (*ia_detach)(struct ieee80211vap *);
void (*ia_node_join)(struct ieee80211_node *);
void (*ia_node_leave)(struct ieee80211_node *);
};
void ieee80211_authenticator_register(int type,
const struct ieee80211_authenticator *);
void ieee80211_authenticator_unregister(int type);
const struct ieee80211_authenticator *ieee80211_authenticator_get(int auth);
struct ieee80211req;
/*
* Template for an MAC ACL policy module. Such modules
* register with the protocol code and are passed the sender's
* address of each received auth frame for validation.
*/
struct ieee80211_aclator {
const char *iac_name; /* printable name */
int (*iac_attach)(struct ieee80211vap *);
void (*iac_detach)(struct ieee80211vap *);
int (*iac_check)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
int (*iac_add)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
int (*iac_remove)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
int (*iac_flush)(struct ieee80211vap *);
int (*iac_setpolicy)(struct ieee80211vap *, int);
int (*iac_getpolicy)(struct ieee80211vap *);
int (*iac_setioctl)(struct ieee80211vap *, struct ieee80211req *);
int (*iac_getioctl)(struct ieee80211vap *, struct ieee80211req *);
};
void ieee80211_aclator_register(const struct ieee80211_aclator *);
void ieee80211_aclator_unregister(const struct ieee80211_aclator *);
const struct ieee80211_aclator *ieee80211_aclator_get(const char *name);
/* flags for ieee80211_fix_rate() */
#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */
#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed legacy rate */
#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */
#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */
#define IEEE80211_F_DOBRS 0x00000010 /* check basic rate set */
#define IEEE80211_F_JOIN 0x00000020 /* sta joining our bss */
#define IEEE80211_F_DOFMCS 0x00000040 /* use fixed HT rate */
int ieee80211_fix_rate(struct ieee80211_node *,
struct ieee80211_rateset *, int);
/*
* WME/WMM support.
*/
struct wmeParams {
uint8_t wmep_acm;
uint8_t wmep_aifsn;
uint8_t wmep_logcwmin; /* log2(cwmin) */
uint8_t wmep_logcwmax; /* log2(cwmax) */
uint8_t wmep_txopLimit;
uint8_t wmep_noackPolicy; /* 0 (ack), 1 (no ack) */
};
#define IEEE80211_TXOP_TO_US(_txop) ((_txop)<<5)
#define IEEE80211_US_TO_TXOP(_us) ((_us)>>5)
struct chanAccParams {
uint8_t cap_info; /* version of the current set */
struct wmeParams cap_wmeParams[WME_NUM_AC];
};
struct ieee80211_wme_state {
u_int wme_flags;
#define WME_F_AGGRMODE 0x00000001 /* STATUS: WME agressive mode */
u_int wme_hipri_traffic; /* VI/VO frames in beacon interval */
u_int wme_hipri_switch_thresh;/* agressive mode switch thresh */
u_int wme_hipri_switch_hysteresis;/* agressive mode switch hysteresis */
struct wmeParams wme_params[4]; /* from assoc resp for each AC*/
struct chanAccParams wme_wmeChanParams; /* WME params applied to self */
struct chanAccParams wme_wmeBssChanParams;/* WME params bcast to stations */
struct chanAccParams wme_chanParams; /* params applied to self */
struct chanAccParams wme_bssChanParams; /* params bcast to stations */
int (*wme_update)(struct ieee80211com *);
};
void ieee80211_wme_initparams(struct ieee80211vap *);
void ieee80211_wme_updateparams(struct ieee80211vap *);
void ieee80211_wme_updateparams_locked(struct ieee80211vap *);
/*
* Return the WME TID from a QoS frame. If no TID
* is present return the index for the "non-QoS" entry.
*/
static __inline uint8_t
ieee80211_gettid(const struct ieee80211_frame *wh)
{
uint8_t tid;
if (IEEE80211_QOS_HAS_SEQ(wh)) {
if (IEEE80211_IS_DSTODS(wh))
tid = ((const struct ieee80211_qosframe_addr4 *)wh)->
i_qos[0];
else
tid = ((const struct ieee80211_qosframe *)wh)->i_qos[0];
tid &= IEEE80211_QOS_TID;
} else
tid = IEEE80211_NONQOS_TID;
return tid;
}
void ieee80211_waitfor_parent(struct ieee80211com *);
void ieee80211_start_locked(struct ieee80211vap *);
void ieee80211_init(void *);
void ieee80211_start_all(struct ieee80211com *);
void ieee80211_stop_locked(struct ieee80211vap *);
void ieee80211_stop(struct ieee80211vap *);
void ieee80211_stop_all(struct ieee80211com *);
void ieee80211_suspend_all(struct ieee80211com *);
void ieee80211_resume_all(struct ieee80211com *);
void ieee80211_dturbo_switch(struct ieee80211vap *, int newflags);
void ieee80211_swbmiss(void *arg);
void ieee80211_beacon_miss(struct ieee80211com *);
int ieee80211_new_state(struct ieee80211vap *, enum ieee80211_state, int);
void ieee80211_print_essid(const uint8_t *, int);
void ieee80211_dump_pkt(struct ieee80211com *,
const uint8_t *, int, int, int);
extern const char *ieee80211_opmode_name[];
extern const char *ieee80211_state_name[IEEE80211_S_MAX];
extern const char *ieee80211_wme_acnames[];
/*
* Beacon frames constructed by ieee80211_beacon_alloc
* have the following structure filled in so drivers
* can update the frame later w/ minimal overhead.
*/
struct ieee80211_beacon_offsets {
uint8_t bo_flags[4]; /* update/state flags */
uint16_t *bo_caps; /* capabilities */
uint8_t *bo_cfp; /* start of CFParms element */
uint8_t *bo_tim; /* start of atim/dtim */
uint8_t *bo_wme; /* start of WME parameters */
uint8_t *bo_tdma; /* start of TDMA parameters */
uint8_t *bo_tim_trailer;/* start of fixed-size trailer */
uint16_t bo_tim_len; /* atim/dtim length in bytes */
uint16_t bo_tim_trailer_len;/* tim trailer length in bytes */
uint8_t *bo_erp; /* start of ERP element */
uint8_t *bo_htinfo; /* start of HT info element */
uint8_t *bo_ath; /* start of ATH parameters */
uint8_t *bo_appie; /* start of AppIE element */
uint16_t bo_appie_len; /* AppIE length in bytes */
uint16_t bo_csa_trailer_len;;
uint8_t *bo_csa; /* start of CSA element */
uint8_t *bo_spare[4];
};
struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *,
struct ieee80211_beacon_offsets *);
/*
* Beacon frame updates are signaled through calls to iv_update_beacon
* with one of the IEEE80211_BEACON_* tokens defined below. For devices
* that construct beacon frames on the host this can trigger a rebuild
* or defer the processing. For devices that offload beacon frame
* handling this callback can be used to signal a rebuild. The bo_flags
* array in the ieee80211_beacon_offsets structure is intended to record
* deferred processing requirements; ieee80211_beacon_update uses the
* state to optimize work. Since this structure is owned by the driver
* and not visible to the 802.11 layer drivers must supply an iv_update_beacon
* callback that marks the flag bits and schedules (as necessary) an update.
*/
enum {
IEEE80211_BEACON_CAPS = 0, /* capabilities */
IEEE80211_BEACON_TIM = 1, /* DTIM/ATIM */
IEEE80211_BEACON_WME = 2,
IEEE80211_BEACON_ERP = 3, /* Extended Rate Phy */
IEEE80211_BEACON_HTINFO = 4, /* HT Information */
IEEE80211_BEACON_APPIE = 5, /* Application IE's */
IEEE80211_BEACON_CFP = 6, /* CFParms */
IEEE80211_BEACON_CSA = 7, /* Channel Switch Announcement */
IEEE80211_BEACON_TDMA = 9, /* TDMA Info */
IEEE80211_BEACON_ATH = 10, /* ATH parameters */
};
int ieee80211_beacon_update(struct ieee80211_node *,
struct ieee80211_beacon_offsets *, struct mbuf *, int mcast);
void ieee80211_csa_startswitch(struct ieee80211com *,
struct ieee80211_channel *, int mode, int count);
void ieee80211_csa_completeswitch(struct ieee80211com *);
void ieee80211_csa_cancelswitch(struct ieee80211com *);
void ieee80211_cac_completeswitch(struct ieee80211vap *);
/*
* Notification methods called from the 802.11 state machine.
* Note that while these are defined here, their implementation
* is OS-specific.
*/
void ieee80211_notify_node_join(struct ieee80211_node *, int newassoc);
void ieee80211_notify_node_leave(struct ieee80211_node *);
void ieee80211_notify_scan_done(struct ieee80211vap *);
void ieee80211_notify_wds_discover(struct ieee80211_node *);
void ieee80211_notify_csa(struct ieee80211com *,
const struct ieee80211_channel *, int mode, int count);
void ieee80211_notify_radar(struct ieee80211com *,
const struct ieee80211_channel *);
enum ieee80211_notify_cac_event {
IEEE80211_NOTIFY_CAC_START = 0, /* CAC timer started */
IEEE80211_NOTIFY_CAC_STOP = 1, /* CAC intentionally stopped */
IEEE80211_NOTIFY_CAC_RADAR = 2, /* CAC stopped due to radar detectio */
IEEE80211_NOTIFY_CAC_EXPIRE = 3, /* CAC expired w/o radar */
};
void ieee80211_notify_cac(struct ieee80211com *,
const struct ieee80211_channel *,
enum ieee80211_notify_cac_event);
void ieee80211_notify_node_deauth(struct ieee80211_node *);
void ieee80211_notify_node_auth(struct ieee80211_node *);
void ieee80211_notify_country(struct ieee80211vap *, const uint8_t [],
const uint8_t cc[2]);
void ieee80211_notify_radio(struct ieee80211com *, int);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_PROTO_H_ */
@@ -0,0 +1,355 @@
/*-
* Copyright (c) 2009 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.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11 radiotap support.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/endian.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_llc.h>
#include <net/if_media.h>
#include <net80211/ieee80211_var.h>
static int radiotap_offset(struct ieee80211_radiotap_header *, int);
void
ieee80211_radiotap_attach(struct ieee80211com *ic,
struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap,
struct ieee80211_radiotap_header *rh, int rlen, uint32_t rx_radiotap)
{
#define B(_v) (1<<(_v))
int off;
th->it_len = htole16(roundup2(tlen, sizeof(uint32_t)));
th->it_present = htole32(tx_radiotap);
ic->ic_th = th;
/* calculate offset to channel data */
off = -1;
if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL))
off = radiotap_offset(th, IEEE80211_RADIOTAP_CHANNEL);
else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL))
off = radiotap_offset(th, IEEE80211_RADIOTAP_XCHANNEL);
if (off == -1) {
if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x",
__func__, tx_radiotap);
/* NB: we handle this case but data will have no chan spec */
} else
ic->ic_txchan = ((uint8_t *) th) + off;
rh->it_len = htole16(roundup2(rlen, sizeof(uint32_t)));
rh->it_present = htole32(rx_radiotap);
ic->ic_rh = rh;
/* calculate offset to channel data */
off = -1;
if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL))
off = radiotap_offset(rh, IEEE80211_RADIOTAP_CHANNEL);
else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL))
off = radiotap_offset(rh, IEEE80211_RADIOTAP_XCHANNEL);
if (off == -1) {
if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x",
__func__, rx_radiotap);
/* NB: we handle this case but data will have no chan spec */
} else
ic->ic_rxchan = ((uint8_t *) rh) + off;
#undef B
}
void
ieee80211_radiotap_detach(struct ieee80211com *ic)
{
}
void
ieee80211_radiotap_vattach(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_radiotap_header *th = ic->ic_th;
if (th != NULL && ic->ic_rh != NULL) {
/* radiotap DLT for raw 802.11 frames */
bpfattach2(vap->iv_ifp, DLT_IEEE802_11_RADIO,
sizeof(struct ieee80211_frame) + le16toh(th->it_len),
&vap->iv_rawbpf);
}
}
void
ieee80211_radiotap_vdetach(struct ieee80211vap *vap)
{
/* NB: bpfattach is called by ether_ifdetach and claims all taps */
}
static void
set_channel(void *p, const struct ieee80211_channel *c)
{
struct {
uint16_t freq;
uint16_t flags;
} *rc = p;
rc->freq = htole16(c->ic_freq);
rc->flags = htole16(c->ic_flags);
}
static void
set_xchannel(void *p, const struct ieee80211_channel *c)
{
struct {
uint32_t flags;
uint16_t freq;
uint8_t ieee;
uint8_t maxpow;
} *rc = p;
rc->flags = htole32(c->ic_flags);
rc->freq = htole16(c->ic_freq);
rc->ieee = c->ic_ieee;
rc->maxpow = c->ic_maxregpower;
}
/*
* Update radiotap state on channel change.
*/
void
ieee80211_radiotap_chan_change(struct ieee80211com *ic)
{
if (ic->ic_rxchan != NULL) {
struct ieee80211_radiotap_header *rh = ic->ic_rh;
if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL))
set_xchannel(ic->ic_rxchan, ic->ic_curchan);
else if (rh->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL))
set_channel(ic->ic_rxchan, ic->ic_curchan);
}
if (ic->ic_txchan != NULL) {
struct ieee80211_radiotap_header *th = ic->ic_th;
if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_XCHANNEL))
set_xchannel(ic->ic_txchan, ic->ic_curchan);
else if (th->it_present & htole32(1<<IEEE80211_RADIOTAP_CHANNEL))
set_channel(ic->ic_txchan, ic->ic_curchan);
}
}
/*
* Distribute radiotap data (+packet) to all monitor mode
* vaps with an active tap other than vap0.
*/
static void
spam_vaps(struct ieee80211vap *vap0, struct mbuf *m,
struct ieee80211_radiotap_header *rh, int len)
{
struct ieee80211com *ic = vap0->iv_ic;
struct ieee80211vap *vap;
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap != vap0 &&
vap->iv_opmode == IEEE80211_M_MONITOR &&
(vap->iv_flags_ext & IEEE80211_FEXT_BPF) &&
vap->iv_state != IEEE80211_S_INIT)
bpf_mtap2(vap->iv_rawbpf, rh, len, m);
}
}
/*
* Dispatch radiotap data for transmitted packet.
*/
void
ieee80211_radiotap_tx(struct ieee80211vap *vap0, struct mbuf *m)
{
struct ieee80211com *ic = vap0->iv_ic;
struct ieee80211_radiotap_header *th = ic->ic_th;
int len;
KASSERT(th != NULL, ("no tx radiotap header"));
len = le16toh(th->it_len);
if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF)
bpf_mtap2(vap0->iv_rawbpf, th, len, m);
/*
* Spam monitor mode vaps.
*/
if (ic->ic_montaps != 0)
spam_vaps(vap0, m, th, len);
}
/*
* Dispatch radiotap data for received packet.
*/
void
ieee80211_radiotap_rx(struct ieee80211vap *vap0, struct mbuf *m)
{
struct ieee80211com *ic = vap0->iv_ic;
struct ieee80211_radiotap_header *rh = ic->ic_rh;
int len;
KASSERT(rh != NULL, ("no rx radiotap header"));
len = le16toh(rh->it_len);
if (vap0->iv_flags_ext & IEEE80211_FEXT_BPF)
bpf_mtap2(vap0->iv_rawbpf, rh, len, m);
/*
* Spam monitor mode vaps with unicast frames. Multicast
* frames are handled by passing through ieee80211_input_all
* which distributes copies to the monitor mode vaps.
*/
if (ic->ic_montaps != 0 && (m->m_flags & M_BCAST) == 0)
spam_vaps(vap0, m, rh, len);
}
/*
* Dispatch radiotap data for a packet received outside the normal
* rx processing path; this is used, for example, to handle frames
* received with errors that would otherwise be dropped.
*/
void
ieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m)
{
struct ieee80211_radiotap_header *rh = ic->ic_rh;
int len = le16toh(rh->it_len);
struct ieee80211vap *vap;
/* XXX locking? */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (ieee80211_radiotap_active_vap(vap) &&
vap->iv_state != IEEE80211_S_INIT)
bpf_mtap2(vap->iv_rawbpf, rh, len, m);
}
}
/*
* Return the offset of the specified item in the radiotap
* header description. If the item is not present or is not
* known -1 is returned.
*/
static int
radiotap_offset(struct ieee80211_radiotap_header *rh, int item)
{
static const struct {
size_t align, width;
} items[] = {
[IEEE80211_RADIOTAP_TSFT] = {
.align = sizeof(uint64_t),
.width = sizeof(uint64_t),
},
[IEEE80211_RADIOTAP_FLAGS] = {
.align = sizeof(uint8_t),
.width = sizeof(uint8_t),
},
[IEEE80211_RADIOTAP_RATE] = {
.align = sizeof(uint8_t),
.width = sizeof(uint8_t),
},
[IEEE80211_RADIOTAP_CHANNEL] = {
.align = sizeof(uint16_t),
.width = 2*sizeof(uint16_t),
},
[IEEE80211_RADIOTAP_FHSS] = {
.align = sizeof(uint16_t),
.width = sizeof(uint16_t),
},
[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = {
.align = sizeof(uint8_t),
.width = sizeof(uint8_t),
},
[IEEE80211_RADIOTAP_DBM_ANTNOISE] = {
.align = sizeof(uint8_t),
.width = sizeof(uint8_t),
},
[IEEE80211_RADIOTAP_LOCK_QUALITY] = {
.align = sizeof(uint16_t),
.width = sizeof(uint16_t),
},
[IEEE80211_RADIOTAP_TX_ATTENUATION] = {
.align = sizeof(uint16_t),
.width = sizeof(uint16_t),
},
[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = {
.align = sizeof(uint16_t),
.width = sizeof(uint16_t),
},
[IEEE80211_RADIOTAP_DBM_TX_POWER] = {
.align = sizeof(uint8_t),
.width = sizeof(uint8_t),
},
[IEEE80211_RADIOTAP_ANTENNA] = {
.align = sizeof(uint8_t),
.width = sizeof(uint8_t),
},
[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = {
.align = sizeof(uint8_t),
.width = sizeof(uint8_t),
},
[IEEE80211_RADIOTAP_DB_ANTNOISE] = {
.align = sizeof(uint8_t),
.width = sizeof(uint8_t),
},
[IEEE80211_RADIOTAP_XCHANNEL] = {
.align = sizeof(uint32_t),
.width = 2*sizeof(uint32_t),
},
};
uint32_t present = le32toh(rh->it_present);
int off, i;
off = sizeof(struct ieee80211_radiotap_header);
for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) {
if ((present & (1<<i)) == 0)
continue;
if (items[i].align == 0) {
/* NB: unidentified element, don't guess */
printf("%s: unknown item %d\n", __func__, i);
return -1;
}
off = roundup2(off, items[i].align);
if (i == item) {
if (off + items[i].width > le16toh(rh->it_len)) {
/* NB: item does not fit in header data */
printf("%s: item %d not in header data, "
"off %d width %zu len %d\n", __func__, i,
off, items[i].width, le16toh(rh->it_len));
return -1;
}
return off;
}
off += items[i].width;
}
return -1;
}
@@ -0,0 +1,234 @@
/* $FreeBSD$ */
/* $NetBSD: ieee80211_radiotap.h,v 1.16 2007/01/06 05:51:15 dyoung Exp $ */
/*-
* Copyright (c) 2003, 2004 David Young. 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 David Young may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``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 DAVID
* YOUNG 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.
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_RADIOTAP_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_RADIOTAP_H_
/* A generic radio capture format is desirable. It must be
* rigidly defined (e.g., units for fields should be given),
* and easily extensible.
*
* The following is an extensible radio capture format. It is
* based on a bitmap indicating which fields are present.
*
* I am trying to describe precisely what the application programmer
* should expect in the following, and for that reason I tell the
* units and origin of each measurement (where it applies), or else I
* use sufficiently weaselly language ("is a monotonically nondecreasing
* function of...") that I cannot set false expectations for lawyerly
* readers.
*/
#if defined(__KERNEL__) || defined(_KERNEL)
#ifndef DLT_IEEE802_11_RADIO
#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus WLAN header */
#endif
#endif /* defined(__KERNEL__) || defined(_KERNEL) */
#define IEEE80211_RADIOTAP_HDRLEN 64 /* XXX deprecated */
/*
* The radio capture header precedes the 802.11 header.
*
* Note well: all radiotap fields are little-endian.
*/
struct ieee80211_radiotap_header {
uint8_t it_version; /* Version 0. Only increases
* for drastic changes,
* introduction of compatible
* new fields does not count.
*/
uint8_t it_pad;
uint16_t it_len; /* length of the whole
* header in bytes, including
* it_version, it_pad,
* it_len, and data fields.
*/
uint32_t it_present; /* A bitmap telling which
* fields are present. Set bit 31
* (0x80000000) to extend the
* bitmap by another 32 bits.
* Additional extensions are made
* by setting bit 31.
*/
} __packed;
/*
* Name Data type Units
* ---- --------- -----
*
* IEEE80211_RADIOTAP_TSFT uint64_t microseconds
*
* Value in microseconds of the MAC's 64-bit 802.11 Time
* Synchronization Function timer when the first bit of the
* MPDU arrived at the MAC. For received frames, only.
*
* IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap
*
* Tx/Rx frequency in MHz, followed by flags (see below).
*
* IEEE80211_RADIOTAP_FHSS uint16_t see below
*
* For frequency-hopping radios, the hop set (first byte)
* and pattern (second byte).
*
* IEEE80211_RADIOTAP_RATE uint8_t 500kb/s or index
*
* Tx/Rx data rate. If bit 0x80 is set then it represents an
* an MCS index and not an IEEE rate.
*
* IEEE80211_RADIOTAP_DBM_ANTSIGNAL int8_t decibels from
* one milliwatt (dBm)
*
* RF signal power at the antenna, decibel difference from
* one milliwatt.
*
* IEEE80211_RADIOTAP_DBM_ANTNOISE int8_t decibels from
* one milliwatt (dBm)
*
* RF noise power at the antenna, decibel difference from one
* milliwatt.
*
* IEEE80211_RADIOTAP_DB_ANTSIGNAL uint8_t decibel (dB)
*
* RF signal power at the antenna, decibel difference from an
* arbitrary, fixed reference.
*
* IEEE80211_RADIOTAP_DB_ANTNOISE uint8_t decibel (dB)
*
* RF noise power at the antenna, decibel difference from an
* arbitrary, fixed reference point.
*
* IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless
*
* Quality of Barker code lock. Unitless. Monotonically
* nondecreasing with "better" lock strength. Called "Signal
* Quality" in datasheets. (Is there a standard way to measure
* this?)
*
* IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless
*
* Transmit power expressed as unitless distance from max
* power set at factory calibration. 0 is max power.
* Monotonically nondecreasing with lower power levels.
*
* IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB)
*
* Transmit power expressed as decibel distance from max power
* set at factory calibration. 0 is max power. Monotonically
* nondecreasing with lower power levels.
*
* IEEE80211_RADIOTAP_DBM_TX_POWER int8_t decibels from
* one milliwatt (dBm)
*
* Transmit power expressed as dBm (decibels from a 1 milliwatt
* reference). This is the absolute power level measured at
* the antenna port.
*
* IEEE80211_RADIOTAP_FLAGS uint8_t bitmap
*
* Properties of transmitted and received frames. See flags
* defined below.
*
* IEEE80211_RADIOTAP_ANTENNA uint8_t antenna index
*
* Unitless indication of the Rx/Tx antenna for this packet.
* The first antenna is antenna 0.
*
* IEEE80211_RADIOTAP_XCHANNEL uint32_t bitmap
* uint16_t MHz
* uint8_t channel number
* int8_t .5 dBm
*
* Extended channel specification: flags (see below) followed by
* frequency in MHz, the corresponding IEEE channel number, and
* finally the maximum regulatory transmit power cap in .5 dBm
* units. This property supersedes IEEE80211_RADIOTAP_CHANNEL
* and only one of the two should be present.
*/
enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_TSFT = 0,
IEEE80211_RADIOTAP_FLAGS = 1,
IEEE80211_RADIOTAP_RATE = 2,
IEEE80211_RADIOTAP_CHANNEL = 3,
IEEE80211_RADIOTAP_FHSS = 4,
IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5,
IEEE80211_RADIOTAP_DBM_ANTNOISE = 6,
IEEE80211_RADIOTAP_LOCK_QUALITY = 7,
IEEE80211_RADIOTAP_TX_ATTENUATION = 8,
IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9,
IEEE80211_RADIOTAP_DBM_TX_POWER = 10,
IEEE80211_RADIOTAP_ANTENNA = 11,
IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
/* NB: gap for netbsd definitions */
IEEE80211_RADIOTAP_XCHANNEL = 18,
IEEE80211_RADIOTAP_EXT = 31,
};
#ifndef _KERNEL
/* channel attributes */
#define IEEE80211_CHAN_TURBO 0x00000010 /* Turbo channel */
#define IEEE80211_CHAN_CCK 0x00000020 /* CCK channel */
#define IEEE80211_CHAN_OFDM 0x00000040 /* OFDM channel */
#define IEEE80211_CHAN_2GHZ 0x00000080 /* 2 GHz spectrum channel. */
#define IEEE80211_CHAN_5GHZ 0x00000100 /* 5 GHz spectrum channel */
#define IEEE80211_CHAN_PASSIVE 0x00000200 /* Only passive scan allowed */
#define IEEE80211_CHAN_DYN 0x00000400 /* Dynamic CCK-OFDM channel */
#define IEEE80211_CHAN_GFSK 0x00000800 /* GFSK channel (FHSS PHY) */
#define IEEE80211_CHAN_GSM 0x00001000 /* 900 MHz spectrum channel */
#define IEEE80211_CHAN_STURBO 0x00002000 /* 11a static turbo channel only */
#define IEEE80211_CHAN_HALF 0x00004000 /* Half rate channel */
#define IEEE80211_CHAN_QUARTER 0x00008000 /* Quarter rate channel */
#endif /* !_KERNEL */
/* For IEEE80211_RADIOTAP_FLAGS */
#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received
* during CFP
*/
#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received
* with short
* preamble
*/
#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received
* with WEP encryption
*/
#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received
* with fragmentation
*/
#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */
#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between
* 802.11 header and payload
* (to 32-bit boundary)
*/
#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* does not pass FCS check */
#define IEEE80211_RADIOTAP_F_SHORTGI 0x80 /* HT short GI */
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_RADIOTAP_H_ */
@@ -0,0 +1,448 @@
/*-
* Copyright (c) 2005-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11 regdomain support.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
static void
null_getradiocaps(struct ieee80211com *ic, int maxchan,
int *n, struct ieee80211_channel *c)
{
/* just feed back the current channel list */
if (maxchan > ic->ic_nchans)
maxchan = ic->ic_nchans;
memcpy(c, ic->ic_channels, maxchan*sizeof(struct ieee80211_channel));
*n = maxchan;
}
static int
null_setregdomain(struct ieee80211com *ic,
struct ieee80211_regdomain *rd,
int nchans, struct ieee80211_channel chans[])
{
return 0; /* accept anything */
}
void
ieee80211_regdomain_attach(struct ieee80211com *ic)
{
if (ic->ic_regdomain.regdomain == 0 &&
ic->ic_regdomain.country == CTRY_DEFAULT) {
ic->ic_regdomain.country = CTRY_UNITED_STATES; /* XXX */
ic->ic_regdomain.location = ' '; /* both */
ic->ic_regdomain.isocc[0] = 'U'; /* XXX */
ic->ic_regdomain.isocc[1] = 'S'; /* XXX */
/* NB: driver calls ieee80211_init_channels or similar */
}
ic->ic_getradiocaps = null_getradiocaps;
ic->ic_setregdomain = null_setregdomain;
}
void
ieee80211_regdomain_detach(struct ieee80211com *ic)
{
if (ic->ic_countryie != NULL) {
free(ic->ic_countryie, M_80211_NODE_IE);
ic->ic_countryie = NULL;
}
}
void
ieee80211_regdomain_vattach(struct ieee80211vap *vap)
{
}
void
ieee80211_regdomain_vdetach(struct ieee80211vap *vap)
{
}
static void
addchan(struct ieee80211com *ic, int ieee, int flags)
{
struct ieee80211_channel *c;
c = &ic->ic_channels[ic->ic_nchans++];
c->ic_freq = ieee80211_ieee2mhz(ieee, flags);
c->ic_ieee = ieee;
c->ic_flags = flags;
c->ic_extieee = 0;
}
/*
* Setup the channel list for the specified regulatory domain,
* country code, and operating modes. This interface is used
* when a driver does not obtain the channel list from another
* source (such as firmware).
*/
int
ieee80211_init_channels(struct ieee80211com *ic,
const struct ieee80211_regdomain *rd, const uint8_t bands[])
{
int i;
/* XXX just do something for now */
ic->ic_nchans = 0;
if (isset(bands, IEEE80211_MODE_11B) ||
isset(bands, IEEE80211_MODE_11G)) {
int maxchan = 11;
if (rd != NULL && rd->ecm)
maxchan = 14;
for (i = 1; i <= maxchan; i++) {
if (isset(bands, IEEE80211_MODE_11B))
addchan(ic, i, IEEE80211_CHAN_B);
if (isset(bands, IEEE80211_MODE_11G))
addchan(ic, i, IEEE80211_CHAN_G);
}
}
if (isset(bands, IEEE80211_MODE_11A)) {
for (i = 36; i <= 64; i += 4)
addchan(ic, i, IEEE80211_CHAN_A);
for (i = 100; i <= 140; i += 4)
addchan(ic, i, IEEE80211_CHAN_A);
for (i = 149; i <= 161; i += 4)
addchan(ic, i, IEEE80211_CHAN_A);
}
if (rd != NULL)
ic->ic_regdomain = *rd;
return 0;
}
static __inline int
chancompar(const void *a, const void *b)
{
const struct ieee80211_channel *ca = a;
const struct ieee80211_channel *cb = b;
return (ca->ic_freq == cb->ic_freq) ?
(ca->ic_flags & IEEE80211_CHAN_ALL) -
(cb->ic_flags & IEEE80211_CHAN_ALL) :
ca->ic_freq - cb->ic_freq;
}
/*
* Insertion sort.
*/
#define swap(_a, _b, _size) { \
uint8_t *s = _b; \
int i = _size; \
do { \
uint8_t tmp = *_a; \
*_a++ = *s; \
*s++ = tmp; \
} while (--i); \
_a -= _size; \
}
static void
sort_channels(void *a, size_t n, size_t size)
{
uint8_t *aa = a;
uint8_t *ai, *t;
KASSERT(n > 0, ("no channels"));
for (ai = aa+size; --n >= 1; ai += size)
for (t = ai; t > aa; t -= size) {
uint8_t *u = t - size;
if (chancompar(u, t) <= 0)
break;
swap(u, t, size);
}
}
#undef swap
/*
* Order channels w/ the same frequency so that
* b < g < htg and a < hta. This is used to optimize
* channel table lookups and some user applications
* may also depend on it (though they should not).
*/
void
ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans)
{
if (nchans > 0)
sort_channels(chans, nchans, sizeof(struct ieee80211_channel));
}
/*
* Allocate and construct a Country Information IE.
*/
struct ieee80211_appie *
ieee80211_alloc_countryie(struct ieee80211com *ic)
{
#define CHAN_UNINTERESTING \
(IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \
IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)
/* XXX what about auto? */
/* flag set of channels to be excluded (band added below) */
static const int skipflags[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = CHAN_UNINTERESTING,
[IEEE80211_MODE_11A] = CHAN_UNINTERESTING,
[IEEE80211_MODE_11B] = CHAN_UNINTERESTING,
[IEEE80211_MODE_11G] = CHAN_UNINTERESTING,
[IEEE80211_MODE_FH] = CHAN_UNINTERESTING
| IEEE80211_CHAN_OFDM
| IEEE80211_CHAN_CCK
| IEEE80211_CHAN_DYN,
[IEEE80211_MODE_TURBO_A] = CHAN_UNINTERESTING,
[IEEE80211_MODE_TURBO_G] = CHAN_UNINTERESTING,
[IEEE80211_MODE_STURBO_A] = CHAN_UNINTERESTING,
[IEEE80211_MODE_HALF] = IEEE80211_CHAN_TURBO
| IEEE80211_CHAN_STURBO,
[IEEE80211_MODE_QUARTER] = IEEE80211_CHAN_TURBO
| IEEE80211_CHAN_STURBO,
[IEEE80211_MODE_11NA] = CHAN_UNINTERESTING,
[IEEE80211_MODE_11NG] = CHAN_UNINTERESTING,
};
const struct ieee80211_regdomain *rd = &ic->ic_regdomain;
uint8_t nextchan, chans[IEEE80211_CHAN_BYTES], *frm;
struct ieee80211_appie *aie;
struct ieee80211_country_ie *ie;
int i, skip, nruns;
aie = malloc(IEEE80211_COUNTRY_MAX_SIZE, M_80211_NODE_IE,
M_NOWAIT | M_ZERO);
if (aie == NULL) {
if_printf(ic->ic_ifp,
"%s: unable to allocate memory for country ie\n", __func__);
/* XXX stat */
return NULL;
}
ie = (struct ieee80211_country_ie *) aie->ie_data;
ie->ie = IEEE80211_ELEMID_COUNTRY;
if (rd->isocc[0] == '\0') {
if_printf(ic->ic_ifp, "no ISO country string for cc %d; "
"using blanks\n", rd->country);
ie->cc[0] = ie->cc[1] = ' ';
} else {
ie->cc[0] = rd->isocc[0];
ie->cc[1] = rd->isocc[1];
}
/*
* Indoor/Outdoor portion of country string:
* 'I' indoor only
* 'O' outdoor only
* ' ' all enviroments
*/
ie->cc[2] = (rd->location == 'I' ? 'I' :
rd->location == 'O' ? 'O' : ' ');
/*
* Run-length encoded channel+max tx power info.
*/
frm = (uint8_t *)&ie->band[0];
nextchan = 0; /* NB: impossible channel # */
nruns = 0;
memset(chans, 0, sizeof(chans));
skip = skipflags[ieee80211_chan2mode(ic->ic_bsschan)];
if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan))
skip |= IEEE80211_CHAN_2GHZ;
else if (IEEE80211_IS_CHAN_2GHZ(ic->ic_bsschan))
skip |= IEEE80211_CHAN_5GHZ;
for (i = 0; i < ic->ic_nchans; i++) {
const struct ieee80211_channel *c = &ic->ic_channels[i];
if (isset(chans, c->ic_ieee)) /* suppress dup's */
continue;
if (c->ic_flags & skip) /* skip band, etc. */
continue;
setbit(chans, c->ic_ieee);
if (c->ic_ieee != nextchan ||
c->ic_maxregpower != frm[-1]) { /* new run */
if (nruns == IEEE80211_COUNTRY_MAX_BANDS) {
if_printf(ic->ic_ifp, "%s: country ie too big, "
"runs > max %d, truncating\n",
__func__, IEEE80211_COUNTRY_MAX_BANDS);
/* XXX stat? fail? */
break;
}
frm[0] = c->ic_ieee; /* starting channel # */
frm[1] = 1; /* # channels in run */
frm[2] = c->ic_maxregpower; /* tx power cap */
frm += 3;
nextchan = c->ic_ieee + 1; /* overflow? */
nruns++;
} else { /* extend run */
frm[-2]++;
nextchan++;
}
}
ie->len = frm - ie->cc;
if (ie->len & 1) { /* Zero pad to multiple of 2 */
ie->len++;
*frm++ = 0;
}
aie->ie_len = frm - aie->ie_data;
return aie;
#undef CHAN_UNINTERESTING
}
static int
allvapsdown(struct ieee80211com *ic)
{
struct ieee80211vap *vap;
IEEE80211_LOCK_ASSERT(ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
if (vap->iv_state != IEEE80211_S_INIT)
return 0;
return 1;
}
int
ieee80211_setregdomain(struct ieee80211vap *vap,
struct ieee80211_regdomain_req *reg)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c;
int desfreq = 0, desflags = 0; /* XXX silence gcc complaint */
int error, i;
if (reg->rd.location != 'I' && reg->rd.location != 'O' &&
reg->rd.location != ' ') {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
"%s: invalid location 0x%x\n", __func__, reg->rd.location);
return EINVAL;
}
if (reg->rd.isocc[0] == '\0' || reg->rd.isocc[1] == '\0') {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
"%s: invalid iso cc 0x%x:0x%x\n", __func__,
reg->rd.isocc[0], reg->rd.isocc[1]);
return EINVAL;
}
if (reg->chaninfo.ic_nchans > IEEE80211_CHAN_MAX) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
"%s: too many channels %u, max %u\n", __func__,
reg->chaninfo.ic_nchans, IEEE80211_CHAN_MAX);
return EINVAL;
}
/*
* Calculate freq<->IEEE mapping and default max tx power
* for channels not setup. The driver can override these
* setting to reflect device properties/requirements.
*/
for (i = 0; i < reg->chaninfo.ic_nchans; i++) {
c = &reg->chaninfo.ic_chans[i];
if (c->ic_freq == 0 || c->ic_flags == 0) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
"%s: invalid channel spec at [%u]\n", __func__, i);
return EINVAL;
}
if (c->ic_maxregpower == 0) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
"%s: invalid channel spec, zero maxregpower, "
"freq %u flags 0x%x\n", __func__,
c->ic_freq, c->ic_flags);
return EINVAL;
}
if (c->ic_ieee == 0)
c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags);
if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0)
c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq +
(IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20),
c->ic_flags);
if (c->ic_maxpower == 0)
c->ic_maxpower = 2*c->ic_maxregpower;
}
IEEE80211_LOCK(ic);
/* XXX bandaid; a running vap will likely crash */
if (!allvapsdown(ic)) {
IEEE80211_UNLOCK(ic);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
"%s: reject: vaps are running\n", __func__);
return EBUSY;
}
error = ic->ic_setregdomain(ic, &reg->rd,
reg->chaninfo.ic_nchans, reg->chaninfo.ic_chans);
if (error != 0) {
IEEE80211_UNLOCK(ic);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
"%s: driver rejected request, error %u\n", __func__, error);
return error;
}
/*
* Commit: copy in new channel table and reset media state.
* On return the state machines will be clocked so all vaps
* will reset their state.
*
* XXX ic_bsschan is marked undefined, must have vap's in
* INIT state or we blow up forcing stations off
*/
/*
* Save any desired channel for restore below. Note this
* needs to be done for all vaps but for now we only do
* the one where the ioctl is issued.
*/
if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
desfreq = vap->iv_des_chan->ic_freq;
desflags = vap->iv_des_chan->ic_flags;
}
/* regdomain parameters */
memcpy(&ic->ic_regdomain, &reg->rd, sizeof(reg->rd));
/* channel table */
memcpy(ic->ic_channels, reg->chaninfo.ic_chans,
reg->chaninfo.ic_nchans * sizeof(struct ieee80211_channel));
ic->ic_nchans = reg->chaninfo.ic_nchans;
memset(&ic->ic_channels[ic->ic_nchans], 0,
(IEEE80211_CHAN_MAX - ic->ic_nchans) *
sizeof(struct ieee80211_channel));
ieee80211_media_init(ic);
/*
* Invalidate channel-related state.
*/
if (ic->ic_countryie != NULL) {
free(ic->ic_countryie, M_80211_NODE_IE);
ic->ic_countryie = NULL;
}
ieee80211_scan_flush(vap);
ieee80211_dfs_reset(ic);
if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
c = ieee80211_find_channel(ic, desfreq, desflags);
/* NB: may be NULL if not present in new channel list */
vap->iv_des_chan = (c != NULL) ? c : IEEE80211_CHAN_ANYC;
}
IEEE80211_UNLOCK(ic);
return 0;
}
@@ -0,0 +1,282 @@
/*-
* Copyright (c) 2005-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_REGDOMAIN_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_REGDOMAIN_H_
/*
* 802.11 regulatory domain definitions.
*/
/*
* ISO 3166 Country/Region Codes
* http://ftp.ics.uci.edu/pub/ietf/http/related/iso3166.txt
*/
enum ISOCountryCode {
CTRY_AFGHANISTAN = 4,
CTRY_ALBANIA = 8, /* Albania */
CTRY_ALGERIA = 12, /* Algeria */
CTRY_AMERICAN_SAMOA = 16,
CTRY_ANDORRA = 20,
CTRY_ANGOLA = 24,
CTRY_ANGUILLA = 660,
CTRY_ANTARTICA = 10,
CTRY_ANTIGUA = 28, /* Antigua and Barbuda */
CTRY_ARGENTINA = 32, /* Argentina */
CTRY_ARMENIA = 51, /* Armenia */
CTRY_ARUBA = 533, /* Aruba */
CTRY_AUSTRALIA = 36, /* Australia */
CTRY_AUSTRIA = 40, /* Austria */
CTRY_AZERBAIJAN = 31, /* Azerbaijan */
CTRY_BAHAMAS = 44, /* Bahamas */
CTRY_BAHRAIN = 48, /* Bahrain */
CTRY_BANGLADESH = 50, /* Bangladesh */
CTRY_BARBADOS = 52,
CTRY_BELARUS = 112, /* Belarus */
CTRY_BELGIUM = 56, /* Belgium */
CTRY_BELIZE = 84,
CTRY_BENIN = 204,
CTRY_BERMUDA = 60,
CTRY_BHUTAN = 64,
CTRY_BOLIVIA = 68, /* Bolivia */
CTRY_BOSNIA_AND_HERZEGOWINA = 70,
CTRY_BOTSWANA = 72,
CTRY_BOUVET_ISLAND = 74,
CTRY_BRAZIL = 76, /* Brazil */
CTRY_BRITISH_INDIAN_OCEAN_TERRITORY = 86,
CTRY_BRUNEI_DARUSSALAM = 96, /* Brunei Darussalam */
CTRY_BULGARIA = 100, /* Bulgaria */
CTRY_BURKINA_FASO = 854,
CTRY_BURUNDI = 108,
CTRY_CAMBODIA = 116,
CTRY_CAMEROON = 120,
CTRY_CANADA = 124, /* Canada */
CTRY_CAPE_VERDE = 132,
CTRY_CAYMAN_ISLANDS = 136,
CTRY_CENTRAL_AFRICAN_REPUBLIC = 140,
CTRY_CHAD = 148,
CTRY_CHILE = 152, /* Chile */
CTRY_CHINA = 156, /* People's Republic of China */
CTRY_CHRISTMAS_ISLAND = 162,
CTRY_COCOS_ISLANDS = 166,
CTRY_COLOMBIA = 170, /* Colombia */
CTRY_COMOROS = 174,
CTRY_CONGO = 178,
CTRY_COOK_ISLANDS = 184,
CTRY_COSTA_RICA = 188, /* Costa Rica */
CTRY_COTE_DIVOIRE = 384,
CTRY_CROATIA = 191, /* Croatia (local name: Hrvatska) */
CTRY_CYPRUS = 196, /* Cyprus */
CTRY_CZECH = 203, /* Czech Republic */
CTRY_DENMARK = 208, /* Denmark */
CTRY_DJIBOUTI = 262,
CTRY_DOMINICA = 212,
CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */
CTRY_EAST_TIMOR = 626,
CTRY_ECUADOR = 218, /* Ecuador */
CTRY_EGYPT = 818, /* Egypt */
CTRY_EL_SALVADOR = 222, /* El Salvador */
CTRY_EQUATORIAL_GUINEA = 226,
CTRY_ERITREA = 232,
CTRY_ESTONIA = 233, /* Estonia */
CTRY_ETHIOPIA = 210,
CTRY_FALKLAND_ISLANDS = 238, /* (Malvinas) */
CTRY_FAEROE_ISLANDS = 234, /* Faeroe Islands */
CTRY_FIJI = 242,
CTRY_FINLAND = 246, /* Finland */
CTRY_FRANCE = 250, /* France */
CTRY_FRANCE2 = 255, /* France (Metropolitan) */
CTRY_FRENCH_GUIANA = 254,
CTRY_FRENCH_POLYNESIA = 258,
CTRY_FRENCH_SOUTHERN_TERRITORIES = 260,
CTRY_GABON = 266,
CTRY_GAMBIA = 270,
CTRY_GEORGIA = 268, /* Georgia */
CTRY_GERMANY = 276, /* Germany */
CTRY_GHANA = 288,
CTRY_GIBRALTAR = 292,
CTRY_GREECE = 300, /* Greece */
CTRY_GREENLAND = 304,
CTRY_GRENADA = 308,
CTRY_GUADELOUPE = 312,
CTRY_GUAM = 316,
CTRY_GUATEMALA = 320, /* Guatemala */
CTRY_GUINEA = 324,
CTRY_GUINEA_BISSAU = 624,
CTRY_GUYANA = 328,
/* XXX correct remainder */
CTRY_HAITI = 332,
CTRY_HONDURAS = 340, /* Honduras */
CTRY_HONG_KONG = 344, /* Hong Kong S.A.R., P.R.C. */
CTRY_HUNGARY = 348, /* Hungary */
CTRY_ICELAND = 352, /* Iceland */
CTRY_INDIA = 356, /* India */
CTRY_INDONESIA = 360, /* Indonesia */
CTRY_IRAN = 364, /* Iran */
CTRY_IRAQ = 368, /* Iraq */
CTRY_IRELAND = 372, /* Ireland */
CTRY_ISRAEL = 376, /* Israel */
CTRY_ITALY = 380, /* Italy */
CTRY_JAMAICA = 388, /* Jamaica */
CTRY_JAPAN = 392, /* Japan */
CTRY_JORDAN = 400, /* Jordan */
CTRY_KAZAKHSTAN = 398, /* Kazakhstan */
CTRY_KENYA = 404, /* Kenya */
CTRY_KOREA_NORTH = 408, /* North Korea */
CTRY_KOREA_ROC = 410, /* South Korea */
CTRY_KOREA_ROC2 = 411, /* South Korea */
CTRY_KUWAIT = 414, /* Kuwait */
CTRY_LATVIA = 428, /* Latvia */
CTRY_LEBANON = 422, /* Lebanon */
CTRY_LIBYA = 434, /* Libya */
CTRY_LIECHTENSTEIN = 438, /* Liechtenstein */
CTRY_LITHUANIA = 440, /* Lithuania */
CTRY_LUXEMBOURG = 442, /* Luxembourg */
CTRY_MACAU = 446, /* Macau */
CTRY_MACEDONIA = 807, /* the Former Yugoslav Republic of Macedonia */
CTRY_MALAYSIA = 458, /* Malaysia */
CTRY_MALTA = 470, /* Malta */
CTRY_MEXICO = 484, /* Mexico */
CTRY_MONACO = 492, /* Principality of Monaco */
CTRY_MOROCCO = 504, /* Morocco */
CTRY_NEPAL = 524, /* Nepal */
CTRY_NETHERLANDS = 528, /* Netherlands */
CTRY_NEW_ZEALAND = 554, /* New Zealand */
CTRY_NICARAGUA = 558, /* Nicaragua */
CTRY_NORWAY = 578, /* Norway */
CTRY_OMAN = 512, /* Oman */
CTRY_PAKISTAN = 586, /* Islamic Republic of Pakistan */
CTRY_PANAMA = 591, /* Panama */
CTRY_PARAGUAY = 600, /* Paraguay */
CTRY_PERU = 604, /* Peru */
CTRY_PHILIPPINES = 608, /* Republic of the Philippines */
CTRY_POLAND = 616, /* Poland */
CTRY_PORTUGAL = 620, /* Portugal */
CTRY_PUERTO_RICO = 630, /* Puerto Rico */
CTRY_QATAR = 634, /* Qatar */
CTRY_ROMANIA = 642, /* Romania */
CTRY_RUSSIA = 643, /* Russia */
CTRY_SAUDI_ARABIA = 682, /* Saudi Arabia */
CTRY_SINGAPORE = 702, /* Singapore */
CTRY_SLOVAKIA = 703, /* Slovak Republic */
CTRY_SLOVENIA = 705, /* Slovenia */
CTRY_SOUTH_AFRICA = 710, /* South Africa */
CTRY_SPAIN = 724, /* Spain */
CTRY_SRILANKA = 144, /* Sri Lanka */
CTRY_SWEDEN = 752, /* Sweden */
CTRY_SWITZERLAND = 756, /* Switzerland */
CTRY_SYRIA = 760, /* Syria */
CTRY_TAIWAN = 158, /* Taiwan */
CTRY_THAILAND = 764, /* Thailand */
CTRY_TRINIDAD_Y_TOBAGO = 780, /* Trinidad y Tobago */
CTRY_TUNISIA = 788, /* Tunisia */
CTRY_TURKEY = 792, /* Turkey */
CTRY_UAE = 784, /* U.A.E. */
CTRY_UKRAINE = 804, /* Ukraine */
CTRY_UNITED_KINGDOM = 826, /* United Kingdom */
CTRY_UNITED_STATES = 840, /* United States */
CTRY_URUGUAY = 858, /* Uruguay */
CTRY_UZBEKISTAN = 860, /* Uzbekistan */
CTRY_VENEZUELA = 862, /* Venezuela */
CTRY_VIET_NAM = 704, /* Viet Nam */
CTRY_YEMEN = 887, /* Yemen */
CTRY_ZIMBABWE = 716, /* Zimbabwe */
/* NB: from here down not listed in 3166; they come from Atheros */
CTRY_DEBUG = 0x1ff, /* debug */
CTRY_DEFAULT = 0, /* default */
CTRY_UNITED_STATES_FCC49 = 842, /* United States (Public Safety)*/
CTRY_KOREA_ROC3 = 412, /* South Korea */
CTRY_JAPAN1 = 393, /* Japan (JP1) */
CTRY_JAPAN2 = 394, /* Japan (JP0) */
CTRY_JAPAN3 = 395, /* Japan (JP1-1) */
CTRY_JAPAN4 = 396, /* Japan (JE1) */
CTRY_JAPAN5 = 397, /* Japan (JE2) */
CTRY_JAPAN6 = 399, /* Japan (JP6) */
CTRY_JAPAN7 = 4007, /* Japan (J7) */
CTRY_JAPAN8 = 4008, /* Japan (J8) */
CTRY_JAPAN9 = 4009, /* Japan (J9) */
CTRY_JAPAN10 = 4010, /* Japan (J10) */
CTRY_JAPAN11 = 4011, /* Japan (J11) */
CTRY_JAPAN12 = 4012, /* Japan (J12) */
CTRY_JAPAN13 = 4013, /* Japan (J13) */
CTRY_JAPAN14 = 4014, /* Japan (J14) */
CTRY_JAPAN15 = 4015, /* Japan (J15) */
CTRY_JAPAN16 = 4016, /* Japan (J16) */
CTRY_JAPAN17 = 4017, /* Japan (J17) */
CTRY_JAPAN18 = 4018, /* Japan (J18) */
CTRY_JAPAN19 = 4019, /* Japan (J19) */
CTRY_JAPAN20 = 4020, /* Japan (J20) */
CTRY_JAPAN21 = 4021, /* Japan (J21) */
CTRY_JAPAN22 = 4022, /* Japan (J22) */
CTRY_JAPAN23 = 4023, /* Japan (J23) */
CTRY_JAPAN24 = 4024, /* Japan (J24) */
};
enum RegdomainCode {
SKU_FCC = 0x10, /* FCC, aka United States */
SKU_CA = 0x20, /* North America, aka Canada */
SKU_ETSI = 0x30, /* Europe */
SKU_ETSI2 = 0x32, /* Europe w/o HT40 in 5GHz */
SKU_ETSI3 = 0x33, /* Europe - channel 36 */
SKU_FCC3 = 0x3a, /* FCC w/5470 band, 11h, DFS */
SKU_JAPAN = 0x40,
SKU_KOREA = 0x45,
SKU_APAC = 0x50, /* Asia Pacific */
SKU_APAC2 = 0x51, /* Asia Pacific w/ DFS on mid-band */
SKU_APAC3 = 0x5d, /* Asia Pacific w/o ISM band */
SKU_ROW = 0x81, /* China/Taiwan/Rest of World */
SKU_NONE = 0xf0, /* "Region Free" */
SKU_DEBUG = 0x1ff,
/* NB: from here down private */
SKU_SR9 = 0x0298, /* Ubiquiti SR9 (900MHz/GSM) */
SKU_XR9 = 0x0299, /* Ubiquiti XR9 (900MHz/GSM) */
SKU_GZ901 = 0x029a, /* Zcomax GZ-901 (900MHz/GSM) */
};
#if defined(__KERNEL__) || defined(_KERNEL)
struct ieee80211com;
void ieee80211_regdomain_attach(struct ieee80211com *);
void ieee80211_regdomain_detach(struct ieee80211com *);
struct ieee80211vap;
void ieee80211_regdomain_vattach(struct ieee80211vap *);
void ieee80211_regdomain_vdetach(struct ieee80211vap *);
struct ieee80211_regdomain;
int ieee80211_init_channels(struct ieee80211com *,
const struct ieee80211_regdomain *, const uint8_t bands[]);
struct ieee80211_channel;
void ieee80211_sort_channels(struct ieee80211_channel *chans, int nchans);
struct ieee80211_appie;
struct ieee80211_appie *ieee80211_alloc_countryie(struct ieee80211com *);
struct ieee80211_regdomain_req;
int ieee80211_setregdomain(struct ieee80211vap *,
struct ieee80211_regdomain_req *);
#endif /* defined(__KERNEL__) || defined(_KERNEL) */
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_REGDOMAIN_H_ */
@@ -0,0 +1,273 @@
/* $FreeBSD$ */
/* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */
/*-
* Copyright (c) 2003, 2004 David Young. 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 David Young may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY David Young ``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 David
* Young 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 "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_rssadapt.h>
struct rssadapt_expavgctl {
/* RSS threshold decay. */
u_int rc_decay_denom;
u_int rc_decay_old;
/* RSS threshold update. */
u_int rc_thresh_denom;
u_int rc_thresh_old;
/* RSS average update. */
u_int rc_avgrssi_denom;
u_int rc_avgrssi_old;
};
static struct rssadapt_expavgctl master_expavgctl = {
rc_decay_denom : 16,
rc_decay_old : 15,
rc_thresh_denom : 8,
rc_thresh_old : 4,
rc_avgrssi_denom : 8,
rc_avgrssi_old : 4
};
#ifdef interpolate
#undef interpolate
#endif
#define interpolate(parm, old, new) ((parm##_old * (old) + \
(parm##_denom - parm##_old) * (new)) / \
parm##_denom)
static void rssadapt_sysctlattach(struct ieee80211_rssadapt *rs,
struct sysctl_ctx_list *ctx, struct sysctl_oid *tree);
/* number of references from net80211 layer */
static int nrefs = 0;
void
ieee80211_rssadapt_setinterval(struct ieee80211_rssadapt *rs, int msecs)
{
int t;
if (msecs < 100)
msecs = 100;
t = msecs_to_ticks(msecs);
rs->interval = (t < 1) ? 1 : t;
}
void
ieee80211_rssadapt_init(struct ieee80211_rssadapt *rs, struct ieee80211vap *vap, int interval)
{
rs->vap = vap;
ieee80211_rssadapt_setinterval(rs, interval);
rssadapt_sysctlattach(rs, vap->iv_sysctl, vap->iv_oid);
}
void
ieee80211_rssadapt_cleanup(struct ieee80211_rssadapt *rs)
{
}
static void
rssadapt_updatestats(struct ieee80211_rssadapt_node *ra)
{
long interval;
ra->ra_pktrate = (ra->ra_pktrate + 10*(ra->ra_nfail + ra->ra_nok))/2;
ra->ra_nfail = ra->ra_nok = 0;
/*
* A node is eligible for its rate to be raised every 1/10 to 10
* seconds, more eligible in proportion to recent packet rates.
*/
interval = MAX(10*1000, 10*1000 / MAX(1, 10 * ra->ra_pktrate));
ra->ra_raise_interval = msecs_to_ticks(interval);
}
void
ieee80211_rssadapt_node_init(struct ieee80211_rssadapt *rsa,
struct ieee80211_rssadapt_node *ra, struct ieee80211_node *ni)
{
const struct ieee80211_rateset *rs = &ni->ni_rates;
ra->ra_rs = rsa;
ra->ra_rates = *rs;
rssadapt_updatestats(ra);
/* pick initial rate */
for (ra->ra_rix = rs->rs_nrates - 1;
ra->ra_rix > 0 && (rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL) > 72;
ra->ra_rix--)
;
ni->ni_txrate = rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL;
ra->ra_ticks = ticks;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
"RSSADAPT initial rate %d", ni->ni_txrate);
}
static __inline int
bucket(int pktlen)
{
int i, top, thridx;
for (i = 0, top = IEEE80211_RSSADAPT_BKT0;
i < IEEE80211_RSSADAPT_BKTS;
i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) {
thridx = i;
if (pktlen <= top)
break;
}
return thridx;
}
int
ieee80211_rssadapt_choose(struct ieee80211_node *ni,
struct ieee80211_rssadapt_node *ra, u_int pktlen)
{
const struct ieee80211_rateset *rs = &ra->ra_rates;
uint16_t (*thrs)[IEEE80211_RATE_SIZE];
int rix, rssi;
if ((ticks - ra->ra_ticks) > ra->ra_rs->interval) {
rssadapt_updatestats(ra);
ra->ra_ticks = ticks;
}
thrs = &ra->ra_rate_thresh[bucket(pktlen)];
/* XXX this is average rssi, should be using last value */
rssi = ni->ni_ic->ic_node_getrssi(ni);
for (rix = rs->rs_nrates-1; rix >= 0; rix--)
if ((*thrs)[rix] < (rssi << 8))
break;
if (rix != ra->ra_rix) {
/* update public rate */
ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
ra->ra_rix = rix;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
"RSSADAPT new rate %d (pktlen %d rssi %d)",
ni->ni_txrate, pktlen, rssi);
}
return rix;
}
/*
* Adapt the data rate to suit the conditions. When a transmitted
* packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions,
* raise the RSS threshold for transmitting packets of similar length at
* the same data rate.
*/
void
ieee80211_rssadapt_lower_rate(struct ieee80211_rssadapt_node *ra,
int pktlen, int rssi)
{
uint16_t last_thr;
uint16_t (*thrs)[IEEE80211_RATE_SIZE];
u_int rix;
thrs = &ra->ra_rate_thresh[bucket(pktlen)];
rix = ra->ra_rix;
last_thr = (*thrs)[rix];
(*thrs)[rix] = interpolate(master_expavgctl.rc_thresh,
last_thr, (rssi << 8));
IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
"RSSADAPT lower threshold for rate %d (last_thr %d new thr %d rssi %d)\n",
ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
last_thr, (*thrs)[rix], rssi);
}
void
ieee80211_rssadapt_raise_rate(struct ieee80211_rssadapt_node *ra,
int pktlen, int rssi)
{
uint16_t (*thrs)[IEEE80211_RATE_SIZE];
uint16_t newthr, oldthr;
int rix;
thrs = &ra->ra_rate_thresh[bucket(pktlen)];
rix = ra->ra_rix;
if ((*thrs)[rix + 1] > (*thrs)[rix]) {
oldthr = (*thrs)[rix + 1];
if ((*thrs)[rix] == 0)
newthr = (rssi << 8);
else
newthr = (*thrs)[rix];
(*thrs)[rix + 1] = interpolate(master_expavgctl.rc_decay,
oldthr, newthr);
IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
"RSSADAPT raise threshold for rate %d (oldthr %d newthr %d rssi %d)\n",
ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
oldthr, newthr, rssi);
ra->ra_last_raise = ticks;
}
}
static int
rssadapt_sysctl_interval(SYSCTL_HANDLER_ARGS)
{
struct ieee80211_rssadapt *rs = arg1;
int msecs = ticks_to_msecs(rs->interval);
int error;
error = sysctl_handle_int(oidp, &msecs, 0, req);
if (error || !req->newptr)
return error;
ieee80211_rssadapt_setinterval(rs, msecs);
return 0;
}
static void
rssadapt_sysctlattach(struct ieee80211_rssadapt *rs,
struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
{
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"rssadapt_rate_interval", CTLTYPE_INT | CTLFLAG_RW, rs,
0, rssadapt_sysctl_interval, "I", "rssadapt operation interval (ms)");
}
/*
* Module glue.
*/
IEEE80211_RATE_MODULE(rssadapt, 1);
@@ -0,0 +1,101 @@
/* $FreeBSD$ */
/* $NetBSD: ieee80211_rssadapt.h,v 1.4 2005/02/26 22:45:09 perry Exp $ */
/*-
* Copyright (c) 2003, 2004 David Young. 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 David Young may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY David Young ``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 David
* Young 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.
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_RSSADAPT_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_RSSADAPT_H_
/* Data-rate adaptation loosely based on "Link Adaptation Strategy
* for IEEE 802.11 WLAN via Received Signal Strength Measurement"
* by Javier del Prado Pavon and Sunghyun Choi.
*/
/* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */
#define IEEE80211_RSSADAPT_BKTS 3
#define IEEE80211_RSSADAPT_BKT0 128
#define IEEE80211_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */
struct ieee80211_rssadapt {
struct ieee80211vap *vap;
int interval; /* update interval (ticks) */
};
struct ieee80211_rssadapt_node {
struct ieee80211_rssadapt *ra_rs; /* backpointer */
struct ieee80211_rateset ra_rates; /* negotiated rates */
int ra_rix; /* current rate index */
int ra_ticks; /* time of last update */
int ra_last_raise; /* time of last rate raise */
int ra_raise_interval; /* rate raise time threshold */
/* Tx failures in this update interval */
uint32_t ra_nfail;
/* Tx successes in this update interval */
uint32_t ra_nok;
/* exponential average packets/second */
uint32_t ra_pktrate;
/* RSSI threshold for each Tx rate */
uint16_t ra_rate_thresh[IEEE80211_RSSADAPT_BKTS]
[IEEE80211_RATE_SIZE];
};
void ieee80211_rssadapt_init(struct ieee80211_rssadapt *,
struct ieee80211vap *, int);
void ieee80211_rssadapt_cleanup(struct ieee80211_rssadapt *);
void ieee80211_rssadapt_setinterval(struct ieee80211_rssadapt *, int);
void ieee80211_rssadapt_node_init(struct ieee80211_rssadapt *,
struct ieee80211_rssadapt_node *, struct ieee80211_node *);
int ieee80211_rssadapt_choose(struct ieee80211_node *,
struct ieee80211_rssadapt_node *, u_int);
/* NB: these are public only for the inline below */
void ieee80211_rssadapt_raise_rate(struct ieee80211_rssadapt_node *,
int pktlen, int rssi);
void ieee80211_rssadapt_lower_rate(struct ieee80211_rssadapt_node *,
int pktlen, int rssi);
#define IEEE80211_RSSADAPT_SUCCESS 1
#define IEEE80211_RSSADAPT_FAILURE 0
static __inline void
ieee80211_rssadapt_tx_complete(struct ieee80211_rssadapt_node *ra,
int success, int pktlen, int rssi)
{
if (success) {
ra->ra_nok++;
if ((ra->ra_rix + 1) < ra->ra_rates.rs_nrates &&
(ticks - ra->ra_last_raise) >= ra->ra_raise_interval)
ieee80211_rssadapt_raise_rate(ra, pktlen, rssi);
} else {
ra->ra_nfail++;
ieee80211_rssadapt_lower_rate(ra, pktlen, rssi);
}
}
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_RSSADAPT_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,301 @@
/*-
* Copyright (c) 2005-2009 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_SCAN_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_SCAN_H_
/*
* 802.11 scanning support.
*
* Scanning is the procedure by which a station locates a bss to join
* (infrastructure/ibss mode), or a channel to use (when operating as
* an ap or ibss master). Scans are either "active" or "passive". An
* active scan causes one or more probe request frames to be sent on
* visiting each channel. A passive request causes each channel in the
* scan set to be visited but no frames to be transmitted; the station
* only listens for traffic. Note that active scanning may still need
* to listen for traffic before sending probe request frames depending
* on regulatory constraints; the 802.11 layer handles this by generating
* a callback when scanning on a ``passive channel'' when the
* IEEE80211_FEXT_PROBECHAN flag is set.
*
* A scan operation involves constructing a set of channels to inspect
* (the scan set), visiting each channel and collecting information
* (e.g. what bss are present), and then analyzing the results to make
* decisions like which bss to join. This process needs to be as fast
* as possible so we do things like intelligently construct scan sets
* and dwell on a channel only as long as necessary. The scan code also
* maintains a cache of recent scan results and uses it to bypass scanning
* whenever possible. The scan cache is also used to enable roaming
* between access points when operating in infrastructure mode.
*
* Scanning is handled with pluggable modules that implement "policy"
* per-operating mode. The core scanning support provides an
* instrastructure to support these modules and exports a common api
* to the rest of the 802.11 layer. Policy modules decide what
* channels to visit, what state to record to make decisions (e.g. ap
* mode scanning for auto channel selection keeps significantly less
* state than sta mode scanning for an ap to associate to), and selects
* the final station/channel to return as the result of a scan.
*
* Scanning is done synchronously when initially bringing a vap to an
* operational state and optionally in the background to maintain the
* scan cache for doing roaming and rogue ap monitoring. Scanning is
* not tied to the 802.11 state machine that governs vaps though there
* is linkage to the IEEE80211_SCAN state. Only one vap at a time may
* be scanning; this scheduling policy is handled in ieee80211_new_state
* and is invisible to the scanning code.
*/
#define IEEE80211_SCAN_MAX IEEE80211_CHAN_MAX
struct ieee80211_scanner; /* scan policy state */
struct ieee80211_scan_ssid {
int len; /* length in bytes */
uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
};
#define IEEE80211_SCAN_MAX_SSID 1 /* max # ssid's to probe */
/*
* Scan state visible to the 802.11 layer. Scan parameters and
* results are stored in this data structure. The ieee80211_scan_state
* structure is extended with space that is maintained private to
* the core scanning support. We allocate one instance and link it
* to the ieee80211com structure; then share it between all associated
* vaps. We could allocate multiple of these, e.g. to hold multiple
* scan results, but this is sufficient for current needs.
*/
struct ieee80211_scan_state {
struct ieee80211vap *ss_vap;
struct ieee80211com *ss_ic;
const struct ieee80211_scanner *ss_ops; /* policy hookup, see below */
void *ss_priv; /* scanner private state */
uint16_t ss_flags;
#define IEEE80211_SCAN_NOPICK 0x0001 /* scan only, no selection */
#define IEEE80211_SCAN_ACTIVE 0x0002 /* active scan (probe req) */
#define IEEE80211_SCAN_PICK1ST 0x0004 /* ``hey sailor'' mode */
#define IEEE80211_SCAN_BGSCAN 0x0008 /* bg scan, exit ps at end */
#define IEEE80211_SCAN_ONCE 0x0010 /* do one complete pass */
#define IEEE80211_SCAN_NOBCAST 0x0020 /* no broadcast probe req */
#define IEEE80211_SCAN_NOJOIN 0x0040 /* no auto-sequencing */
#define IEEE80211_SCAN_GOTPICK 0x1000 /* got candidate, can stop */
uint8_t ss_nssid; /* # ssid's to probe/match */
struct ieee80211_scan_ssid ss_ssid[IEEE80211_SCAN_MAX_SSID];
/* ssid's to probe/match */
/* ordered channel set */
struct ieee80211_channel *ss_chans[IEEE80211_SCAN_MAX];
uint16_t ss_next; /* ix of next chan to scan */
uint16_t ss_last; /* ix+1 of last chan to scan */
unsigned long ss_mindwell; /* min dwell on channel */
unsigned long ss_maxdwell; /* max dwell on channel */
};
/*
* The upper 16 bits of the flags word is used to communicate
* information to the scanning code that is NOT recorded in
* ss_flags. It might be better to split this stuff out into
* a separate variable to avoid confusion.
*/
#define IEEE80211_SCAN_FLUSH 0x00010000 /* flush candidate table */
#define IEEE80211_SCAN_NOSSID 0x80000000 /* don't update ssid list */
struct ieee80211com;
void ieee80211_scan_sta_init(void);
void ieee80211_scan_attach(struct ieee80211com *);
void ieee80211_scan_detach(struct ieee80211com *);
void ieee80211_scan_vattach(struct ieee80211vap *);
void ieee80211_scan_vdetach(struct ieee80211vap *);
void ieee80211_scan_dump_channels(const struct ieee80211_scan_state *);
#define IEEE80211_SCAN_FOREVER 0x7fffffff
int ieee80211_start_scan(struct ieee80211vap *, int flags,
u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[]);
int ieee80211_check_scan(struct ieee80211vap *, int flags,
u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[]);
int ieee80211_check_scan_current(struct ieee80211vap *);
int ieee80211_bg_scan(struct ieee80211vap *, int);
void ieee80211_cancel_scan(struct ieee80211vap *);
void ieee80211_cancel_anyscan(struct ieee80211vap *);
void ieee80211_scan_next(struct ieee80211vap *);
void ieee80211_scan_done(struct ieee80211vap *);
void ieee80211_probe_curchan(struct ieee80211vap *, int);
struct ieee80211_channel *ieee80211_scan_pickchannel(struct ieee80211com *, int);
struct ieee80211_scanparams;
void ieee80211_add_scan(struct ieee80211vap *,
const struct ieee80211_scanparams *,
const struct ieee80211_frame *,
int subtype, int rssi, int noise);
void ieee80211_scan_timeout(struct ieee80211com *);
void ieee80211_scan_assoc_success(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
enum {
IEEE80211_SCAN_FAIL_TIMEOUT = 1, /* no response to mgmt frame */
IEEE80211_SCAN_FAIL_STATUS = 2 /* negative response to " " */
};
void ieee80211_scan_assoc_fail(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN], int reason);
void ieee80211_scan_flush(struct ieee80211vap *);
struct ieee80211_scan_entry;
typedef void ieee80211_scan_iter_func(void *,
const struct ieee80211_scan_entry *);
void ieee80211_scan_iterate(struct ieee80211vap *,
ieee80211_scan_iter_func, void *);
enum {
IEEE80211_BPARSE_BADIELEN = 0x01, /* ie len past end of frame */
IEEE80211_BPARSE_RATES_INVALID = 0x02, /* invalid RATES ie */
IEEE80211_BPARSE_XRATES_INVALID = 0x04, /* invalid XRATES ie */
IEEE80211_BPARSE_SSID_INVALID = 0x08, /* invalid SSID ie */
IEEE80211_BPARSE_CHAN_INVALID = 0x10, /* invalid FH/DSPARMS chan */
IEEE80211_BPARSE_OFFCHAN = 0x20, /* DSPARMS chan != curchan */
IEEE80211_BPARSE_BINTVAL_INVALID= 0x40, /* invalid beacon interval */
IEEE80211_BPARSE_CSA_INVALID = 0x80, /* invalid CSA ie */
};
/*
* Parameters supplied when adding/updating an entry in a
* scan cache. Pointer variables should be set to NULL
* if no data is available. Pointer references can be to
* local data; any information that is saved will be copied.
* All multi-byte values must be in host byte order.
*/
struct ieee80211_scanparams {
uint8_t status; /* bitmask of IEEE80211_BPARSE_* */
uint8_t chan; /* channel # from FH/DSPARMS */
uint8_t bchan; /* curchan's channel # */
uint8_t fhindex;
uint16_t fhdwell; /* FHSS dwell interval */
uint16_t capinfo; /* 802.11 capabilities */
uint16_t erp; /* NB: 0x100 indicates ie present */
uint16_t bintval;
uint8_t timoff;
uint8_t *ies; /* all captured ies */
size_t ies_len; /* length of all captured ies */
uint8_t *tim;
uint8_t *tstamp;
uint8_t *country;
uint8_t *ssid;
uint8_t *rates;
uint8_t *xrates;
uint8_t *doth;
uint8_t *wpa;
uint8_t *rsn;
uint8_t *wme;
uint8_t *htcap;
uint8_t *htinfo;
uint8_t *ath;
uint8_t *tdma;
uint8_t *csa;
uint8_t *meshid;
uint8_t *meshconf;
uint8_t *spare[3];
};
/*
* Scan cache entry format used when exporting data from a policy
* module; this data may be represented some other way internally.
*/
struct ieee80211_scan_entry {
uint8_t se_macaddr[IEEE80211_ADDR_LEN];
uint8_t se_bssid[IEEE80211_ADDR_LEN];
/* XXX can point inside se_ies */
uint8_t se_ssid[2+IEEE80211_NWID_LEN];
uint8_t se_rates[2+IEEE80211_RATE_MAXSIZE];
uint8_t se_xrates[2+IEEE80211_RATE_MAXSIZE];
union {
uint8_t data[8];
u_int64_t tsf;
} se_tstamp; /* from last rcv'd beacon */
uint16_t se_intval; /* beacon interval (host byte order) */
uint16_t se_capinfo; /* capabilities (host byte order) */
struct ieee80211_channel *se_chan;/* channel where sta found */
uint16_t se_timoff; /* byte offset to TIM ie */
uint16_t se_fhdwell; /* FH only (host byte order) */
uint8_t se_fhindex; /* FH only */
uint8_t se_dtimperiod; /* DTIM period */
uint16_t se_erp; /* ERP from beacon/probe resp */
int8_t se_rssi; /* avg'd recv ssi */
int8_t se_noise; /* noise floor */
uint8_t se_cc[2]; /* captured country code */
uint8_t se_meshid[2+IEEE80211_MESHID_LEN];
struct ieee80211_ies se_ies; /* captured ie's */
u_int se_age; /* age of entry (0 on create) */
};
/*
* Template for an in-kernel scan policy module.
* Modules register with the scanning code and are
* typically loaded as needed.
*/
struct ieee80211_scanner {
const char *scan_name; /* printable name */
int (*scan_attach)(struct ieee80211_scan_state *);
int (*scan_detach)(struct ieee80211_scan_state *);
int (*scan_start)(struct ieee80211_scan_state *,
struct ieee80211vap *);
int (*scan_restart)(struct ieee80211_scan_state *,
struct ieee80211vap *);
int (*scan_cancel)(struct ieee80211_scan_state *,
struct ieee80211vap *);
int (*scan_end)(struct ieee80211_scan_state *,
struct ieee80211vap *);
int (*scan_flush)(struct ieee80211_scan_state *);
struct ieee80211_channel *(*scan_pickchan)(
struct ieee80211_scan_state *, int);
/* add an entry to the cache */
int (*scan_add)(struct ieee80211_scan_state *,
const struct ieee80211_scanparams *,
const struct ieee80211_frame *,
int subtype, int rssi, int noise);
/* age and/or purge entries in the cache */
void (*scan_age)(struct ieee80211_scan_state *);
/* note that association failed for an entry */
void (*scan_assoc_fail)(struct ieee80211_scan_state *,
const uint8_t macaddr[IEEE80211_ADDR_LEN],
int reason);
/* note that association succeed for an entry */
void (*scan_assoc_success)(struct ieee80211_scan_state *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
/* iterate over entries in the scan cache */
void (*scan_iterate)(struct ieee80211_scan_state *,
ieee80211_scan_iter_func *, void *);
void (*scan_spare0)(void);
void (*scan_spare1)(void);
void (*scan_spare2)(void);
void (*scan_spare4)(void);
};
void ieee80211_scanner_register(enum ieee80211_opmode,
const struct ieee80211_scanner *);
void ieee80211_scanner_unregister(enum ieee80211_opmode,
const struct ieee80211_scanner *);
void ieee80211_scanner_unregister_all(const struct ieee80211_scanner *);
const struct ieee80211_scanner *ieee80211_scanner_get(enum ieee80211_opmode);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_SCAN_H_ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,36 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_STA_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_STA_H_
/*
* Station-mode implementation definitions.
*/
void ieee80211_sta_attach(struct ieee80211com *);
void ieee80211_sta_detach(struct ieee80211com *);
void ieee80211_sta_vattach(struct ieee80211vap *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_STA_H_ */
@@ -0,0 +1,900 @@
/*-
* Copyright (c) 2002-2009 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.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_llc.h>
#include <net/if_media.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_input.h>
#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_superg.h>
/*
* Atheros fast-frame encapsulation format.
* FF max payload:
* 802.2 + FFHDR + HPAD + 802.3 + 802.2 + 1500 + SPAD + 802.3 + 802.2 + 1500:
* 8 + 4 + 4 + 14 + 8 + 1500 + 6 + 14 + 8 + 1500
* = 3066
*/
/* fast frame header is 32-bits */
#define ATH_FF_PROTO 0x0000003f /* protocol */
#define ATH_FF_PROTO_S 0
#define ATH_FF_FTYPE 0x000000c0 /* frame type */
#define ATH_FF_FTYPE_S 6
#define ATH_FF_HLEN32 0x00000300 /* optional hdr length */
#define ATH_FF_HLEN32_S 8
#define ATH_FF_SEQNUM 0x001ffc00 /* sequence number */
#define ATH_FF_SEQNUM_S 10
#define ATH_FF_OFFSET 0xffe00000 /* offset to 2nd payload */
#define ATH_FF_OFFSET_S 21
#define ATH_FF_MAX_HDR_PAD 4
#define ATH_FF_MAX_SEP_PAD 6
#define ATH_FF_MAX_HDR 30
#define ATH_FF_PROTO_L2TUNNEL 0 /* L2 tunnel protocol */
#define ATH_FF_ETH_TYPE 0x88bd /* Ether type for encapsulated frames */
#define ATH_FF_SNAP_ORGCODE_0 0x00
#define ATH_FF_SNAP_ORGCODE_1 0x03
#define ATH_FF_SNAP_ORGCODE_2 0x7f
#define ATH_FF_TXQMIN 2 /* min txq depth for staging */
#define ATH_FF_TXQMAX 50 /* maximum # of queued frames allowed */
#define ATH_FF_STAGEMAX 5 /* max waiting period for staged frame*/
#define ETHER_HEADER_COPY(dst, src) \
memcpy(dst, src, sizeof(struct ether_header))
static int ieee80211_ffppsmin = 2; /* pps threshold for ff aggregation */
SYSCTL_INT(_net_wlan, OID_AUTO, ffppsmin, CTLTYPE_INT | CTLFLAG_RW,
&ieee80211_ffppsmin, 0, "min packet rate before fast-frame staging");
static int ieee80211_ffagemax = -1; /* max time frames held on stage q */
SYSCTL_PROC(_net_wlan, OID_AUTO, ffagemax, CTLTYPE_INT | CTLFLAG_RW,
&ieee80211_ffagemax, 0, ieee80211_sysctl_msecs_ticks, "I",
"max hold time for fast-frame staging (ms)");
void
ieee80211_superg_attach(struct ieee80211com *ic)
{
struct ieee80211_superg *sg;
if (ic->ic_caps & IEEE80211_C_FF) {
sg = (struct ieee80211_superg *) malloc(
sizeof(struct ieee80211_superg), M_80211_VAP,
M_NOWAIT | M_ZERO);
if (sg == NULL) {
printf("%s: cannot allocate SuperG state block\n",
__func__);
return;
}
ic->ic_superg = sg;
}
ieee80211_ffagemax = msecs_to_ticks(150);
}
void
ieee80211_superg_detach(struct ieee80211com *ic)
{
if (ic->ic_superg != NULL) {
free(ic->ic_superg, M_80211_VAP);
ic->ic_superg = NULL;
}
}
void
ieee80211_superg_vattach(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
if (ic->ic_superg == NULL) /* NB: can't do fast-frames w/o state */
vap->iv_caps &= ~IEEE80211_C_FF;
if (vap->iv_caps & IEEE80211_C_FF)
vap->iv_flags |= IEEE80211_F_FF;
/* NB: we only implement sta mode */
if (vap->iv_opmode == IEEE80211_M_STA &&
(vap->iv_caps & IEEE80211_C_TURBOP))
vap->iv_flags |= IEEE80211_F_TURBOP;
}
void
ieee80211_superg_vdetach(struct ieee80211vap *vap)
{
}
#define ATH_OUI_BYTES 0x00, 0x03, 0x7f
/*
* Add a WME information element to a frame.
*/
uint8_t *
ieee80211_add_ath(uint8_t *frm, uint8_t caps, ieee80211_keyix defkeyix)
{
static const struct ieee80211_ath_ie info = {
.ath_id = IEEE80211_ELEMID_VENDOR,
.ath_len = sizeof(struct ieee80211_ath_ie) - 2,
.ath_oui = { ATH_OUI_BYTES },
.ath_oui_type = ATH_OUI_TYPE,
.ath_oui_subtype= ATH_OUI_SUBTYPE,
.ath_version = ATH_OUI_VERSION,
};
struct ieee80211_ath_ie *ath = (struct ieee80211_ath_ie *) frm;
memcpy(frm, &info, sizeof(info));
ath->ath_capability = caps;
if (defkeyix != IEEE80211_KEYIX_NONE) {
ath->ath_defkeyix[0] = (defkeyix & 0xff);
ath->ath_defkeyix[1] = ((defkeyix >> 8) & 0xff);
} else {
ath->ath_defkeyix[0] = 0xff;
ath->ath_defkeyix[1] = 0x7f;
}
return frm + sizeof(info);
}
#undef ATH_OUI_BYTES
uint8_t *
ieee80211_add_athcaps(uint8_t *frm, const struct ieee80211_node *bss)
{
const struct ieee80211vap *vap = bss->ni_vap;
return ieee80211_add_ath(frm,
vap->iv_flags & IEEE80211_F_ATHEROS,
((vap->iv_flags & IEEE80211_F_WPA) == 0 &&
bss->ni_authmode != IEEE80211_AUTH_8021X) ?
vap->iv_def_txkey : IEEE80211_KEYIX_NONE);
}
void
ieee80211_parse_ath(struct ieee80211_node *ni, uint8_t *ie)
{
const struct ieee80211_ath_ie *ath =
(const struct ieee80211_ath_ie *) ie;
ni->ni_ath_flags = ath->ath_capability;
ni->ni_ath_defkeyix = LE_READ_2(&ath->ath_defkeyix);
}
int
ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
const struct ieee80211_frame *wh)
{
struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_ath_ie *ath;
u_int len = frm[1];
int capschanged;
uint16_t defkeyix;
if (len < sizeof(struct ieee80211_ath_ie)-2) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
wh, "Atheros", "too short, len %u", len);
return -1;
}
ath = (const struct ieee80211_ath_ie *)frm;
capschanged = (ni->ni_ath_flags != ath->ath_capability);
defkeyix = LE_READ_2(ath->ath_defkeyix);
if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
ni->ni_ath_flags = ath->ath_capability;
ni->ni_ath_defkeyix = defkeyix;
IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
"ath ie change: new caps 0x%x defkeyix 0x%x",
ni->ni_ath_flags, ni->ni_ath_defkeyix);
}
if (IEEE80211_ATH_CAP(vap, ni, ATHEROS_CAP_TURBO_PRIME)) {
uint16_t curflags, newflags;
/*
* Check for turbo mode switch. Calculate flags
* for the new mode and effect the switch.
*/
newflags = curflags = vap->iv_ic->ic_bsschan->ic_flags;
/* NB: BOOST is not in ic_flags, so get it from the ie */
if (ath->ath_capability & ATHEROS_CAP_BOOST)
newflags |= IEEE80211_CHAN_TURBO;
else
newflags &= ~IEEE80211_CHAN_TURBO;
if (newflags != curflags)
ieee80211_dturbo_switch(vap, newflags);
}
return capschanged;
}
/*
* Decap the encapsulated frame pair and dispatch the first
* for delivery. The second frame is returned for delivery
* via the normal path.
*/
struct mbuf *
ieee80211_ff_decap(struct ieee80211_node *ni, struct mbuf *m)
{
#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
#define MS(x,f) (((x) & f) >> f##_S)
struct ieee80211vap *vap = ni->ni_vap;
struct llc *llc;
uint32_t ath;
struct mbuf *n;
int framelen;
/* NB: we assume caller does this check for us */
KASSERT(IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF),
("ff not negotiated"));
/*
* Check for fast-frame tunnel encapsulation.
*/
if (m->m_pkthdr.len < 3*FF_LLC_SIZE)
return m;
if (m->m_len < FF_LLC_SIZE &&
(m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame",
"%s", "m_pullup(llc) failed");
vap->iv_stats.is_rx_tooshort++;
return NULL;
}
llc = (struct llc *)(mtod(m, uint8_t *) +
sizeof(struct ether_header));
if (llc->llc_snap.ether_type != htons(ATH_FF_ETH_TYPE))
return m;
m_adj(m, FF_LLC_SIZE);
m_copydata(m, 0, sizeof(uint32_t), (caddr_t) &ath);
if (MS(ath, ATH_FF_PROTO) != ATH_FF_PROTO_L2TUNNEL) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame",
"unsupport tunnel protocol, header 0x%x", ath);
vap->iv_stats.is_ff_badhdr++;
m_freem(m);
return NULL;
}
/* NB: skip header and alignment padding */
m_adj(m, roundup(sizeof(uint32_t) - 2, 4) + 2);
vap->iv_stats.is_ff_decap++;
/*
* Decap the first frame, bust it apart from the
* second and deliver; then decap the second frame
* and return it to the caller for normal delivery.
*/
m = ieee80211_decap1(m, &framelen);
if (m == NULL) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame", "%s", "first decap failed");
vap->iv_stats.is_ff_tooshort++;
return NULL;
}
n = m_split(m, framelen, M_NOWAIT);
if (n == NULL) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame",
"%s", "unable to split encapsulated frames");
vap->iv_stats.is_ff_split++;
m_freem(m); /* NB: must reclaim */
return NULL;
}
/* XXX not right for WDS */
vap->iv_deliver_data(vap, ni, m); /* 1st of pair */
/*
* Decap second frame.
*/
m_adj(n, roundup2(framelen, 4) - framelen); /* padding */
n = ieee80211_decap1(n, &framelen);
if (n == NULL) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame", "%s", "second decap failed");
vap->iv_stats.is_ff_tooshort++;
}
/* XXX verify framelen against mbuf contents */
return n; /* 2nd delivered by caller */
#undef MS
#undef FF_LLC_SIZE
}
/*
* Do Ethernet-LLC encapsulation for each payload in a fast frame
* tunnel encapsulation. The frame is assumed to have an Ethernet
* header at the front that must be stripped before prepending the
* LLC followed by the Ethernet header passed in (with an Ethernet
* type that specifies the payload size).
*/
static struct mbuf *
ff_encap1(struct ieee80211vap *vap, struct mbuf *m,
const struct ether_header *eh)
{
struct llc *llc;
uint16_t payload;
/* XXX optimize by combining m_adj+M_PREPEND */
m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
llc = mtod(m, struct llc *);
llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
llc->llc_control = LLC_UI;
llc->llc_snap.org_code[0] = 0;
llc->llc_snap.org_code[1] = 0;
llc->llc_snap.org_code[2] = 0;
llc->llc_snap.ether_type = eh->ether_type;
payload = m->m_pkthdr.len; /* NB: w/o Ethernet header */
M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
if (m == NULL) { /* XXX cannot happen */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for ether_header\n", __func__);
vap->iv_stats.is_tx_nobuf++;
return NULL;
}
ETHER_HEADER_COPY(mtod(m, void *), eh);
mtod(m, struct ether_header *)->ether_type = htons(payload);
return m;
}
/*
* Fast frame encapsulation. There must be two packets
* chained with m_nextpkt. We do header adjustment for
* each, add the tunnel encapsulation, and then concatenate
* the mbuf chains to form a single frame for transmission.
*/
struct mbuf *
ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace,
struct ieee80211_key *key)
{
struct mbuf *m2;
struct ether_header eh1, eh2;
struct llc *llc;
struct mbuf *m;
int pad;
m2 = m1->m_nextpkt;
if (m2 == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: only one frame\n", __func__);
goto bad;
}
m1->m_nextpkt = NULL;
/*
* Include fast frame headers in adjusting header layout.
*/
KASSERT(m1->m_len >= sizeof(eh1), ("no ethernet header!"));
ETHER_HEADER_COPY(&eh1, mtod(m1, caddr_t));
m1 = ieee80211_mbuf_adjust(vap,
hdrspace + sizeof(struct llc) + sizeof(uint32_t) + 2 +
sizeof(struct ether_header),
key, m1);
if (m1 == NULL) {
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
m_freem(m2);
goto bad;
}
/*
* Copy second frame's Ethernet header out of line
* and adjust for encapsulation headers. Note that
* we make room for padding in case there isn't room
* at the end of first frame.
*/
KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!"));
ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t));
m2 = ieee80211_mbuf_adjust(vap,
ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header),
NULL, m2);
if (m2 == NULL) {
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
goto bad;
}
/*
* Now do tunnel encapsulation. First, each
* frame gets a standard encapsulation.
*/
m1 = ff_encap1(vap, m1, &eh1);
if (m1 == NULL)
goto bad;
m2 = ff_encap1(vap, m2, &eh2);
if (m2 == NULL)
goto bad;
/*
* Pad leading frame to a 4-byte boundary. If there
* is space at the end of the first frame, put it
* there; otherwise prepend to the front of the second
* frame. We know doing the second will always work
* because we reserve space above. We prefer appending
* as this typically has better DMA alignment properties.
*/
for (m = m1; m->m_next != NULL; m = m->m_next)
;
pad = roundup2(m1->m_pkthdr.len, 4) - m1->m_pkthdr.len;
if (pad) {
if (M_TRAILINGSPACE(m) < pad) { /* prepend to second */
m2->m_data -= pad;
m2->m_len += pad;
m2->m_pkthdr.len += pad;
} else { /* append to first */
m->m_len += pad;
m1->m_pkthdr.len += pad;
}
}
/*
* Now, stick 'em together and prepend the tunnel headers;
* first the Atheros tunnel header (all zero for now) and
* then a special fast frame LLC.
*
* XXX optimize by prepending together
*/
m->m_next = m2; /* NB: last mbuf from above */
m1->m_pkthdr.len += m2->m_pkthdr.len;
M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT);
if (m1 == NULL) { /* XXX cannot happen */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for tunnel header\n", __func__);
vap->iv_stats.is_tx_nobuf++;
return NULL;
}
memset(mtod(m1, void *), 0, sizeof(uint32_t)+2);
M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT);
if (m1 == NULL) { /* XXX cannot happen */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for llc header\n", __func__);
vap->iv_stats.is_tx_nobuf++;
return NULL;
}
llc = mtod(m1, struct llc *);
llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
llc->llc_control = LLC_UI;
llc->llc_snap.org_code[0] = ATH_FF_SNAP_ORGCODE_0;
llc->llc_snap.org_code[1] = ATH_FF_SNAP_ORGCODE_1;
llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2;
llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE);
vap->iv_stats.is_ff_encap++;
return m1;
bad:
if (m1 != NULL)
m_freem(m1);
if (m2 != NULL)
m_freem(m2);
return NULL;
}
static void
ff_transmit(struct ieee80211_node *ni, struct mbuf *m)
{
struct ieee80211vap *vap = ni->ni_vap;
int error;
/* encap and xmit */
m = ieee80211_encap(vap, ni, m);
if (m != NULL) {
struct ifnet *ifp = vap->iv_ifp;
struct ifnet *parent = ni->ni_ic->ic_ifp;
error = parent->if_transmit(parent, m);
if (error != 0) {
/* NB: IFQ_HANDOFF reclaims mbuf */
ieee80211_free_node(ni);
} else {
ifp->if_opackets++;
}
} else
ieee80211_free_node(ni);
}
/*
* Flush frames to device; note we re-use the linked list
* the frames were stored on and use the sentinel (unchanged)
* which may be non-NULL.
*/
static void
ff_flush(struct mbuf *head, struct mbuf *last)
{
struct mbuf *m, *next;
struct ieee80211_node *ni;
struct ieee80211vap *vap;
for (m = head; m != last; m = next) {
next = m->m_nextpkt;
m->m_nextpkt = NULL;
ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
vap = ni->ni_vap;
IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
"%s: flush frame, age %u", __func__, M_AGE_GET(m));
vap->iv_stats.is_ff_flush++;
ff_transmit(ni, m);
}
}
/*
* Age frames on the staging queue.
*/
void
ieee80211_ff_age(struct ieee80211com *ic, struct ieee80211_stageq *sq,
int quanta)
{
struct ieee80211_superg *sg = ic->ic_superg;
struct mbuf *m, *head;
struct ieee80211_node *ni;
struct ieee80211_tx_ampdu *tap;
KASSERT(sq->head != NULL, ("stageq empty"));
IEEE80211_LOCK(ic);
head = sq->head;
while ((m = sq->head) != NULL && M_AGE_GET(m) < quanta) {
/* clear tap ref to frame */
ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
tap = &ni->ni_tx_ampdu[M_WME_GETAC(m)];
KASSERT(tap->txa_private == m, ("staging queue empty"));
tap->txa_private = NULL;
sq->head = m->m_nextpkt;
sq->depth--;
sg->ff_stageqdepth--;
}
if (m == NULL)
sq->tail = NULL;
else
M_AGE_SUB(m, quanta);
IEEE80211_UNLOCK(ic);
ff_flush(head, m);
}
static void
stageq_add(struct ieee80211_stageq *sq, struct mbuf *m)
{
int age = ieee80211_ffagemax;
if (sq->tail != NULL) {
sq->tail->m_nextpkt = m;
age -= M_AGE_GET(sq->head);
} else
sq->head = m;
KASSERT(age >= 0, ("age %d", age));
M_AGE_SET(m, age);
m->m_nextpkt = NULL;
sq->tail = m;
sq->depth++;
}
static void
stageq_remove(struct ieee80211_stageq *sq, struct mbuf *mstaged)
{
struct mbuf *m, *mprev;
mprev = NULL;
for (m = sq->head; m != NULL; m = m->m_nextpkt) {
if (m == mstaged) {
if (mprev == NULL)
sq->head = m->m_nextpkt;
else
mprev->m_nextpkt = m->m_nextpkt;
if (sq->tail == m)
sq->tail = mprev;
sq->depth--;
return;
}
mprev = m;
}
printf("%s: packet not found\n", __func__);
}
static uint32_t
ff_approx_txtime(struct ieee80211_node *ni,
const struct mbuf *m1, const struct mbuf *m2)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
uint32_t framelen;
/*
* Approximate the frame length to be transmitted. A swag to add
* the following maximal values to the skb payload:
* - 32: 802.11 encap + CRC
* - 24: encryption overhead (if wep bit)
* - 4 + 6: fast-frame header and padding
* - 16: 2 LLC FF tunnel headers
* - 14: 1 802.3 FF tunnel header (mbuf already accounts for 2nd)
*/
framelen = m1->m_pkthdr.len + 32 +
ATH_FF_MAX_HDR_PAD + ATH_FF_MAX_SEP_PAD + ATH_FF_MAX_HDR;
if (vap->iv_flags & IEEE80211_F_PRIVACY)
framelen += 24;
if (m2 != NULL)
framelen += m2->m_pkthdr.len;
return ieee80211_compute_duration(ic->ic_rt, framelen, ni->ni_txrate, 0);
}
/*
* Check if the supplied frame can be partnered with an existing
* or pending frame. Return a reference to any frame that should be
* sent on return; otherwise return NULL.
*/
struct mbuf *
ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_superg *sg = ic->ic_superg;
const int pri = M_WME_GETAC(m);
struct ieee80211_stageq *sq;
struct ieee80211_tx_ampdu *tap;
struct mbuf *mstaged;
uint32_t txtime, limit;
/*
* Check if the supplied frame can be aggregated.
*
* NB: we allow EAPOL frames to be aggregated with other ucast traffic.
* Do 802.1x EAPOL frames proceed in the clear? Then they couldn't
* be aggregated with other types of frames when encryption is on?
*/
IEEE80211_LOCK(ic);
tap = &ni->ni_tx_ampdu[pri];
mstaged = tap->txa_private; /* NB: we reuse AMPDU state */
ieee80211_txampdu_count_packet(tap);
/*
* When not in station mode never aggregate a multicast
* frame; this insures, for example, that a combined frame
* does not require multiple encryption keys.
*/
if (vap->iv_opmode != IEEE80211_M_STA &&
ETHER_IS_MULTICAST(mtod(m, struct ether_header *)->ether_dhost)) {
/* XXX flush staged frame? */
IEEE80211_UNLOCK(ic);
return m;
}
/*
* If there is no frame to combine with and the pps is
* too low; then do not attempt to aggregate this frame.
*/
if (mstaged == NULL &&
ieee80211_txampdu_getpps(tap) < ieee80211_ffppsmin) {
IEEE80211_UNLOCK(ic);
return m;
}
sq = &sg->ff_stageq[pri];
/*
* Check the txop limit to insure the aggregate fits.
*/
limit = IEEE80211_TXOP_TO_US(
ic->ic_wme.wme_chanParams.cap_wmeParams[pri].wmep_txopLimit);
if (limit != 0 &&
(txtime = ff_approx_txtime(ni, m, mstaged)) > limit) {
/*
* Aggregate too long, return to the caller for direct
* transmission. In addition, flush any pending frame
* before sending this one.
*/
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: txtime %u exceeds txop limit %u\n",
__func__, txtime, limit);
tap->txa_private = NULL;
if (mstaged != NULL)
stageq_remove(sq, mstaged);
IEEE80211_UNLOCK(ic);
if (mstaged != NULL) {
IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
"%s: flush staged frame", __func__);
/* encap and xmit */
ff_transmit(ni, mstaged);
}
return m; /* NB: original frame */
}
/*
* An aggregation candidate. If there's a frame to partner
* with then combine and return for processing. Otherwise
* save this frame and wait for a partner to show up (or
* the frame to be flushed). Note that staged frames also
* hold their node reference.
*/
if (mstaged != NULL) {
tap->txa_private = NULL;
stageq_remove(sq, mstaged);
IEEE80211_UNLOCK(ic);
IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
"%s: aggregate fast-frame", __func__);
/*
* Release the node reference; we only need
* the one already in mstaged.
*/
KASSERT(mstaged->m_pkthdr.rcvif == (void *)ni,
("rcvif %p ni %p", mstaged->m_pkthdr.rcvif, ni));
ieee80211_free_node(ni);
m->m_nextpkt = NULL;
mstaged->m_nextpkt = m;
mstaged->m_flags |= M_FF; /* NB: mark for encap work */
} else {
KASSERT(tap->txa_private == NULL,
("txa_private %p", tap->txa_private));
tap->txa_private = m;
stageq_add(sq, m);
sg->ff_stageqdepth++;
IEEE80211_UNLOCK(ic);
IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
"%s: stage frame, %u queued", __func__, sq->depth);
/* NB: mstaged is NULL */
}
return mstaged;
}
void
ieee80211_ff_node_init(struct ieee80211_node *ni)
{
/*
* Clean FF state on re-associate. This handles the case
* where a station leaves w/o notifying us and then returns
* before node is reaped for inactivity.
*/
ieee80211_ff_node_cleanup(ni);
}
void
ieee80211_ff_node_cleanup(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_superg *sg = ic->ic_superg;
struct ieee80211_tx_ampdu *tap;
struct mbuf *m, *head;
int ac;
IEEE80211_LOCK(ic);
head = NULL;
for (ac = 0; ac < WME_NUM_AC; ac++) {
tap = &ni->ni_tx_ampdu[ac];
m = tap->txa_private;
if (m != NULL) {
tap->txa_private = NULL;
stageq_remove(&sg->ff_stageq[ac], m);
m->m_nextpkt = head;
head = m;
}
}
IEEE80211_UNLOCK(ic);
for (m = head; m != NULL; m = m->m_nextpkt) {
m_freem(m);
ieee80211_free_node(ni);
}
}
/*
* Switch between turbo and non-turbo operating modes.
* Use the specified channel flags to locate the new
* channel, update 802.11 state, and then call back into
* the driver to effect the change.
*/
void
ieee80211_dturbo_switch(struct ieee80211vap *vap, int newflags)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *chan;
chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags);
if (chan == NULL) { /* XXX should not happen */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no channel with freq %u flags 0x%x\n",
__func__, ic->ic_bsschan->ic_freq, newflags);
return;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: %s -> %s (freq %u flags 0x%x)\n", __func__,
ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)],
ieee80211_phymode_name[ieee80211_chan2mode(chan)],
chan->ic_freq, chan->ic_flags);
ic->ic_bsschan = chan;
ic->ic_prevchan = ic->ic_curchan;
ic->ic_curchan = chan;
ic->ic_rt = ieee80211_get_ratetable(chan);
ic->ic_set_channel(ic);
ieee80211_radiotap_chan_change(ic);
/* NB: do not need to reset ERP state 'cuz we're in sta mode */
}
/*
* Return the current ``state'' of an Atheros capbility.
* If associated in station mode report the negotiated
* setting. Otherwise report the current setting.
*/
static int
getathcap(struct ieee80211vap *vap, int cap)
{
if (vap->iv_opmode == IEEE80211_M_STA &&
vap->iv_state == IEEE80211_S_RUN)
return IEEE80211_ATH_CAP(vap, vap->iv_bss, cap) != 0;
else
return (vap->iv_flags & cap) != 0;
}
static int
superg_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
switch (ireq->i_type) {
case IEEE80211_IOC_FF:
ireq->i_val = getathcap(vap, IEEE80211_F_FF);
break;
case IEEE80211_IOC_TURBOP:
ireq->i_val = getathcap(vap, IEEE80211_F_TURBOP);
break;
default:
return ENOSYS;
}
return 0;
}
IEEE80211_IOCTL_GET(superg, superg_ioctl_get80211);
static int
superg_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
switch (ireq->i_type) {
case IEEE80211_IOC_FF:
if (ireq->i_val) {
if ((vap->iv_caps & IEEE80211_C_FF) == 0)
return EOPNOTSUPP;
vap->iv_flags |= IEEE80211_F_FF;
} else
vap->iv_flags &= ~IEEE80211_F_FF;
return ENETRESET;
case IEEE80211_IOC_TURBOP:
if (ireq->i_val) {
if ((vap->iv_caps & IEEE80211_C_TURBOP) == 0)
return EOPNOTSUPP;
vap->iv_flags |= IEEE80211_F_TURBOP;
} else
vap->iv_flags &= ~IEEE80211_F_TURBOP;
return ENETRESET;
default:
return ENOSYS;
}
return 0;
}
IEEE80211_IOCTL_SET(superg, superg_ioctl_set80211);
@@ -0,0 +1,129 @@
/*-
* Copyright (c) 2009 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_SUPERG_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_SUPERG_H_
/*
* Atheros' 802.11 SuperG protocol support.
*/
/*
* Atheros advanced capability information element.
*/
struct ieee80211_ath_ie {
uint8_t ath_id; /* IEEE80211_ELEMID_VENDOR */
uint8_t ath_len; /* length in bytes */
uint8_t ath_oui[3]; /* ATH_OUI */
uint8_t ath_oui_type; /* ATH_OUI_TYPE */
uint8_t ath_oui_subtype; /* ATH_OUI_SUBTYPE */
uint8_t ath_version; /* spec revision */
uint8_t ath_capability; /* capability info */
#define ATHEROS_CAP_TURBO_PRIME 0x01 /* dynamic turbo--aka Turbo' */
#define ATHEROS_CAP_COMPRESSION 0x02 /* data compression */
#define ATHEROS_CAP_FAST_FRAME 0x04 /* fast (jumbo) frames */
#define ATHEROS_CAP_XR 0x08 /* Xtended Range support */
#define ATHEROS_CAP_AR 0x10 /* Advanded Radar support */
#define ATHEROS_CAP_BURST 0x20 /* Bursting - not negotiated */
#define ATHEROS_CAP_WME 0x40 /* CWMin tuning */
#define ATHEROS_CAP_BOOST 0x80 /* use turbo/!turbo mode */
uint8_t ath_defkeyix[2];
} __packed;
#define ATH_OUI_VERSION 0x00
#define ATH_OUI_SUBTYPE 0x01
#ifdef _KERNEL
struct ieee80211_stageq {
struct mbuf *head; /* frames linked w/ m_nextpkt */
struct mbuf *tail; /* last frame in queue */
int depth; /* # items on head */
};
struct ieee80211_superg {
/* fast-frames staging q */
struct ieee80211_stageq ff_stageq[WME_NUM_AC];
int ff_stageqdepth; /* cumulative depth */
};
void ieee80211_superg_attach(struct ieee80211com *);
void ieee80211_superg_detach(struct ieee80211com *);
void ieee80211_superg_vattach(struct ieee80211vap *);
void ieee80211_superg_vdetach(struct ieee80211vap *);
uint8_t *ieee80211_add_ath(uint8_t *, uint8_t, ieee80211_keyix);
uint8_t *ieee80211_add_athcaps(uint8_t *, const struct ieee80211_node *);
void ieee80211_parse_ath(struct ieee80211_node *, uint8_t *);
int ieee80211_parse_athparams(struct ieee80211_node *, uint8_t *,
const struct ieee80211_frame *);
void ieee80211_ff_node_init(struct ieee80211_node *);
void ieee80211_ff_node_cleanup(struct ieee80211_node *);
struct mbuf *ieee80211_ff_check(struct ieee80211_node *, struct mbuf *);
void ieee80211_ff_age(struct ieee80211com *, struct ieee80211_stageq *,
int quanta);
static __inline void
ieee80211_ff_flush(struct ieee80211com *ic, int ac)
{
struct ieee80211_superg *sg = ic->ic_superg;
if (sg != NULL && sg->ff_stageq[ac].depth)
ieee80211_ff_age(ic, &sg->ff_stageq[ac], 0x7fffffff);
}
static __inline void
ieee80211_ff_age_all(struct ieee80211com *ic, int quanta)
{
struct ieee80211_superg *sg = ic->ic_superg;
if (sg != NULL && sg->ff_stageqdepth) {
if (sg->ff_stageq[WME_AC_VO].depth)
ieee80211_ff_age(ic, &sg->ff_stageq[WME_AC_VO], quanta);
if (sg->ff_stageq[WME_AC_VI].depth)
ieee80211_ff_age(ic, &sg->ff_stageq[WME_AC_VI], quanta);
if (sg->ff_stageq[WME_AC_BE].depth)
ieee80211_ff_age(ic, &sg->ff_stageq[WME_AC_BE], quanta);
if (sg->ff_stageq[WME_AC_BK].depth)
ieee80211_ff_age(ic, &sg->ff_stageq[WME_AC_BK], quanta);
}
}
struct mbuf *ieee80211_ff_encap(struct ieee80211vap *, struct mbuf *,
int, struct ieee80211_key *);
struct mbuf *ieee80211_ff_decap(struct ieee80211_node *, struct mbuf *);
static __inline struct mbuf *
ieee80211_decap_fastframe(struct ieee80211vap *vap, struct ieee80211_node *ni,
struct mbuf *m)
{
return IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
ieee80211_ff_decap(ni, m) : m;
}
#endif /* _KERNEL */
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_SUPERG_H_ */
@@ -0,0 +1,818 @@
/*-
* Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
* Copyright (c) 2007-2009 Intel Corporation
* 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.
*
* 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.
*/
#include <sys/cdefs.h>
#ifdef __FreeBSD__
__FBSDID("$FreeBSD$");
#endif
/*
* IEEE 802.11 TDMA mode support.
*/
#include "opt_inet.h"
#include "opt_tdma.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/endian.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_llc.h>
#include <net/ethernet.h>
#include <net/bpf.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_tdma.h>
#include <net80211/ieee80211_input.h>
#ifndef TDMA_SLOTLEN_DEFAULT
#define TDMA_SLOTLEN_DEFAULT 10*1000 /* 10ms */
#endif
#ifndef TDMA_SLOTCNT_DEFAULT
#define TDMA_SLOTCNT_DEFAULT 2 /* 2x (pt-to-pt) */
#endif
#ifndef TDMA_BINTVAL_DEFAULT
#define TDMA_BINTVAL_DEFAULT 5 /* 5x ~= 100TU beacon intvl */
#endif
#ifndef TDMA_TXRATE_11B_DEFAULT
#define TDMA_TXRATE_11B_DEFAULT 2*11
#endif
#ifndef TDMA_TXRATE_11G_DEFAULT
#define TDMA_TXRATE_11G_DEFAULT 2*24
#endif
#ifndef TDMA_TXRATE_11A_DEFAULT
#define TDMA_TXRATE_11A_DEFAULT 2*24
#endif
#ifndef TDMA_TXRATE_TURBO_DEFAULT
#define TDMA_TXRATE_TURBO_DEFAULT 2*24
#endif
#ifndef TDMA_TXRATE_HALF_DEFAULT
#define TDMA_TXRATE_HALF_DEFAULT 2*12
#endif
#ifndef TDMA_TXRATE_QUARTER_DEFAULT
#define TDMA_TXRATE_QUARTER_DEFAULT 2*6
#endif
#ifndef TDMA_TXRATE_11NA_DEFAULT
#define TDMA_TXRATE_11NA_DEFAULT (4 | IEEE80211_RATE_MCS)
#endif
#ifndef TDMA_TXRATE_11NG_DEFAULT
#define TDMA_TXRATE_11NG_DEFAULT (4 | IEEE80211_RATE_MCS)
#endif
#define TDMA_VERSION_VALID(_version) \
(TDMA_VERSION_V2 <= (_version) && (_version) <= TDMA_VERSION)
#define TDMA_SLOTCNT_VALID(_slotcnt) \
(2 <= (_slotcnt) && (_slotcnt) <= TDMA_MAXSLOTS)
/* XXX magic constants */
#define TDMA_SLOTLEN_VALID(_slotlen) \
(2*100 <= (_slotlen) && (unsigned)(_slotlen) <= 0xfffff)
/* XXX probably should set a max */
#define TDMA_BINTVAL_VALID(_bintval) (1 <= (_bintval))
/*
* This code is not prepared to handle more than 2 slots.
*/
CTASSERT(TDMA_MAXSLOTS == 2);
static void tdma_vdetach(struct ieee80211vap *vap);
static int tdma_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void tdma_beacon_miss(struct ieee80211vap *vap);
static void tdma_recv_mgmt(struct ieee80211_node *, struct mbuf *,
int subtype, int rssi, int nf);
static int tdma_update(struct ieee80211vap *vap,
const struct ieee80211_tdma_param *tdma, struct ieee80211_node *ni,
int pickslot);
static int tdma_process_params(struct ieee80211_node *ni,
const u_int8_t *ie, int rssi, int nf, const struct ieee80211_frame *wh);
static void
settxparms(struct ieee80211vap *vap, enum ieee80211_phymode mode, int rate)
{
vap->iv_txparms[mode].ucastrate = rate;
vap->iv_txparms[mode].mcastrate = rate;
}
static void
setackpolicy(struct ieee80211com *ic, int noack)
{
struct ieee80211_wme_state *wme = &ic->ic_wme;
int ac;
for (ac = 0; ac < WME_NUM_AC; ac++) {
wme->wme_chanParams.cap_wmeParams[ac].wmep_noackPolicy = noack;
wme->wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy = noack;
}
}
void
ieee80211_tdma_vattach(struct ieee80211vap *vap)
{
struct ieee80211_tdma_state *ts;
KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
("not a tdma vap, caps 0x%x", vap->iv_caps));
ts = (struct ieee80211_tdma_state *) malloc(
sizeof(struct ieee80211_tdma_state), M_80211_VAP, M_NOWAIT | M_ZERO);
if (ts == NULL) {
printf("%s: cannot allocate TDMA state block\n", __func__);
/* NB: fall back to adhdemo mode */
vap->iv_caps &= ~IEEE80211_C_TDMA;
return;
}
/* NB: default configuration is passive so no beacons */
ts->tdma_version = TDMA_VERSION;
ts->tdma_slotlen = TDMA_SLOTLEN_DEFAULT;
ts->tdma_slotcnt = TDMA_SLOTCNT_DEFAULT;
ts->tdma_bintval = TDMA_BINTVAL_DEFAULT;
ts->tdma_slot = 1; /* passive operation */
/* setup default fixed rates */
settxparms(vap, IEEE80211_MODE_11A, TDMA_TXRATE_11A_DEFAULT);
settxparms(vap, IEEE80211_MODE_11B, TDMA_TXRATE_11B_DEFAULT);
settxparms(vap, IEEE80211_MODE_11G, TDMA_TXRATE_11G_DEFAULT);
settxparms(vap, IEEE80211_MODE_TURBO_A, TDMA_TXRATE_TURBO_DEFAULT);
settxparms(vap, IEEE80211_MODE_TURBO_G, TDMA_TXRATE_TURBO_DEFAULT);
settxparms(vap, IEEE80211_MODE_STURBO_A, TDMA_TXRATE_TURBO_DEFAULT);
settxparms(vap, IEEE80211_MODE_11NA, TDMA_TXRATE_11NA_DEFAULT);
settxparms(vap, IEEE80211_MODE_11NG, TDMA_TXRATE_11NG_DEFAULT);
settxparms(vap, IEEE80211_MODE_HALF, TDMA_TXRATE_HALF_DEFAULT);
settxparms(vap, IEEE80211_MODE_QUARTER, TDMA_TXRATE_QUARTER_DEFAULT);
setackpolicy(vap->iv_ic, 1); /* disable ACK's */
ts->tdma_opdetach = vap->iv_opdetach;
vap->iv_opdetach = tdma_vdetach;
ts->tdma_newstate = vap->iv_newstate;
vap->iv_newstate = tdma_newstate;
vap->iv_bmiss = tdma_beacon_miss;
ts->tdma_recv_mgmt = vap->iv_recv_mgmt;
vap->iv_recv_mgmt = tdma_recv_mgmt;
vap->iv_tdma = ts;
}
static void
tdma_vdetach(struct ieee80211vap *vap)
{
struct ieee80211_tdma_state *ts = vap->iv_tdma;
if (ts == NULL) {
/* NB: should not have touched any ic state */
return;
}
ts->tdma_opdetach(vap);
free(vap->iv_tdma, M_80211_VAP);
vap->iv_tdma = NULL;
setackpolicy(vap->iv_ic, 0); /* enable ACK's */
}
static void
sta_leave(void *arg, struct ieee80211_node *ni)
{
struct ieee80211vap *vap = arg;
if (ni->ni_vap == vap && ni != vap->iv_bss)
ieee80211_node_leave(ni);
}
/*
* TDMA state machine handler.
*/
static int
tdma_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct ieee80211_tdma_state *ts = vap->iv_tdma;
struct ieee80211com *ic = vap->iv_ic;
enum ieee80211_state ostate;
int status;
IEEE80211_LOCK_ASSERT(ic);
ostate = vap->iv_state;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
__func__, ieee80211_state_name[ostate],
ieee80211_state_name[nstate], arg);
if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
callout_stop(&vap->iv_swbmiss);
if (nstate == IEEE80211_S_SCAN &&
(ostate == IEEE80211_S_INIT || ostate == IEEE80211_S_RUN) &&
ts->tdma_slot != 0) {
/*
* Override adhoc behaviour when operating as a slave;
* we need to scan even if the channel is locked.
*/
vap->iv_state = nstate; /* state transition */
ieee80211_cancel_scan(vap); /* background scan */
if (ostate == IEEE80211_S_RUN) {
/* purge station table; entries are stale */
ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap);
}
if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
ieee80211_check_scan(vap,
vap->iv_scanreq_flags,
vap->iv_scanreq_duration,
vap->iv_scanreq_mindwell,
vap->iv_scanreq_maxdwell,
vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
} else
ieee80211_check_scan_current(vap);
status = 0;
} else {
status = ts->tdma_newstate(vap, nstate, arg);
}
if (status == 0 &&
nstate == IEEE80211_S_RUN && ostate != IEEE80211_S_RUN &&
(vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) &&
ts->tdma_slot != 0 &&
vap->iv_des_chan == IEEE80211_CHAN_ANYC) {
/*
* Start s/w beacon miss timer for slave devices w/o
* hardware support. Note we do this only if we're
* not locked to a channel (i.e. roam to follow the
* master). The 2x is a fudge for our doing this in
* software.
*/
vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS(
2 * vap->iv_bmissthreshold * ts->tdma_bintval *
((ts->tdma_slotcnt * ts->tdma_slotlen) / 1024));
vap->iv_swbmiss_count = 0;
callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
ieee80211_swbmiss, vap);
}
return status;
}
static void
tdma_beacon_miss(struct ieee80211vap *vap)
{
struct ieee80211_tdma_state *ts = vap->iv_tdma;
KASSERT((vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
KASSERT(vap->iv_state == IEEE80211_S_RUN,
("wrong state %d", vap->iv_state));
IEEE80211_DPRINTF(vap,
IEEE80211_MSG_STATE | IEEE80211_MSG_TDMA | IEEE80211_MSG_DEBUG,
"beacon miss, mode %u state %s\n",
vap->iv_opmode, ieee80211_state_name[vap->iv_state]);
if (ts->tdma_peer != NULL) { /* XXX? can this be null? */
ieee80211_notify_node_leave(vap->iv_bss);
ts->tdma_peer = NULL;
/*
* Treat beacon miss like an associate failure wrt the
* scan policy; this forces the entry in the scan cache
* to be ignored after several tries.
*/
ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr,
IEEE80211_STATUS_TIMEOUT);
}
#if 0
ts->tdma_inuse = 0; /* clear slot usage */
#endif
ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
}
static void
tdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
int subtype, int rssi, int nf)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_tdma_state *ts = vap->iv_tdma;
if (subtype == IEEE80211_FC0_SUBTYPE_BEACON &&
(ic->ic_flags & IEEE80211_F_SCAN) == 0) {
struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *);
struct ieee80211_scanparams scan;
if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
return;
if (scan.tdma == NULL) {
/*
* TDMA stations must beacon a TDMA ie; ignore
* any other station.
* XXX detect overlapping bss and change channel
*/
IEEE80211_DISCARD(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
wh, ieee80211_mgt_subtype_name[subtype >>
IEEE80211_FC0_SUBTYPE_SHIFT],
"%s", "no TDMA ie");
vap->iv_stats.is_rx_mgtdiscard++;
return;
}
if (ni == vap->iv_bss &&
!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
/*
* Fake up a node for this newly
* discovered member of the IBSS.
*/
ni = ieee80211_add_neighbor(vap, wh, &scan);
if (ni == NULL) {
/* NB: stat kept for alloc failure */
return;
}
}
/*
* Check for state updates.
*/
if (IEEE80211_ADDR_EQ(wh->i_addr3, ni->ni_bssid)) {
/*
* Count frame now that we know it's to be processed.
*/
vap->iv_stats.is_rx_beacon++;
IEEE80211_NODE_STAT(ni, rx_beacons);
/*
* Record tsf of last beacon. NB: this must be
* done before calling tdma_process_params
* as deeper routines reference it.
*/
memcpy(&ni->ni_tstamp.data, scan.tstamp,
sizeof(ni->ni_tstamp.data));
/*
* Count beacon frame for s/w bmiss handling.
*/
vap->iv_swbmiss_count++;
/*
* Process tdma ie. The contents are used to sync
* the slot timing, reconfigure the bss, etc.
*/
(void) tdma_process_params(ni, scan.tdma, rssi, nf, wh);
return;
}
/*
* NB: defer remaining work to the adhoc code; this causes
* 2x parsing of the frame but should happen infrequently
*/
}
ts->tdma_recv_mgmt(ni, m0, subtype, rssi, nf);
}
/*
* Update TDMA state on receipt of a beacon frame with
* a TDMA information element. The sender's identity
* is provided so we can track who our peer is. If pickslot
* is non-zero we scan the slot allocation state in the ie
* to locate a free slot for our use.
*/
static int
tdma_update(struct ieee80211vap *vap, const struct ieee80211_tdma_param *tdma,
struct ieee80211_node *ni, int pickslot)
{
struct ieee80211_tdma_state *ts = vap->iv_tdma;
int slot, slotlen, update;
KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
("not a tdma vap, caps 0x%x", vap->iv_caps));
update = 0;
if (tdma->tdma_slotcnt != ts->tdma_slotcnt) {
if (!TDMA_SLOTCNT_VALID(tdma->tdma_slotcnt)) {
if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1))
printf("%s: bad slot cnt %u\n",
__func__, tdma->tdma_slotcnt);
return 0;
}
update |= TDMA_UPDATE_SLOTCNT;
}
slotlen = le16toh(tdma->tdma_slotlen) * 100;
if (slotlen != ts->tdma_slotlen) {
if (!TDMA_SLOTLEN_VALID(slotlen)) {
if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1))
printf("%s: bad slot len %u\n",
__func__, slotlen);
return 0;
}
update |= TDMA_UPDATE_SLOTLEN;
}
if (tdma->tdma_bintval != ts->tdma_bintval) {
if (!TDMA_BINTVAL_VALID(tdma->tdma_bintval)) {
if (ppsratecheck(&ts->tdma_lastprint, &ts->tdma_fails, 1))
printf("%s: bad beacon interval %u\n",
__func__, tdma->tdma_bintval);
return 0;
}
update |= TDMA_UPDATE_BINTVAL;
}
slot = ts->tdma_slot;
if (pickslot) {
/*
* Pick unoccupied slot. Note we never choose slot 0.
*/
for (slot = tdma->tdma_slotcnt-1; slot > 0; slot--)
if (isclr(tdma->tdma_inuse, slot))
break;
if (slot <= 0) {
printf("%s: no free slot, slotcnt %u inuse: 0x%x\n",
__func__, tdma->tdma_slotcnt,
tdma->tdma_inuse[0]);
/* XXX need to do something better */
return 0;
}
if (slot != ts->tdma_slot)
update |= TDMA_UPDATE_SLOT;
}
if (ni != ts->tdma_peer) {
/* update everything */
update = TDMA_UPDATE_SLOT
| TDMA_UPDATE_SLOTCNT
| TDMA_UPDATE_SLOTLEN
| TDMA_UPDATE_BINTVAL;
}
if (update) {
/*
* New/changed parameters; update runtime state.
*/
/* XXX overwrites user parameters */
if (update & TDMA_UPDATE_SLOTCNT)
ts->tdma_slotcnt = tdma->tdma_slotcnt;
if (update & TDMA_UPDATE_SLOTLEN)
ts->tdma_slotlen = slotlen;
if (update & TDMA_UPDATE_SLOT)
ts->tdma_slot = slot;
if (update & TDMA_UPDATE_BINTVAL)
ts->tdma_bintval = tdma->tdma_bintval;
/* mark beacon to be updated before next xmit */
ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA,
"%s: slot %u slotcnt %u slotlen %u us bintval %u\n",
__func__, ts->tdma_slot, ts->tdma_slotcnt,
ts->tdma_slotlen, ts->tdma_bintval);
}
/*
* Notify driver. Note we can be called before
* entering RUN state if we scanned and are
* joining an existing bss. In that case do not
* call the driver because not all necessary state
* has been setup. The next beacon will dtrt.
*/
if (vap->iv_state == IEEE80211_S_RUN)
vap->iv_ic->ic_tdma_update(ni, tdma, update);
/*
* Dispatch join event on first beacon from new master.
*/
if (ts->tdma_peer != ni) {
if (ts->tdma_peer != NULL)
ieee80211_notify_node_leave(vap->iv_bss);
ieee80211_notify_node_join(ni, 1);
/* NB: no reference, we just use the address */
ts->tdma_peer = ni;
}
return 1;
}
/*
* Process received TDMA parameters.
*/
static int
tdma_process_params(struct ieee80211_node *ni, const u_int8_t *ie,
int rssi, int nf, const struct ieee80211_frame *wh)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_tdma_state *ts = vap->iv_tdma;
const struct ieee80211_tdma_param *tdma =
(const struct ieee80211_tdma_param *) ie;
u_int len = ie[1];
KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
("not a tdma vap, caps 0x%x", vap->iv_caps));
if (len < sizeof(*tdma) - 2) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA,
wh, "tdma", "too short, len %u", len);
return IEEE80211_REASON_IE_INVALID;
}
if (tdma->tdma_version != ts->tdma_version) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA,
wh, "tdma", "bad version %u (ours %u)",
tdma->tdma_version, ts->tdma_version);
return IEEE80211_REASON_IE_INVALID;
}
/*
* NB: ideally we'd check against tdma_slotcnt, but that
* would require extra effort so do this easy check that
* covers the work below; more stringent checks are done
* before we make more extensive use of the ie contents.
*/
if (tdma->tdma_slot >= TDMA_MAXSLOTS) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_TDMA,
wh, "tdma", "invalid slot %u", tdma->tdma_slot);
return IEEE80211_REASON_IE_INVALID;
}
/*
* Can reach here while scanning, update
* operational state only in RUN state.
*/
if (vap->iv_state == IEEE80211_S_RUN) {
if (tdma->tdma_slot != ts->tdma_slot &&
isclr(ts->tdma_inuse, tdma->tdma_slot)) {
IEEE80211_NOTE(vap, IEEE80211_MSG_TDMA, ni,
"discovered in slot %u", tdma->tdma_slot);
setbit(ts->tdma_inuse, tdma->tdma_slot);
/* XXX dispatch event only when operating as master */
if (ts->tdma_slot == 0)
ieee80211_notify_node_join(ni, 1);
}
setbit(ts->tdma_active, tdma->tdma_slot);
if (tdma->tdma_slot == ts->tdma_slot-1) {
/*
* Slave tsf synchronization to station
* just before us in the schedule. The driver
* is responsible for copying the timestamp
* of the received beacon into our beacon
* frame so the sender can calculate round
* trip time. We cannot do that here because
* we don't know how to update our beacon frame.
*/
(void) tdma_update(vap, tdma, ni, 0);
/* XXX reschedule swbmiss timer on parameter change */
} else if (tdma->tdma_slot == ts->tdma_slot+1) {
uint64_t tstamp;
#if 0
uint32_t rstamp = (uint32_t) le64toh(rs->tsf);
int32_t rtt;
#endif
/*
* Use returned timstamp to calculate the
* roundtrip time.
*/
memcpy(&tstamp, tdma->tdma_tstamp, 8);
#if 0
/* XXX use only 15 bits of rstamp */
rtt = rstamp - (le64toh(tstamp) & 0x7fff);
if (rtt < 0)
rtt += 0x7fff;
/* XXX hack to quiet normal use */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOT1X,
"tdma rtt %5u [rstamp %5u tstamp %llu]\n",
rtt, rstamp,
(unsigned long long) le64toh(tstamp));
#endif
} else if (tdma->tdma_slot == ts->tdma_slot &&
le64toh(ni->ni_tstamp.tsf) > vap->iv_bss->ni_tstamp.tsf) {
/*
* Station using the same slot as us and has
* been around longer than us; we must move.
* Note this can happen if stations do not
* see each other while scanning.
*/
IEEE80211_DPRINTF(vap, IEEE80211_MSG_TDMA,
"slot %u collision rxtsf %llu tsf %llu\n",
tdma->tdma_slot,
(unsigned long long) le64toh(ni->ni_tstamp.tsf),
vap->iv_bss->ni_tstamp.tsf);
setbit(ts->tdma_inuse, tdma->tdma_slot);
(void) tdma_update(vap, tdma, ni, 1);
}
}
return 0;
}
int
ieee80211_tdma_getslot(struct ieee80211vap *vap)
{
struct ieee80211_tdma_state *ts = vap->iv_tdma;
KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
("not a tdma vap, caps 0x%x", vap->iv_caps));
return ts->tdma_slot;
}
/*
* Parse a TDMA ie on station join and use it to setup node state.
*/
void
ieee80211_parse_tdma(struct ieee80211_node *ni, const uint8_t *ie)
{
struct ieee80211vap *vap = ni->ni_vap;
if (vap->iv_caps & IEEE80211_C_TDMA) {
const struct ieee80211_tdma_param *tdma =
(const struct ieee80211_tdma_param *)ie;
struct ieee80211_tdma_state *ts = vap->iv_tdma;
/*
* Adopt TDMA configuration when joining an
* existing network.
*/
setbit(ts->tdma_inuse, tdma->tdma_slot);
(void) tdma_update(vap, tdma, ni, 1);
/*
* Propagate capabilities based on the local
* configuration and the remote station's advertised
* capabilities. In particular this permits us to
* enable use of QoS to disable ACK's.
*/
if ((vap->iv_flags & IEEE80211_F_WME) &&
ni->ni_ies.wme_ie != NULL)
ni->ni_flags |= IEEE80211_NODE_QOS;
}
}
#define TDMA_OUI_BYTES 0x00, 0x03, 0x7f
/*
* Add a TDMA parameters element to a frame.
*/
uint8_t *
ieee80211_add_tdma(uint8_t *frm, struct ieee80211vap *vap)
{
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
static const struct ieee80211_tdma_param param = {
.tdma_id = IEEE80211_ELEMID_VENDOR,
.tdma_len = sizeof(struct ieee80211_tdma_param) - 2,
.tdma_oui = { TDMA_OUI_BYTES },
.tdma_type = TDMA_OUI_TYPE,
.tdma_subtype = TDMA_SUBTYPE_PARAM,
.tdma_version = TDMA_VERSION,
};
const struct ieee80211_tdma_state *ts = vap->iv_tdma;
uint16_t slotlen;
KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
("not a tdma vap, caps 0x%x", vap->iv_caps));
memcpy(frm, &param, sizeof(param));
frm += __offsetof(struct ieee80211_tdma_param, tdma_slot);
*frm++ = ts->tdma_slot;
*frm++ = ts->tdma_slotcnt;
/* NB: convert units to fit in 16-bits */
slotlen = ts->tdma_slotlen / 100; /* 100us units */
ADDSHORT(frm, slotlen);
*frm++ = ts->tdma_bintval;
*frm++ = ts->tdma_inuse[0];
frm += 10; /* pad+timestamp */
return frm;
#undef ADDSHORT
}
#undef TDMA_OUI_BYTES
/*
* Update TDMA state at TBTT.
*/
void
ieee80211_tdma_update_beacon(struct ieee80211vap *vap,
struct ieee80211_beacon_offsets *bo)
{
struct ieee80211_tdma_state *ts = vap->iv_tdma;
KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
("not a tdma vap, caps 0x%x", vap->iv_caps));
if (isset(bo->bo_flags, IEEE80211_BEACON_TDMA)) {
(void) ieee80211_add_tdma(bo->bo_tdma, vap);
clrbit(bo->bo_flags, IEEE80211_BEACON_TDMA);
}
if (ts->tdma_slot != 0) /* only on master */
return;
if (ts->tdma_count <= 0) {
/*
* Time to update the mask of active/inuse stations.
* We track stations that we've received a beacon
* frame from and update this mask periodically.
* This allows us to miss a few beacons before marking
* a slot free for re-use.
*/
ts->tdma_inuse[0] = ts->tdma_active[0];
ts->tdma_active[0] = 0x01;
/* update next time 'round */
/* XXX use notify framework */
setbit(bo->bo_flags, IEEE80211_BEACON_TDMA);
/* NB: use s/w beacon miss threshold; may be too high */
ts->tdma_count = vap->iv_bmissthreshold-1;
} else
ts->tdma_count--;
}
static int
tdma_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_tdma_state *ts = vap->iv_tdma;
if ((vap->iv_caps & IEEE80211_C_TDMA) == 0)
return EOPNOTSUPP;
switch (ireq->i_type) {
case IEEE80211_IOC_TDMA_SLOT:
ireq->i_val = ts->tdma_slot;
break;
case IEEE80211_IOC_TDMA_SLOTCNT:
ireq->i_val = ts->tdma_slotcnt;
break;
case IEEE80211_IOC_TDMA_SLOTLEN:
ireq->i_val = ts->tdma_slotlen;
break;
case IEEE80211_IOC_TDMA_BINTERVAL:
ireq->i_val = ts->tdma_bintval;
break;
default:
return ENOSYS;
}
return 0;
}
IEEE80211_IOCTL_GET(tdma, tdma_ioctl_get80211);
static int
tdma_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_tdma_state *ts = vap->iv_tdma;
if ((vap->iv_caps & IEEE80211_C_TDMA) == 0)
return EOPNOTSUPP;
switch (ireq->i_type) {
case IEEE80211_IOC_TDMA_SLOT:
if (!(0 <= ireq->i_val && ireq->i_val <= ts->tdma_slotcnt))
return EINVAL;
if (ireq->i_val != ts->tdma_slot) {
ts->tdma_slot = ireq->i_val;
goto restart;
}
break;
case IEEE80211_IOC_TDMA_SLOTCNT:
if (!TDMA_SLOTCNT_VALID(ireq->i_val))
return EINVAL;
if (ireq->i_val != ts->tdma_slotcnt) {
ts->tdma_slotcnt = ireq->i_val;
goto restart;
}
break;
case IEEE80211_IOC_TDMA_SLOTLEN:
/*
* XXX
* 150 insures at least 1/8 TU
* 0xfffff is the max duration for bursting
* (implict by way of 16-bit data type for i_val)
*/
if (!TDMA_SLOTLEN_VALID(ireq->i_val))
return EINVAL;
if (ireq->i_val != ts->tdma_slotlen) {
ts->tdma_slotlen = ireq->i_val;
goto restart;
}
break;
case IEEE80211_IOC_TDMA_BINTERVAL:
if (!TDMA_BINTVAL_VALID(ireq->i_val))
return EINVAL;
if (ireq->i_val != ts->tdma_bintval) {
ts->tdma_bintval = ireq->i_val;
goto restart;
}
break;
default:
return ENOSYS;
}
return 0;
restart:
ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA);
return ERESTART;
}
IEEE80211_IOCTL_SET(tdma, tdma_ioctl_set80211);
@@ -0,0 +1,102 @@
/*-
* Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
* Copyright (c) 2007-2009 Intel Corporation
* 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_TDMA_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_TDMA_H_
/*
* TDMA-mode implementation definitions.
*/
#define TDMA_SUBTYPE_PARAM 0x01
#define TDMA_VERSION_V2 2
#define TDMA_VERSION TDMA_VERSION_V2
/* NB: we only support 2 right now but protocol handles up to 8 */
#define TDMA_MAXSLOTS 2 /* max slots/sta's */
#define TDMA_PARAM_LEN_V2 sizeof(struct ieee80211_tdma_param)
/*
* TDMA information element.
*/
struct ieee80211_tdma_param {
u_int8_t tdma_id; /* IEEE80211_ELEMID_VENDOR */
u_int8_t tdma_len;
u_int8_t tdma_oui[3]; /* TDMA_OUI */
u_int8_t tdma_type; /* TDMA_OUI_TYPE */
u_int8_t tdma_subtype; /* TDMA_SUBTYPE_PARAM */
u_int8_t tdma_version; /* spec revision */
u_int8_t tdma_slot; /* station slot # [0..7] */
u_int8_t tdma_slotcnt; /* bss slot count [1..8] */
u_int16_t tdma_slotlen; /* bss slot len (100us) */
u_int8_t tdma_bintval; /* beacon interval (superframes) */
u_int8_t tdma_inuse[1]; /* slot occupancy map */
u_int8_t tdma_pad[2];
u_int8_t tdma_tstamp[8]; /* timestamp from last beacon */
} __packed;
#ifdef _KERNEL
/*
* Implementation state.
*/
struct ieee80211_tdma_state {
u_int tdma_slotlen; /* bss slot length (us) */
uint8_t tdma_version; /* protocol version to use */
uint8_t tdma_slotcnt; /* bss slot count */
uint8_t tdma_bintval; /* beacon interval (slots) */
uint8_t tdma_slot; /* station slot # */
uint8_t tdma_inuse[1]; /* mask of slots in use */
uint8_t tdma_active[1]; /* mask of active slots */
int tdma_count; /* active/inuse countdown */
void *tdma_peer; /* peer station cookie */
struct timeval tdma_lastprint; /* time of last rate-limited printf */
int tdma_fails; /* fail count for rate-limiting */
/* parent method pointers */
int (*tdma_newstate)(struct ieee80211vap *, enum ieee80211_state,
int arg);
void (*tdma_recv_mgmt)(struct ieee80211_node *,
struct mbuf *, int, int, int);
void (*tdma_opdetach)(struct ieee80211vap *);
};
#define TDMA_UPDATE_SLOT 0x0001 /* tdma_slot changed */
#define TDMA_UPDATE_SLOTCNT 0x0002 /* tdma_slotcnt changed */
#define TDMA_UPDATE_SLOTLEN 0x0004 /* tdma_slotlen changed */
#define TDMA_UPDATE_BINTVAL 0x0008 /* tdma_bintval changed */
void ieee80211_tdma_vattach(struct ieee80211vap *);
int ieee80211_tdma_getslot(struct ieee80211vap *vap);
void ieee80211_parse_tdma(struct ieee80211_node *ni, const uint8_t *ie);
uint8_t *ieee80211_add_tdma(uint8_t *frm, struct ieee80211vap *vap);
struct ieee80211_beacon_offsets;
void ieee80211_tdma_update_beacon(struct ieee80211vap *vap,
struct ieee80211_beacon_offsets *bo);
#endif /* _KERNEL */
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_TDMA_H_ */
@@ -0,0 +1,914 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
* Copyright (c) 2009 Colin Günther, coling@gmx.de
* 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_VAR_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_VAR_H_
/*
* Definitions for IEEE 802.11 drivers.
*/
/* NB: portability glue must go first */
#ifdef __NetBSD__
#include <net80211/ieee80211_netbsd.h>
#elif __FreeBSD__
#include <net80211/ieee80211_freebsd.h>
#elif __linux__
#include <net80211/ieee80211_linux.h>
#elif __HAIKU__
#include <net80211/ieee80211_haiku.h>
#else
#error "No support for your operating system!"
#endif
#include <net80211/_ieee80211.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_ageq.h>
#include <net80211/ieee80211_crypto.h>
#include <net80211/ieee80211_dfs.h>
#include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */
#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_power.h>
#include <net80211/ieee80211_node.h>
#include <net80211/ieee80211_proto.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_scan.h>
#define IEEE80211_TXPOWER_MAX 100 /* .5 dbM (XXX units?) */
#define IEEE80211_TXPOWER_MIN 0 /* kill radio */
#define IEEE80211_DTIM_DEFAULT 1 /* default DTIM period */
#define IEEE80211_BINTVAL_DEFAULT 100 /* default beacon interval (TU's) */
#define IEEE80211_BMISS_MAX 2 /* maximum consecutive bmiss allowed */
#define IEEE80211_HWBMISS_DEFAULT 7 /* h/w bmiss threshold (beacons) */
#define IEEE80211_BGSCAN_INTVAL_MIN 15 /* min bg scan intvl (secs) */
#define IEEE80211_BGSCAN_INTVAL_DEFAULT (5*60) /* default bg scan intvl */
#define IEEE80211_BGSCAN_IDLE_MIN 100 /* min idle time (ms) */
#define IEEE80211_BGSCAN_IDLE_DEFAULT 250 /* default idle time (ms) */
#define IEEE80211_SCAN_VALID_MIN 10 /* min scan valid time (secs) */
#define IEEE80211_SCAN_VALID_DEFAULT 60 /* default scan valid time */
#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */
#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */
#define IEEE80211_FIXED_RATE_NONE 0xff
#define IEEE80211_TXMAX_DEFAULT 6 /* default ucast max retries */
#define IEEE80211_RTS_DEFAULT IEEE80211_RTS_MAX
#define IEEE80211_FRAG_DEFAULT IEEE80211_FRAG_MAX
#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024)
#define IEEE80211_TU_TO_MS(x) (((x) * 1024) / 1000)
#define IEEE80211_TU_TO_TICKS(x)(((x) * 1024 * hz) / (1000 * 1000))
/*
* 802.11 control state is split into a common portion that maps
* 1-1 to a physical device and one or more "Virtual AP's" (VAP)
* that are bound to an ieee80211com instance and share a single
* underlying device. Each VAP has a corresponding OS device
* entity through which traffic flows and that applications use
* for issuing ioctls, etc.
*/
/*
* Data common to one or more virtual AP's. State shared by
* the underlying device and the net80211 layer is exposed here;
* e.g. device-specific callbacks.
*/
struct ieee80211vap;
typedef void (*ieee80211vap_attach)(struct ieee80211vap *);
struct ieee80211_appie {
uint16_t ie_len; /* size of ie_data */
uint8_t ie_data[0]; /* user-specified IE's */
};
struct ieee80211_tdma_param;
struct ieee80211_rate_table;
struct ieee80211_tx_ampdu;
struct ieee80211_rx_ampdu;
struct ieee80211_superg;
struct ieee80211_frame;
struct ieee80211com {
struct ifnet *ic_ifp; /* associated device */
ieee80211_com_lock_t ic_comlock; /* state update lock */
TAILQ_HEAD(, ieee80211vap) ic_vaps; /* list of vap instances */
int ic_headroom; /* driver tx headroom needs */
enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */
enum ieee80211_opmode ic_opmode; /* operation mode */
struct ifmedia ic_media; /* interface media config */
struct callout ic_inact; /* inactivity processing */
struct taskqueue *ic_tq; /* deferred state thread */
struct task ic_parent_task; /* deferred parent processing */
struct task ic_promisc_task;/* deferred promisc update */
struct task ic_mcast_task; /* deferred mcast update */
struct task ic_chan_task; /* deferred channel change */
struct task ic_bmiss_task; /* deferred beacon miss hndlr */
uint32_t ic_flags; /* state flags */
uint32_t ic_flags_ext; /* extended state flags */
uint32_t ic_flags_ht; /* HT state flags */
uint32_t ic_flags_ven; /* vendor state flags */
uint32_t ic_caps; /* capabilities */
uint32_t ic_htcaps; /* HT capabilities */
uint32_t ic_cryptocaps; /* crypto capabilities */
uint8_t ic_modecaps[2]; /* set of mode capabilities */
uint8_t ic_promisc; /* vap's needing promisc mode */
uint8_t ic_allmulti; /* vap's needing all multicast*/
uint8_t ic_nrunning; /* vap's marked running */
uint8_t ic_curmode; /* current mode */
uint16_t ic_bintval; /* beacon interval */
uint16_t ic_lintval; /* listen interval */
uint16_t ic_holdover; /* PM hold over duration */
uint16_t ic_txpowlimit; /* global tx power limit */
struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
/*
* Channel state:
*
* ic_channels is the set of available channels for the device;
* it is setup by the driver
* ic_nchans is the number of valid entries in ic_channels
* ic_chan_avail is a bit vector of these channels used to check
* whether a channel is available w/o searching the channel table.
* ic_chan_active is a (potentially) constrained subset of
* ic_chan_avail that reflects any mode setting or user-specified
* limit on the set of channels to use/scan
* ic_curchan is the current channel the device is set to; it may
* be different from ic_bsschan when we are off-channel scanning
* or otherwise doing background work
* ic_bsschan is the channel selected for operation; it may
* be undefined (IEEE80211_CHAN_ANYC)
* ic_prevchan is a cached ``previous channel'' used to optimize
* lookups when switching back+forth between two channels
* (e.g. for dynamic turbo)
*/
int ic_nchans; /* # entries in ic_channels */
struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX];
uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES];
uint8_t ic_chan_active[IEEE80211_CHAN_BYTES];
uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES];
struct ieee80211_channel *ic_curchan; /* current channel */
const struct ieee80211_rate_table *ic_rt; /* table for ic_curchan */
struct ieee80211_channel *ic_bsschan; /* bss channel */
struct ieee80211_channel *ic_prevchan; /* previous channel */
struct ieee80211_regdomain ic_regdomain;/* regulatory data */
struct ieee80211_appie *ic_countryie; /* calculated country ie */
struct ieee80211_channel *ic_countryie_chan;
/* 802.11h/DFS state */
struct ieee80211_channel *ic_csa_newchan;/* channel for doing CSA */
short ic_csa_mode; /* mode for doing CSA */
short ic_csa_count; /* count for doing CSA */
struct ieee80211_dfs_state ic_dfs; /* DFS state */
struct ieee80211_scan_state *ic_scan; /* scan state */
long long ic_lastdata; /* time of last data frame */
long long ic_lastscan; /* time last scan completed */
/* NB: this is the union of all vap stations/neighbors */
int ic_max_keyix; /* max h/w key index */
struct ieee80211_node_table ic_sta; /* stations/neighbors */
struct ieee80211_ageq ic_stageq; /* frame staging queue */
uint32_t ic_hash_key; /* random key for mac hash */
/* XXX multi-bss: split out common/vap parts */
struct ieee80211_wme_state ic_wme; /* WME/WMM state */
/* XXX multi-bss: can per-vap be done/make sense? */
enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */
uint16_t ic_nonerpsta; /* # non-ERP stations */
uint16_t ic_longslotsta; /* # long slot time stations */
uint16_t ic_sta_assoc; /* stations associated */
uint16_t ic_ht_sta_assoc;/* HT stations associated */
uint16_t ic_ht40_sta_assoc;/* HT40 stations associated */
uint8_t ic_curhtprotmode;/* HTINFO bss state */
enum ieee80211_protmode ic_htprotmode; /* HT protection mode */
long long ic_lastnonerp; /* last time non-ERP sta noted*/
long long ic_lastnonht; /* last time non-HT sta noted */
/* optional state for Atheros SuperG protocol extensions */
struct ieee80211_superg *ic_superg;
/* radiotap handling */
struct ieee80211_radiotap_header *ic_th;/* tx radiotap headers */
void *ic_txchan; /* channel state in ic_th */
struct ieee80211_radiotap_header *ic_rh;/* rx radiotap headers */
void *ic_rxchan; /* channel state in ic_rh */
int ic_montaps; /* active monitor mode taps */
/* virtual ap create/delete */
struct ieee80211vap* (*ic_vap_create)(struct ieee80211com *,
const char name[IFNAMSIZ], int unit,
int opmode, int flags,
const uint8_t bssid[IEEE80211_ADDR_LEN],
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
void (*ic_vap_delete)(struct ieee80211vap *);
/* operating mode attachment */
ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX];
/* return hardware/radio capabilities */
void (*ic_getradiocaps)(struct ieee80211com *,
int, int *, struct ieee80211_channel []);
/* check and/or prepare regdomain state change */
int (*ic_setregdomain)(struct ieee80211com *,
struct ieee80211_regdomain *,
int, struct ieee80211_channel []);
/* send/recv 802.11 management frame */
int (*ic_send_mgmt)(struct ieee80211_node *,
int, int);
/* send raw 802.11 frame */
int (*ic_raw_xmit)(struct ieee80211_node *,
struct mbuf *,
const struct ieee80211_bpf_params *);
/* update device state for 802.11 slot time change */
void (*ic_updateslot)(struct ifnet *);
/* handle multicast state changes */
void (*ic_update_mcast)(struct ifnet *);
/* handle promiscuous mode changes */
void (*ic_update_promisc)(struct ifnet *);
/* new station association callback/notification */
void (*ic_newassoc)(struct ieee80211_node *, int);
/* TDMA update notification */
void (*ic_tdma_update)(struct ieee80211_node *,
const struct ieee80211_tdma_param *, int);
/* node state management */
struct ieee80211_node* (*ic_node_alloc)(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN]);
void (*ic_node_free)(struct ieee80211_node *);
void (*ic_node_cleanup)(struct ieee80211_node *);
void (*ic_node_age)(struct ieee80211_node *);
void (*ic_node_drain)(struct ieee80211_node *);
int8_t (*ic_node_getrssi)(const struct ieee80211_node*);
void (*ic_node_getsignal)(const struct ieee80211_node*,
int8_t *, int8_t *);
void (*ic_node_getmimoinfo)(
const struct ieee80211_node*,
struct ieee80211_mimo_info *);
/* scanning support */
void (*ic_scan_start)(struct ieee80211com *);
void (*ic_scan_end)(struct ieee80211com *);
void (*ic_set_channel)(struct ieee80211com *);
void (*ic_scan_curchan)(struct ieee80211_scan_state *,
unsigned long);
void (*ic_scan_mindwell)(struct ieee80211_scan_state *);
/*
* 802.11n ADDBA support. A simple/generic implementation
* of A-MPDU tx aggregation is provided; the driver may
* override these methods to provide their own support.
* A-MPDU rx re-ordering happens automatically if the
* driver passes out-of-order frames to ieee80211_input
* from an assocated HT station.
*/
int (*ic_recv_action)(struct ieee80211_node *,
const struct ieee80211_frame *,
const uint8_t *frm, const uint8_t *efrm);
int (*ic_send_action)(struct ieee80211_node *,
int category, int action, void *);
/* check if A-MPDU should be enabled this station+ac */
int (*ic_ampdu_enable)(struct ieee80211_node *,
struct ieee80211_tx_ampdu *);
/* start/stop doing A-MPDU tx aggregation for a station */
int (*ic_addba_request)(struct ieee80211_node *,
struct ieee80211_tx_ampdu *,
int dialogtoken, int baparamset,
int batimeout);
int (*ic_addba_response)(struct ieee80211_node *,
struct ieee80211_tx_ampdu *,
int status, int baparamset, int batimeout);
void (*ic_addba_stop)(struct ieee80211_node *,
struct ieee80211_tx_ampdu *);
/* BAR response received */
void (*ic_bar_response)(struct ieee80211_node *,
struct ieee80211_tx_ampdu *, int status);
/* start/stop doing A-MPDU rx processing for a station */
int (*ic_ampdu_rx_start)(struct ieee80211_node *,
struct ieee80211_rx_ampdu *, int baparamset,
int batimeout, int baseqctl);
void (*ic_ampdu_rx_stop)(struct ieee80211_node *,
struct ieee80211_rx_ampdu *);
uint64_t ic_spare[8];
};
struct ieee80211_aclator;
struct ieee80211_tdma_state;
struct ieee80211_mesh_state;
struct ieee80211_hwmp_state;
struct ieee80211vap {
struct ifmedia iv_media; /* interface media config */
struct ifnet *iv_ifp; /* associated device */
struct bpf_if *iv_rawbpf; /* packet filter structure */
struct sysctl_ctx_list *iv_sysctl; /* dynamic sysctl context */
struct sysctl_oid *iv_oid; /* net.wlan.X sysctl oid */
TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */
struct ieee80211com *iv_ic; /* back ptr to common state */
uint32_t iv_debug; /* debug msg flags */
struct ieee80211_stats iv_stats; /* statistics */
uint8_t iv_myaddr[IEEE80211_ADDR_LEN];
uint32_t iv_flags; /* state flags */
uint32_t iv_flags_ext; /* extended state flags */
uint32_t iv_flags_ht; /* HT state flags */
uint32_t iv_flags_ven; /* vendor state flags */
uint32_t iv_caps; /* capabilities */
uint32_t iv_htcaps; /* HT capabilities */
enum ieee80211_opmode iv_opmode; /* operation mode */
enum ieee80211_state iv_state; /* state machine state */
enum ieee80211_state iv_nstate; /* pending state */
int iv_nstate_arg; /* pending state arg */
struct task iv_nstate_task; /* deferred state processing */
struct task iv_swbmiss_task;/* deferred iv_bmiss call */
struct callout iv_mgtsend; /* mgmt frame response timer */
/* inactivity timer settings */
int iv_inact_init; /* setting for new station */
int iv_inact_auth; /* auth but not assoc setting */
int iv_inact_run; /* authorized setting */
int iv_inact_probe; /* inactive probe time */
int iv_des_nssid; /* # desired ssids */
struct ieee80211_scan_ssid iv_des_ssid[1];/* desired ssid table */
uint8_t iv_des_bssid[IEEE80211_ADDR_LEN];
struct ieee80211_channel *iv_des_chan; /* desired channel */
uint16_t iv_des_mode; /* desired mode */
int iv_nicknamelen; /* XXX junk */
uint8_t iv_nickname[IEEE80211_NWID_LEN];
u_int iv_bgscanidle; /* bg scan idle threshold */
u_int iv_bgscanintvl; /* bg scan min interval */
u_int iv_scanvalid; /* scan cache valid threshold */
u_int iv_scanreq_duration;
u_int iv_scanreq_mindwell;
u_int iv_scanreq_maxdwell;
uint16_t iv_scanreq_flags;/* held scan request params */
uint8_t iv_scanreq_nssid;
struct ieee80211_scan_ssid iv_scanreq_ssid[IEEE80211_SCAN_MAX_SSID];
/* sta-mode roaming state */
enum ieee80211_roamingmode iv_roaming; /* roaming mode */
struct ieee80211_roamparam iv_roamparms[IEEE80211_MODE_MAX];
uint8_t iv_bmissthreshold;
uint8_t iv_bmiss_count; /* current beacon miss count */
int iv_bmiss_max; /* max bmiss before scan */
uint16_t iv_swbmiss_count;/* beacons in last period */
uint16_t iv_swbmiss_period;/* s/w bmiss period */
struct callout iv_swbmiss; /* s/w beacon miss timer */
int iv_ampdu_rxmax; /* A-MPDU rx limit (bytes) */
int iv_ampdu_density;/* A-MPDU density */
int iv_ampdu_limit; /* A-MPDU tx limit (bytes) */
int iv_amsdu_limit; /* A-MSDU tx limit (bytes) */
u_int iv_ampdu_mintraffic[WME_NUM_AC];
uint32_t *iv_aid_bitmap; /* association id map */
uint16_t iv_max_aid;
uint16_t iv_sta_assoc; /* stations associated */
uint16_t iv_ps_sta; /* stations in power save */
uint16_t iv_ps_pending; /* ps sta's w/ pending frames */
uint16_t iv_txseq; /* mcast xmit seq# space */
uint16_t iv_tim_len; /* ic_tim_bitmap size (bytes) */
uint8_t *iv_tim_bitmap; /* power-save stations w/ data*/
uint8_t iv_dtim_period; /* DTIM period */
uint8_t iv_dtim_count; /* DTIM count from last bcn */
/* set/unset aid pwrsav state */
int iv_csa_count; /* count for doing CSA */
struct ieee80211_node *iv_bss; /* information for this node */
struct ieee80211_txparam iv_txparms[IEEE80211_MODE_MAX];
uint16_t iv_rtsthreshold;
uint16_t iv_fragthreshold;
int iv_inact_timer; /* inactivity timer wait */
/* application-specified IE's to attach to mgt frames */
struct ieee80211_appie *iv_appie_beacon;
struct ieee80211_appie *iv_appie_probereq;
struct ieee80211_appie *iv_appie_proberesp;
struct ieee80211_appie *iv_appie_assocreq;
struct ieee80211_appie *iv_appie_assocresp;
struct ieee80211_appie *iv_appie_wpa;
uint8_t *iv_wpa_ie;
uint8_t *iv_rsn_ie;
uint16_t iv_max_keyix; /* max h/w key index */
ieee80211_keyix iv_def_txkey; /* default/group tx key index */
struct ieee80211_key iv_nw_keys[IEEE80211_WEP_NKID];
int (*iv_key_alloc)(struct ieee80211vap *,
struct ieee80211_key *,
ieee80211_keyix *, ieee80211_keyix *);
int (*iv_key_delete)(struct ieee80211vap *,
const struct ieee80211_key *);
int (*iv_key_set)(struct ieee80211vap *,
const struct ieee80211_key *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
void (*iv_key_update_begin)(struct ieee80211vap *);
void (*iv_key_update_end)(struct ieee80211vap *);
const struct ieee80211_authenticator *iv_auth; /* authenticator glue */
void *iv_ec; /* private auth state */
const struct ieee80211_aclator *iv_acl; /* acl glue */
void *iv_as; /* private aclator state */
struct ieee80211_tdma_state *iv_tdma; /* tdma state */
struct ieee80211_mesh_state *iv_mesh; /* MBSS state */
struct ieee80211_hwmp_state *iv_hwmp; /* HWMP state */
/* operate-mode detach hook */
void (*iv_opdetach)(struct ieee80211vap *);
/* receive processing */
int (*iv_input)(struct ieee80211_node *,
struct mbuf *, int, int);
void (*iv_recv_mgmt)(struct ieee80211_node *,
struct mbuf *, int, int, int);
void (*iv_recv_ctl)(struct ieee80211_node *,
struct mbuf *, int);
void (*iv_deliver_data)(struct ieee80211vap *,
struct ieee80211_node *, struct mbuf *);
#if 0
/* send processing */
int (*iv_send_mgmt)(struct ieee80211_node *,
int, int);
#endif
/* beacon miss processing */
void (*iv_bmiss)(struct ieee80211vap *);
/* reset device state after 802.11 parameter/state change */
int (*iv_reset)(struct ieee80211vap *, u_long);
/* [schedule] beacon frame update */
void (*iv_update_beacon)(struct ieee80211vap *, int);
/* power save handling */
void (*iv_update_ps)(struct ieee80211vap *, int);
int (*iv_set_tim)(struct ieee80211_node *, int);
/* state machine processing */
int (*iv_newstate)(struct ieee80211vap *,
enum ieee80211_state, int);
/* 802.3 output method for raw frame xmit */
int (*iv_output)(struct ifnet *, struct mbuf *,
struct sockaddr *, struct route *);
uint64_t iv_spare[8];
};
#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN)
/* ic_flags/iv_flags */
#define IEEE80211_F_TURBOP 0x00000001 /* CONF: ATH Turbo enabled*/
#define IEEE80211_F_COMP 0x00000002 /* CONF: ATH comp enabled */
#define IEEE80211_F_FF 0x00000004 /* CONF: ATH FF enabled */
#define IEEE80211_F_BURST 0x00000008 /* CONF: bursting enabled */
/* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */
#define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */
#define IEEE80211_F_PUREG 0x00000020 /* CONF: 11g w/o 11b sta's */
#define IEEE80211_F_SCAN 0x00000080 /* STATUS: scanning */
#define IEEE80211_F_ASCAN 0x00000100 /* STATUS: active scan */
#define IEEE80211_F_SIBSS 0x00000200 /* STATUS: start IBSS */
/* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */
#define IEEE80211_F_SHSLOT 0x00000400 /* STATUS: use short slot time*/
#define IEEE80211_F_PMGTON 0x00000800 /* CONF: Power mgmt enable */
#define IEEE80211_F_DESBSSID 0x00001000 /* CONF: des_bssid is set */
#define IEEE80211_F_WME 0x00002000 /* CONF: enable WME use */
#define IEEE80211_F_BGSCAN 0x00004000 /* CONF: bg scan enabled (???)*/
#define IEEE80211_F_SWRETRY 0x00008000 /* CONF: sw tx retry enabled */
#define IEEE80211_F_TXPOW_FIXED 0x00010000 /* TX Power: fixed rate */
#define IEEE80211_F_IBSSON 0x00020000 /* CONF: IBSS creation enable */
#define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: use short preamble */
#define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */
#define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */
#define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/
#define IEEE80211_F_CSAPENDING 0x00400000 /* STATUS: chan switch pending*/
#define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */
#define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */
#define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */
#define IEEE80211_F_DROPUNENC 0x02000000 /* CONF: drop unencrypted */
#define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */
#define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */
#define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */
#define IEEE80211_F_PCF 0x20000000 /* CONF: PCF enabled */
#define IEEE80211_F_DOTH 0x40000000 /* CONF: 11h enabled */
#define IEEE80211_F_DWDS 0x80000000 /* CONF: Dynamic WDS enabled */
#define IEEE80211_F_BITS \
"\20\1TURBOP\2COMP\3FF\4BURST\5PRIVACY\6PUREG\10SCAN\11ASCAN\12SIBSS" \
"\13SHSLOT\14PMGTON\15DESBSSID\16WME\17BGSCAN\20SWRETRY\21TXPOW_FIXED" \
"\22IBSSON\23SHPREAMBLE\24DATAPAD\25USEPROT\26USERBARKER\27CSAPENDING" \
"\30WPA1\31WPA2\32DROPUNENC\33COUNTERM\34HIDESSID\35NOBRIDG\36PCF" \
"\37DOTH\40DWDS"
/* Atheros protocol-specific flags */
#define IEEE80211_F_ATHEROS \
(IEEE80211_F_FF | IEEE80211_F_COMP | IEEE80211_F_TURBOP)
/* Check if an Atheros capability was negotiated for use */
#define IEEE80211_ATH_CAP(vap, ni, bit) \
((vap)->iv_flags & (ni)->ni_ath_flags & (bit))
/* ic_flags_ext/iv_flags_ext */
#define IEEE80211_FEXT_INACT 0x00000002 /* CONF: sta inact handling */
#define IEEE80211_FEXT_SCANWAIT 0x00000004 /* STATUS: awaiting scan */
/* 0x00000006 reserved */
#define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: complete bgscan */
#define IEEE80211_FEXT_WPS 0x00000010 /* CONF: WPS enabled */
#define IEEE80211_FEXT_TSN 0x00000020 /* CONF: TSN enabled */
#define IEEE80211_FEXT_SCANREQ 0x00000040 /* STATUS: scan req params */
#define IEEE80211_FEXT_RESUME 0x00000080 /* STATUS: start on resume */
#define IEEE80211_FEXT_4ADDR 0x00000100 /* CONF: apply 4-addr encap */
#define IEEE80211_FEXT_NONERP_PR 0x00000200 /* STATUS: non-ERP sta present*/
#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */
#define IEEE80211_FEXT_DFS 0x00000800 /* CONF: DFS enabled */
#define IEEE80211_FEXT_DOTD 0x00001000 /* CONF: 11d enabled */
#define IEEE80211_FEXT_STATEWAIT 0x00002000 /* STATUS: awaiting state chg */
#define IEEE80211_FEXT_REINIT 0x00004000 /* STATUS: INIT state first */
#define IEEE80211_FEXT_BPF 0x00008000 /* STATUS: BPF tap present */
/* NB: immutable: should be set only when creating a vap */
#define IEEE80211_FEXT_WDSLEGACY 0x00010000 /* CONF: legacy WDS operation */
#define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/
#define IEEE80211_FEXT_BITS \
"\20\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\10RESUME" \
"\0114ADDR\12NONEPR_PR\13SWBMISS\14DFS\15DOTD\16STATEWAIT\17REINIT" \
"\20BPF\21WDSLEGACY\22PROBECHAN"
/* ic_flags_ht/iv_flags_ht */
#define IEEE80211_FHT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */
#define IEEE80211_FHT_GF 0x00040000 /* CONF: Greenfield enabled */
#define IEEE80211_FHT_HT 0x00080000 /* CONF: HT supported */
#define IEEE80211_FHT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */
#define IEEE80211_FHT_AMPDU_RX 0x00200000 /* CONF: A-MPDU rx supported */
#define IEEE80211_FHT_AMSDU_TX 0x00400000 /* CONF: A-MSDU tx supported */
#define IEEE80211_FHT_AMSDU_RX 0x00800000 /* CONF: A-MSDU rx supported */
#define IEEE80211_FHT_USEHT40 0x01000000 /* CONF: 20/40 use enabled */
#define IEEE80211_FHT_PUREN 0x02000000 /* CONF: 11n w/o legacy sta's */
#define IEEE80211_FHT_SHORTGI20 0x04000000 /* CONF: short GI in HT20 */
#define IEEE80211_FHT_SHORTGI40 0x08000000 /* CONF: short GI in HT40 */
#define IEEE80211_FHT_HTCOMPAT 0x10000000 /* CONF: HT vendor OUI's */
#define IEEE80211_FHT_RIFS 0x20000000 /* CONF: RIFS enabled */
#define IEEE80211_FHT_STBC_TX 0x40000000 /* CONF: STBC tx enabled */
#define IEEE80211_FHT_STBC_RX 0x80000000 /* CONF: STBC rx enabled */
#define IEEE80211_FHT_BITS \
"\20\1NONHT_PR" \
"\23GF\24HT\25AMDPU_TX\26AMPDU_TX" \
"\27AMSDU_TX\30AMSDU_RX\31USEHT40\32PUREN\33SHORTGI20\34SHORTGI40" \
"\35HTCOMPAT\36RIFS\37STBC_TX\40STBC_RX"
#define IEEE80211_FVEN_BITS "\20"
/* ic_caps/iv_caps: device driver capabilities */
/* 0x2e available */
#define IEEE80211_C_STA 0x00000001 /* CAPABILITY: STA available */
#define IEEE80211_C_8023ENCAP 0x00000002 /* CAPABILITY: 802.3 encap */
#define IEEE80211_C_FF 0x00000040 /* CAPABILITY: ATH FF avail */
#define IEEE80211_C_TURBOP 0x00000080 /* CAPABILITY: ATH Turbo avail*/
#define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */
#define IEEE80211_C_PMGT 0x00000200 /* CAPABILITY: Power mgmt */
#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */
#define IEEE80211_C_AHDEMO 0x00000800 /* CAPABILITY: Old Adhoc Demo */
#define IEEE80211_C_SWRETRY 0x00001000 /* CAPABILITY: sw tx retry */
#define IEEE80211_C_TXPMGT 0x00002000 /* CAPABILITY: tx power mgmt */
#define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */
#define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */
#define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */
#define IEEE80211_C_DFS 0x00020000 /* CAPABILITY: DFS/radar avail*/
#define IEEE80211_C_MBSS 0x00040000 /* CAPABILITY: MBSS available */
/* 0x7c0000 available */
#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */
#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */
#define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/
#define IEEE80211_C_BURST 0x02000000 /* CAPABILITY: frame bursting */
#define IEEE80211_C_WME 0x04000000 /* CAPABILITY: WME avail */
#define IEEE80211_C_WDS 0x08000000 /* CAPABILITY: 4-addr support */
/* 0x10000000 reserved */
#define IEEE80211_C_BGSCAN 0x20000000 /* CAPABILITY: bg scanning */
#define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */
#define IEEE80211_C_TDMA 0x80000000 /* CAPABILITY: TDMA avail */
/* XXX protection/barker? */
#define IEEE80211_C_OPMODE \
(IEEE80211_C_STA | IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | \
IEEE80211_C_AHDEMO | IEEE80211_C_MONITOR | IEEE80211_C_WDS | \
IEEE80211_C_TDMA | IEEE80211_C_MBSS)
#define IEEE80211_C_BITS \
"\20\1STA\002803ENCAP\7FF\10TURBOP\11IBSS\12PMGT" \
"\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
"\21MONITOR\22DFS\23MBSS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
"\37TXFRAG\40TDMA"
/*
* ic_htcaps/iv_htcaps: HT-specific device/driver capabilities
*
* NB: the low 16-bits are the 802.11 definitions, the upper
* 16-bits are used to define s/w/driver capabilities.
*/
#define IEEE80211_HTC_AMPDU 0x00010000 /* CAPABILITY: A-MPDU tx */
#define IEEE80211_HTC_AMSDU 0x00020000 /* CAPABILITY: A-MSDU tx */
/* NB: HT40 is implied by IEEE80211_HTCAP_CHWIDTH40 */
#define IEEE80211_HTC_HT 0x00040000 /* CAPABILITY: HT operation */
#define IEEE80211_HTC_SMPS 0x00080000 /* CAPABILITY: MIMO power save*/
#define IEEE80211_HTC_RIFS 0x00100000 /* CAPABILITY: RIFS support */
#define IEEE80211_C_HTCAP_BITS \
"\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
"\21AMPDU\22AMSDU\23HT\24SMPS\25RIFS"
void ieee80211_ifattach(struct ieee80211com *,
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
void ieee80211_ifdetach(struct ieee80211com *);
int ieee80211_vap_setup(struct ieee80211com *, struct ieee80211vap *,
const char name[IFNAMSIZ], int unit, int opmode, int flags,
const uint8_t bssid[IEEE80211_ADDR_LEN],
const uint8_t macaddr[IEEE80211_ADDR_LEN]);
int ieee80211_vap_attach(struct ieee80211vap *,
ifm_change_cb_t, ifm_stat_cb_t);
void ieee80211_vap_detach(struct ieee80211vap *);
const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic,
const struct ieee80211_channel *);
void ieee80211_announce(struct ieee80211com *);
void ieee80211_announce_channels(struct ieee80211com *);
void ieee80211_drain(struct ieee80211com *);
void ieee80211_media_init(struct ieee80211com *);
struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]);
int ieee80211_media_change(struct ifnet *);
void ieee80211_media_status(struct ifnet *, struct ifmediareq *);
int ieee80211_ioctl(struct ifnet *, u_long, caddr_t);
int ieee80211_rate2media(struct ieee80211com *, int,
enum ieee80211_phymode);
int ieee80211_media2rate(int);
int ieee80211_mhz2ieee(u_int, u_int);
int ieee80211_chan2ieee(struct ieee80211com *,
const struct ieee80211_channel *);
u_int ieee80211_ieee2mhz(u_int, u_int);
struct ieee80211_channel *ieee80211_find_channel(struct ieee80211com *,
int freq, int flags);
struct ieee80211_channel *ieee80211_find_channel_byieee(struct ieee80211com *,
int ieee, int flags);
int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode);
enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *);
uint32_t ieee80211_mac_hash(const struct ieee80211com *,
const uint8_t addr[IEEE80211_ADDR_LEN]);
void ieee80211_radiotap_attach(struct ieee80211com *,
struct ieee80211_radiotap_header *th, int tlen,
uint32_t tx_radiotap,
struct ieee80211_radiotap_header *rh, int rlen,
uint32_t rx_radiotap);
void ieee80211_radiotap_detach(struct ieee80211com *);
void ieee80211_radiotap_vattach(struct ieee80211vap *);
void ieee80211_radiotap_vdetach(struct ieee80211vap *);
void ieee80211_radiotap_chan_change(struct ieee80211com *);
void ieee80211_radiotap_tx(struct ieee80211vap *, struct mbuf *);
void ieee80211_radiotap_rx(struct ieee80211vap *, struct mbuf *);
void ieee80211_radiotap_rx_all(struct ieee80211com *, struct mbuf *);
static __inline int
ieee80211_radiotap_active(const struct ieee80211com *ic)
{
return (ic->ic_flags_ext & IEEE80211_FEXT_BPF) != 0;
}
static __inline int
ieee80211_radiotap_active_vap(const struct ieee80211vap *vap)
{
return (vap->iv_flags_ext & IEEE80211_FEXT_BPF) ||
vap->iv_ic->ic_montaps != 0;
}
/*
* Enqueue a task on the state thread.
*/
static __inline void
ieee80211_runtask(struct ieee80211com *ic, struct task *task)
{
taskqueue_enqueue(ic->ic_tq, task);
}
/*
* Wait for a queued task to complete.
*/
static __inline void
ieee80211_draintask(struct ieee80211com *ic, struct task *task)
{
taskqueue_drain(ic->ic_tq, task);
}
/*
* Key update synchronization methods. XXX should not be visible.
*/
static __inline void
ieee80211_key_update_begin(struct ieee80211vap *vap)
{
vap->iv_key_update_begin(vap);
}
static __inline void
ieee80211_key_update_end(struct ieee80211vap *vap)
{
vap->iv_key_update_end(vap);
}
/*
* XXX these need to be here for IEEE80211_F_DATAPAD
*/
/*
* Return the space occupied by the 802.11 header and any
* padding required by the driver. This works for a
* management or data frame.
*/
static __inline int
ieee80211_hdrspace(struct ieee80211com *ic, const void *data)
{
int size = ieee80211_hdrsize(data);
if (ic->ic_flags & IEEE80211_F_DATAPAD)
size = roundup(size, sizeof(uint32_t));
return size;
}
/*
* Like ieee80211_hdrspace, but handles any type of frame.
*/
static __inline int
ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data)
{
int size = ieee80211_anyhdrsize(data);
if (ic->ic_flags & IEEE80211_F_DATAPAD)
size = roundup(size, sizeof(uint32_t));
return size;
}
/*
* Notify a vap that beacon state has been updated.
*/
static __inline void
ieee80211_beacon_notify(struct ieee80211vap *vap, int what)
{
if (vap->iv_state == IEEE80211_S_RUN)
vap->iv_update_beacon(vap, what);
}
/*
* Calculate HT channel promotion flags for a channel.
* XXX belongs in ieee80211_ht.h but needs IEEE80211_FHT_*
*/
static __inline int
ieee80211_htchanflags(const struct ieee80211_channel *c)
{
return IEEE80211_IS_CHAN_HT40(c) ?
IEEE80211_FHT_HT | IEEE80211_FHT_USEHT40 :
IEEE80211_IS_CHAN_HT(c) ? IEEE80211_FHT_HT : 0;
}
/*
* Debugging facilities compiled in when IEEE80211_DEBUG is defined.
*
* The intent is that any problem in the net80211 layer can be
* diagnosed by inspecting the statistics (dumped by the wlanstats
* program) and/or the msgs generated by net80211. Messages are
* broken into functional classes and can be controlled with the
* wlandebug program. Certain of these msg groups are for facilities
* that are no longer part of net80211 (e.g. IEEE80211_MSG_DOT1XSM).
*/
#define IEEE80211_MSG_11N 0x80000000 /* 11n mode debug */
#define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */
#define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */
#define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */
#define IEEE80211_MSG_INPUT 0x08000000 /* input handling */
#define IEEE80211_MSG_XRATE 0x04000000 /* rate set handling */
#define IEEE80211_MSG_ELEMID 0x02000000 /* element id parsing */
#define IEEE80211_MSG_NODE 0x01000000 /* node handling */
#define IEEE80211_MSG_ASSOC 0x00800000 /* association handling */
#define IEEE80211_MSG_AUTH 0x00400000 /* authentication handling */
#define IEEE80211_MSG_SCAN 0x00200000 /* scanning */
#define IEEE80211_MSG_OUTPUT 0x00100000 /* output handling */
#define IEEE80211_MSG_STATE 0x00080000 /* state machine */
#define IEEE80211_MSG_POWER 0x00040000 /* power save handling */
#define IEEE80211_MSG_HWMP 0x00020000 /* hybrid mesh protocol */
#define IEEE80211_MSG_DOT1XSM 0x00010000 /* 802.1x state machine */
#define IEEE80211_MSG_RADIUS 0x00008000 /* 802.1x radius client */
#define IEEE80211_MSG_RADDUMP 0x00004000 /* dump 802.1x radius packets */
#define IEEE80211_MSG_MESH 0x00002000 /* mesh networking */
#define IEEE80211_MSG_WPA 0x00001000 /* WPA/RSN protocol */
#define IEEE80211_MSG_ACL 0x00000800 /* ACL handling */
#define IEEE80211_MSG_WME 0x00000400 /* WME protocol */
#define IEEE80211_MSG_SUPERG 0x00000200 /* Atheros SuperG protocol */
#define IEEE80211_MSG_DOTH 0x00000100 /* 802.11h support */
#define IEEE80211_MSG_INACT 0x00000080 /* inactivity handling */
#define IEEE80211_MSG_ROAM 0x00000040 /* sta-mode roaming */
#define IEEE80211_MSG_RATECTL 0x00000020 /* tx rate control */
#define IEEE80211_MSG_ACTION 0x00000010 /* action frame handling */
#define IEEE80211_MSG_WDS 0x00000008 /* WDS handling */
#define IEEE80211_MSG_IOCTL 0x00000004 /* ioctl handling */
#define IEEE80211_MSG_TDMA 0x00000002 /* TDMA handling */
#define IEEE80211_MSG_ANY 0xffffffff /* anything */
#define IEEE80211_MSG_BITS \
"\20\2TDMA\3IOCTL\4WDS\5ACTION\6RATECTL\7ROAM\10INACT\11DOTH\12SUPERG" \
"\13WME\14ACL\15WPA\16RADKEYS\17RADDUMP\20RADIUS\21DOT1XSM\22HWMP" \
"\23POWER\24STATE\25OUTPUT\26SCAN\27AUTH\30ASSOC\31NODE\32ELEMID" \
"\33XRATE\34INPUT\35CRYPTO\36DUPMPKTS\37DEBUG\04011N"
#ifdef IEEE80211_DEBUG
#define ieee80211_msg(_vap, _m) ((_vap)->iv_debug & (_m))
#define IEEE80211_DPRINTF(_vap, _m, _fmt, ...) do { \
if (ieee80211_msg(_vap, _m)) \
ieee80211_note(_vap, _fmt, __VA_ARGS__); \
} while (0)
#define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...) do { \
if (ieee80211_msg(_vap, _m)) \
ieee80211_note_mac(_vap, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\
} while (0)
#define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...) do { \
if (ieee80211_msg(_vap, _m)) \
ieee80211_note_mac(_vap, _mac, _fmt, __VA_ARGS__); \
} while (0)
#define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...) do { \
if (ieee80211_msg(_vap, _m)) \
ieee80211_note_frame(_vap, _wh, _fmt, __VA_ARGS__); \
} while (0)
void ieee80211_note(struct ieee80211vap *, const char *, ...);
void ieee80211_note_mac(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN], const char *, ...);
void ieee80211_note_frame(struct ieee80211vap *,
const struct ieee80211_frame *, const char *, ...);
#define ieee80211_msg_debug(_vap) \
((_vap)->iv_debug & IEEE80211_MSG_DEBUG)
#define ieee80211_msg_dumppkts(_vap) \
((_vap)->iv_debug & IEEE80211_MSG_DUMPPKTS)
#define ieee80211_msg_input(_vap) \
((_vap)->iv_debug & IEEE80211_MSG_INPUT)
#define ieee80211_msg_radius(_vap) \
((_vap)->iv_debug & IEEE80211_MSG_RADIUS)
#define ieee80211_msg_dumpradius(_vap) \
((_vap)->iv_debug & IEEE80211_MSG_RADDUMP)
#define ieee80211_msg_dumpradkeys(_vap) \
((_vap)->iv_debug & IEEE80211_MSG_RADKEYS)
#define ieee80211_msg_scan(_vap) \
((_vap)->iv_debug & IEEE80211_MSG_SCAN)
#define ieee80211_msg_assoc(_vap) \
((_vap)->iv_debug & IEEE80211_MSG_ASSOC)
/*
* Emit a debug message about discarding a frame or information
* element. One format is for extracting the mac address from
* the frame header; the other is for when a header is not
* available or otherwise appropriate.
*/
#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) do { \
if ((_vap)->iv_debug & (_m)) \
ieee80211_discard_frame(_vap, _wh, _type, _fmt, __VA_ARGS__);\
} while (0)
#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) do { \
if ((_vap)->iv_debug & (_m)) \
ieee80211_discard_ie(_vap, _wh, _type, _fmt, __VA_ARGS__);\
} while (0)
#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) do { \
if ((_vap)->iv_debug & (_m)) \
ieee80211_discard_mac(_vap, _mac, _type, _fmt, __VA_ARGS__);\
} while (0)
void ieee80211_discard_frame(struct ieee80211vap *,
const struct ieee80211_frame *, const char *type, const char *fmt, ...);
void ieee80211_discard_ie(struct ieee80211vap *,
const struct ieee80211_frame *, const char *type, const char *fmt, ...);
void ieee80211_discard_mac(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN], const char *type,
const char *fmt, ...);
#else
#define IEEE80211_DPRINTF(_vap, _m, _fmt, ...)
#define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...)
#define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...)
#define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...)
#define ieee80211_msg_dumppkts(_vap) 0
#define ieee80211_msg(_vap, _m) 0
#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...)
#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...)
#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...)
#endif
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_VAR_H_ */
@@ -0,0 +1,788 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
#ifdef __FreeBSD__
__FBSDID("$FreeBSD$");
#endif
/*
* IEEE 802.11 WDS mode support.
*/
#include "opt_inet.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/endian.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_llc.h>
#include <net/ethernet.h>
#include <net/bpf.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_wds.h>
#include <net80211/ieee80211_input.h>
#ifdef IEEE80211_SUPPORT_SUPERG
#include <net80211/ieee80211_superg.h>
#endif
static void wds_vattach(struct ieee80211vap *);
static int wds_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static int wds_input(struct ieee80211_node *ni, struct mbuf *m, int, int);
static void wds_recv_mgmt(struct ieee80211_node *, struct mbuf *,
int subtype, int, int);
void
ieee80211_wds_attach(struct ieee80211com *ic)
{
ic->ic_vattach[IEEE80211_M_WDS] = wds_vattach;
}
void
ieee80211_wds_detach(struct ieee80211com *ic)
{
}
static void
wds_vdetach(struct ieee80211vap *vap)
{
if (vap->iv_bss != NULL) {
/* XXX locking? */
if (vap->iv_bss->ni_wdsvap == vap)
vap->iv_bss->ni_wdsvap = NULL;
}
}
static void
wds_vattach(struct ieee80211vap *vap)
{
vap->iv_newstate = wds_newstate;
vap->iv_input = wds_input;
vap->iv_recv_mgmt = wds_recv_mgmt;
vap->iv_opdetach = wds_vdetach;
}
static void
wds_flush(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
struct mbuf *m, *next;
int8_t rssi, nf;
m = ieee80211_ageq_remove(&ic->ic_stageq,
(void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr));
if (m == NULL)
return;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_WDS, ni,
"%s", "flush wds queue");
ic->ic_node_getsignal(ni, &rssi, &nf);
for (; m != NULL; m = next) {
next = m->m_nextpkt;
m->m_nextpkt = NULL;
ieee80211_input(ni, m, rssi, nf);
}
}
static int
ieee80211_create_wds(struct ieee80211vap *vap, struct ieee80211_channel *chan)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node_table *nt = &ic->ic_sta;
struct ieee80211_node *ni, *obss;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
"%s: creating link to %s on channel %u\n", __func__,
ether_sprintf(vap->iv_des_bssid), ieee80211_chan2ieee(ic, chan));
/* NB: vap create must specify the bssid for the link */
KASSERT(vap->iv_flags & IEEE80211_F_DESBSSID, ("no bssid"));
/* NB: we should only be called on RUN transition */
KASSERT(vap->iv_state == IEEE80211_S_RUN, ("!RUN state"));
if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
/*
* Dynamic/non-legacy WDS. Reference the associated
* station specified by the desired bssid setup at vap
* create. Point ni_wdsvap at the WDS vap so 4-address
* frames received through the associated AP vap will
* be dispatched upward (e.g. to a bridge) as though
* they arrived on the WDS vap.
*/
IEEE80211_NODE_LOCK(nt);
obss = NULL;
ni = ieee80211_find_node_locked(&ic->ic_sta, vap->iv_des_bssid);
if (ni == NULL) {
/*
* Node went away before we could hookup. This
* should be ok; no traffic will flow and a leave
* event will be dispatched that should cause
* the vap to be destroyed.
*/
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
"%s: station %s went away\n",
__func__, ether_sprintf(vap->iv_des_bssid));
/* XXX stat? */
} else if (ni->ni_wdsvap != NULL) {
/*
* Node already setup with a WDS vap; we cannot
* allow multiple references so disallow. If
* ni_wdsvap points at us that's ok; we should
* do nothing anyway.
*/
/* XXX printf instead? */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
"%s: station %s in use with %s\n",
__func__, ether_sprintf(vap->iv_des_bssid),
ni->ni_wdsvap->iv_ifp->if_xname);
/* XXX stat? */
} else {
/*
* Committed to new node, setup state.
*/
obss = vap->iv_bss;
vap->iv_bss = ni;
ni->ni_wdsvap = vap;
}
IEEE80211_NODE_UNLOCK(nt);
if (obss != NULL) {
/* NB: deferred to avoid recursive lock */
ieee80211_free_node(obss);
}
} else {
/*
* Legacy WDS vap setup.
*/
/*
* The far end does not associate so we just create
* create a new node and install it as the vap's
* bss node. We must simulate an association and
* authorize the port for traffic to flow.
* XXX check if node already in sta table?
*/
ni = ieee80211_node_create_wds(vap, vap->iv_des_bssid, chan);
if (ni != NULL) {
obss = vap->iv_bss;
vap->iv_bss = ieee80211_ref_node(ni);
ni->ni_flags |= IEEE80211_NODE_AREF;
if (obss != NULL)
ieee80211_free_node(obss);
/* give driver a chance to setup state like ni_txrate */
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, 1);
/* tell the authenticator about new station */
if (vap->iv_auth->ia_node_join != NULL)
vap->iv_auth->ia_node_join(ni);
if (ni->ni_authmode != IEEE80211_AUTH_8021X)
ieee80211_node_authorize(ni);
ieee80211_notify_node_join(ni, 1 /*newassoc*/);
/* XXX inject l2uf frame */
}
}
/*
* Flush any pending frames now that were setup.
*/
if (ni != NULL)
wds_flush(ni);
return (ni == NULL ? ENOENT : 0);
}
/*
* Propagate multicast frames of an ap vap to all DWDS links.
* The caller is assumed to have verified this frame is multicast.
*/
void
ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
{
struct ieee80211com *ic = vap0->iv_ic;
struct ifnet *parent = ic->ic_ifp;
const struct ether_header *eh = mtod(m, const struct ether_header *);
struct ieee80211_node *ni;
struct ieee80211vap *vap;
struct ifnet *ifp;
struct mbuf *mcopy;
int err;
KASSERT(ETHER_IS_MULTICAST(eh->ether_dhost),
("%s not mcast", ether_sprintf(eh->ether_dhost)));
/* XXX locking */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
/* only DWDS vaps are interesting */
if (vap->iv_opmode != IEEE80211_M_WDS ||
(vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))
continue;
/* if it came in this interface, don't send it back out */
ifp = vap->iv_ifp;
if (ifp == m->m_pkthdr.rcvif)
continue;
/*
* Duplicate the frame and send it.
*/
mcopy = m_copypacket(m, M_DONTWAIT);
if (mcopy == NULL) {
ifp->if_oerrors++;
/* XXX stat + msg */
continue;
}
ni = ieee80211_find_txnode(vap, eh->ether_dhost);
if (ni == NULL) {
/* NB: ieee80211_find_txnode does stat+msg */
ifp->if_oerrors++;
m_freem(mcopy);
continue;
}
/* calculate priority so drivers can find the tx queue */
if (ieee80211_classify(ni, mcopy)) {
IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_OUTPUT | IEEE80211_MSG_WDS,
eh->ether_dhost, NULL,
"%s", "classification failure");
vap->iv_stats.is_tx_classify++;
ifp->if_oerrors++;
m_freem(mcopy);
ieee80211_free_node(ni);
continue;
}
BPF_MTAP(ifp, m); /* 802.3 tx */
/*
* Encapsulate the packet in prep for transmission.
*/
mcopy = ieee80211_encap(vap, ni, mcopy);
if (mcopy == NULL) {
/* NB: stat+msg handled in ieee80211_encap */
ieee80211_free_node(ni);
continue;
}
mcopy->m_flags |= M_MCAST;
mcopy->m_pkthdr.rcvif = (void *) ni;
err = parent->if_transmit(parent, mcopy);
if (err) {
/* NB: IFQ_HANDOFF reclaims mbuf */
ifp->if_oerrors++;
ieee80211_free_node(ni);
} else
ifp->if_opackets++;
}
}
/*
* Handle DWDS discovery on receipt of a 4-address frame in
* ap mode. Queue the frame and post an event for someone
* to plumb the necessary WDS vap for this station. Frames
* received prior to the vap set running will then be reprocessed
* as if they were just received.
*/
void
ieee80211_dwds_discover(struct ieee80211_node *ni, struct mbuf *m)
{
struct ieee80211com *ic = ni->ni_ic;
/*
* Save the frame with an aging interval 4 times
* the listen interval specified by the station.
* Frames that sit around too long are reclaimed
* using this information.
* XXX handle overflow?
* XXX per/vap beacon interval?
*/
m->m_pkthdr.rcvif = (void *)(uintptr_t)
ieee80211_mac_hash(ic, ni->ni_macaddr);
(void) ieee80211_ageq_append(&ic->ic_stageq, m,
((ni->ni_intval * ic->ic_lintval) << 2) / 1024);
ieee80211_notify_wds_discover(ni);
}
/*
* IEEE80211_M_WDS vap state machine handler.
*/
static int
wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
enum ieee80211_state ostate;
int error;
IEEE80211_LOCK_ASSERT(ic);
ostate = vap->iv_state;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
vap->iv_state = nstate; /* state transition */
callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */
if (ostate != IEEE80211_S_SCAN)
ieee80211_cancel_scan(vap); /* background scan */
ni = vap->iv_bss; /* NB: no reference held */
error = 0;
switch (nstate) {
case IEEE80211_S_INIT:
switch (ostate) {
case IEEE80211_S_SCAN:
ieee80211_cancel_scan(vap);
break;
default:
break;
}
if (ostate != IEEE80211_S_INIT) {
/* NB: optimize INIT -> INIT case */
ieee80211_reset_bss(vap);
}
break;
case IEEE80211_S_SCAN:
switch (ostate) {
case IEEE80211_S_INIT:
ieee80211_check_scan_current(vap);
break;
default:
break;
}
break;
case IEEE80211_S_RUN:
if (ostate == IEEE80211_S_INIT) {
/*
* Already have a channel; bypass the scan
* and startup immediately.
*/
error = ieee80211_create_wds(vap, ic->ic_curchan);
}
break;
default:
break;
}
return error;
}
/*
* 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 iv_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.
*/
static int
wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
{
#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
#define HAS_SEQ(type) ((type & 0x4) == 0)
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ifnet *ifp = vap->iv_ifp;
struct ieee80211_frame *wh;
struct ieee80211_key *key;
struct ether_header *eh;
int hdrspace, need_tap;
uint8_t dir, type, subtype, qos;
uint16_t rxseq;
if (m->m_flags & M_AMPDU_MPDU) {
/*
* Fastpath for A-MPDU reorder q resubmission. Frames
* w/ M_AMPDU_MPDU 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_MPDU flag and we can bypass most of
* the normal processing.
*/
wh = mtod(m, struct ieee80211_frame *);
type = IEEE80211_FC0_TYPE_DATA;
dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
subtype = IEEE80211_FC0_SUBTYPE_QOS;
hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
goto resubmit_ampdu;
}
KASSERT(ni != NULL, ("null node"));
need_tap = 1; /* mbuf need to be tapped. */
type = -1; /* undefined */
if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, NULL,
"too short (1): len %u", m->m_pkthdr.len);
vap->iv_stats.is_rx_tooshort++;
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.
*/
wh = mtod(m, struct ieee80211_frame *);
if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
IEEE80211_FC0_VERSION_0) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x",
wh->i_fc[0], wh->i_fc[1]);
vap->iv_stats.is_rx_badversion++;
goto err;
}
dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
/* NB: WDS vap's do not scan */
if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_addr4)) {
IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_ANY, ni->ni_macaddr, NULL,
"too short (3): len %u", m->m_pkthdr.len);
vap->iv_stats.is_rx_tooshort++;
goto out;
}
/* NB: the TA is implicitly verified by finding the wds peer node */
if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) &&
!IEEE80211_ADDR_EQ(wh->i_addr1, ifp->if_broadcastaddr)) {
/* not interested in */
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
wh->i_addr1, NULL, "%s", "not to bss");
vap->iv_stats.is_rx_wrongbss++;
goto out;
}
IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
ni->ni_noise = nf;
if (HAS_SEQ(type)) {
uint8_t tid = ieee80211_gettid(wh);
if (IEEE80211_QOS_HAS_SEQ(wh) &&
TID_TO_WME_AC(tid) >= WME_AC_VI)
ic->ic_wme.wme_hipri_traffic++;
rxseq = le16toh(*(uint16_t *)wh->i_seq);
if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
(wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
/* duplicate, discard */
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
wh->i_addr1, "duplicate",
"seqno <%u,%u> fragno <%u,%u> tid %u",
rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
ni->ni_rxseqs[tid] >> IEEE80211_SEQ_SEQ_SHIFT,
rxseq & IEEE80211_SEQ_FRAG_MASK,
ni->ni_rxseqs[tid] & IEEE80211_SEQ_FRAG_MASK,
tid);
vap->iv_stats.is_rx_dup++;
IEEE80211_NODE_STAT(ni, rx_dup);
goto out;
}
ni->ni_rxseqs[tid] = rxseq;
}
switch (type) {
case IEEE80211_FC0_TYPE_DATA:
hdrspace = ieee80211_hdrspace(ic, wh);
if (m->m_len < hdrspace &&
(m = m_pullup(m, hdrspace)) == NULL) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, NULL,
"data too short: expecting %u", hdrspace);
vap->iv_stats.is_rx_tooshort++;
goto out; /* XXX */
}
if (dir != IEEE80211_FC1_DIR_DSTODS) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, "data", "incorrect dir 0x%x", dir);
vap->iv_stats.is_rx_wrongdir++;
goto out;
}
/*
* Only legacy WDS traffic should take this path.
*/
if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, "data", "%s", "not legacy wds");
vap->iv_stats.is_rx_wrongdir++;/*XXX*/
goto out;
}
if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
ni->ni_inact = ni->ni_inact_reload;
/*
* Handle 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.
*/
if ((m->m_flags & M_AMPDU) &&
ieee80211_ampdu_reorder(ni, m) != 0) {
m = NULL;
goto out;
}
resubmit_ampdu:
/*
* Handle privacy requirements. Note that we
* must not be preempted from here until after
* we (potentially) call ieee80211_crypto_demic;
* otherwise we may violate assumptions in the
* crypto cipher modules used to do delayed update
* of replay sequence numbers.
*/
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
/*
* Discard encrypted frames when privacy is off.
*/
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, "WEP", "%s", "PRIVACY off");
vap->iv_stats.is_rx_noprivacy++;
IEEE80211_NODE_STAT(ni, rx_noprivacy);
goto out;
}
key = ieee80211_crypto_decap(ni, m, hdrspace);
if (key == NULL) {
/* NB: stats+msgs handled in crypto_decap */
IEEE80211_NODE_STAT(ni, rx_wepfail);
goto out;
}
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
} else {
/* XXX M_WEP and IEEE80211_F_PRIVACY */
key = NULL;
}
/*
* Save QoS bits for use below--before we strip the header.
*/
if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
((struct ieee80211_qosframe *)wh)->i_qos[0];
} else
qos = 0;
/*
* Next up, any fragmentation.
*/
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
m = ieee80211_defrag(ni, m, hdrspace);
if (m == NULL) {
/* Fragment dropped or frame not complete yet */
goto out;
}
}
wh = NULL; /* no longer valid, catch any uses */
/*
* Next strip any MSDU crypto bits.
*/
if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
ni->ni_macaddr, "data", "%s", "demic error");
vap->iv_stats.is_rx_demicfail++;
IEEE80211_NODE_STAT(ni, rx_demicfail);
goto out;
}
/* copy to listener after decrypt */
if (ieee80211_radiotap_active_vap(vap))
ieee80211_radiotap_rx(vap, m);
need_tap = 0;
/*
* Finally, strip the 802.11 header.
*/
m = ieee80211_decap(vap, m, hdrspace);
if (m == NULL) {
/* XXX mask bit to check for both */
/* don't count Null data frames as errors */
if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
goto out;
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
ni->ni_macaddr, "data", "%s", "decap error");
vap->iv_stats.is_rx_decap++;
IEEE80211_NODE_STAT(ni, rx_decap);
goto err;
}
eh = mtod(m, struct ether_header *);
if (!ieee80211_node_is_authorized(ni)) {
/*
* Deny any non-PAE frames received prior to
* authorization. For open/shared-key
* authentication the port is mark authorized
* after authentication completes. For 802.1x
* the port is not marked authorized by the
* authenticator until the handshake has completed.
*/
if (eh->ether_type != htons(ETHERTYPE_PAE)) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
eh->ether_shost, "data",
"unauthorized port: ether type 0x%x len %u",
eh->ether_type, m->m_pkthdr.len);
vap->iv_stats.is_rx_unauth++;
IEEE80211_NODE_STAT(ni, rx_unauth);
goto err;
}
} else {
/*
* When denying unencrypted frames, discard
* any non-PAE frames received without encryption.
*/
if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
(key == NULL && (m->m_flags & M_WEP) == 0) &&
eh->ether_type != htons(ETHERTYPE_PAE)) {
/*
* Drop unencrypted frames.
*/
vap->iv_stats.is_rx_unencrypted++;
IEEE80211_NODE_STAT(ni, rx_unencrypted);
goto out;
}
}
/* XXX require HT? */
if (qos & IEEE80211_QOS_AMSDU) {
m = ieee80211_decap_amsdu(ni, m);
if (m == NULL)
return IEEE80211_FC0_TYPE_DATA;
} else {
#ifdef IEEE80211_SUPPORT_SUPERG
m = ieee80211_decap_fastframe(vap, ni, m);
if (m == NULL)
return IEEE80211_FC0_TYPE_DATA;
#endif
}
ieee80211_deliver_data(vap, ni, m);
return IEEE80211_FC0_TYPE_DATA;
case IEEE80211_FC0_TYPE_MGT:
vap->iv_stats.is_rx_mgmt++;
IEEE80211_NODE_STAT(ni, rx_mgmt);
if (dir != IEEE80211_FC1_DIR_NODS) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, "data", "incorrect dir 0x%x", dir);
vap->iv_stats.is_rx_wrongdir++;
goto err;
}
if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "mgt", "too short: len %u",
m->m_pkthdr.len);
vap->iv_stats.is_rx_tooshort++;
goto out;
}
#ifdef IEEE80211_DEBUG
if (ieee80211_msg_debug(vap) || ieee80211_msg_dumppkts(vap)) {
if_printf(ifp, "received %s from %s rssi %d\n",
ieee80211_mgt_subtype_name[subtype >>
IEEE80211_FC0_SUBTYPE_SHIFT],
ether_sprintf(wh->i_addr2), rssi);
}
#endif
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, NULL, "%s", "WEP set but not permitted");
vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
goto out;
}
vap->iv_recv_mgmt(ni, m, subtype, rssi, nf);
goto out;
case IEEE80211_FC0_TYPE_CTL:
vap->iv_stats.is_rx_ctl++;
IEEE80211_NODE_STAT(ni, rx_ctrl);
goto out;
default:
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
wh, "bad", "frame type 0x%x", type);
/* should not come here */
break;
}
err:
ifp->if_ierrors++;
out:
if (m != NULL) {
if (need_tap && ieee80211_radiotap_active_vap(vap))
ieee80211_radiotap_rx(vap, m);
m_freem(m);
}
return type;
#undef SEQ_LEQ
}
static void
wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
int subtype, int rssi, int nf)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
u_int8_t *frm, *efrm;
wh = mtod(m0, struct ieee80211_frame *);
frm = (u_int8_t *)&wh[1];
efrm = mtod(m0, u_int8_t *) + m0->m_len;
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_DEAUTH:
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
case IEEE80211_FC0_SUBTYPE_BEACON:
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
case IEEE80211_FC0_SUBTYPE_AUTH:
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_DISASSOC:
vap->iv_stats.is_rx_mgtdiscard++;
break;
case IEEE80211_FC0_SUBTYPE_ACTION:
if (vap->iv_state != IEEE80211_S_RUN ||
IEEE80211_IS_MULTICAST(wh->i_addr1)) {
vap->iv_stats.is_rx_mgtdiscard++;
break;
}
ni->ni_inact = ni->ni_inact_reload;
if (ieee80211_parse_action(ni, m0) == 0)
ic->ic_recv_action(ni, wh, frm, efrm);
break;
default:
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
wh, "mgt", "subtype 0x%x not handled", subtype);
vap->iv_stats.is_rx_badsubtype++;
break;
}
}
@@ -0,0 +1,39 @@
/*-
* Copyright (c) 2007-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _FBSD_COMPAT_NET80211_IEEE80211_WDS_H_
#define _FBSD_COMPAT_NET80211_IEEE80211_WDS_H_
/*
* WDS implementation definitions.
*/
void ieee80211_wds_attach(struct ieee80211com *);
void ieee80211_wds_detach(struct ieee80211com *);
void ieee80211_dwds_mcast(struct ieee80211vap *, struct mbuf *);
void ieee80211_dwds_discover(struct ieee80211_node *, struct mbuf *);
int ieee80211_node_wdsq_age(struct ieee80211_node *);
#endif /* _FBSD_COMPAT_NET80211_IEEE80211_WDS_H_ */
@@ -0,0 +1,76 @@
/*-
* Copyright (c) 2004 Video54 Technologies, Inc.
* Copyright (c) 2004-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
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* External authenticator placeholder module.
*
* This support is optional; it is only used when the 802.11 layer's
* authentication mode is set to use 802.1x or WPA is enabled separately
* (for WPA-PSK). If compiled as a module this code does not need
* to be present unless 802.1x/WPA is in use.
*
* The authenticator hooks into the 802.11 layer. At present we use none
* of the available callbacks--the user mode authenticator process works
* entirely from messages about stations joining and leaving.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net/route.h>
#include <net80211/ieee80211_var.h>
/* XXX number of references from net80211 layer; needed for module code */
static int nrefs = 0;
/*
* One module handles everything for now. May want
* to split things up for embedded applications.
*/
static const struct ieee80211_authenticator xauth = {
.ia_name = "external",
.ia_attach = NULL,
.ia_detach = NULL,
.ia_node_join = NULL,
.ia_node_leave = NULL,
};
IEEE80211_AUTH_MODULE(xauth, 1);
IEEE80211_AUTH_ALG(x8021x, IEEE80211_AUTH_8021X, xauth);
IEEE80211_AUTH_ALG(wpa, IEEE80211_AUTH_WPA, xauth);
@@ -0,0 +1,6 @@
#ifndef _FBSD_COMPAT_NET80211_OPT_INET_H_
#define _FBSD_COMPAT_NET80211_OPT_INET_H_
#define INET 1
#endif /* _FBSD_COMPAT_NET80211_OPT_INET_H_ */
@@ -0,0 +1,6 @@
#ifndef _FBSD_COMPAT_NET80211_OPT_INET6_H_
#define _FBSD_COMPAT_NET80211_OPT_INET6_H_
//#define INET6 1
#endif /* _FBSD_COMPAT_NET80211_OPT_INET6_H_ */
@@ -0,0 +1,6 @@
#ifndef _FBSD_COMPAT_NET80211_OPT_IPX_H_
#define _FBSD_COMPAT_NET80211_OPT_IPX_H_
//define IPX 1
#endif /* _FBSD_COMPAT_NET80211_OPT_IPX_H_ */
@@ -0,0 +1,8 @@
#ifndef _FBSD_COMPAT_NET80211_OPT_WLAN_H_
#define _FBSD_COMPAT_NET80211_OPT_WLAN_H_
#define IEEE80211_DEBUG 1
//#define IEEE80211_AMDPU_AGE 1
//#define IEEE80211_SUPPORT_MESH 1
#endif /* _FBSD_COMPAT_NET80211_OPT_WLAN_H_ */