* 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:
@@ -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 ;
|
||||
|
||||
@@ -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 = ®->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, ®->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, ®->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, ¶m, 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_ */
|
||||
Reference in New Issue
Block a user