diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-10-09 22:42:09 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-10-10 09:06:58 +0200 |
commit | bceabc95c1c85d793200446fa85f1ddc6313ea29 (patch) | |
tree | 973c8bd8deca9fd69913f2895cc91e0e6114d46c /freebsd/sys/net80211 | |
parent | Add FreeBSD sources as a submodule (diff) | |
download | rtems-libbsd-bceabc95c1c85d793200446fa85f1ddc6313ea29.tar.bz2 |
Move files to match FreeBSD layout
Diffstat (limited to 'freebsd/sys/net80211')
67 files changed, 47199 insertions, 0 deletions
diff --git a/freebsd/sys/net80211/_ieee80211.h b/freebsd/sys/net80211/_ieee80211.h new file mode 100644 index 00000000..764e5cf1 --- /dev/null +++ b/freebsd/sys/net80211/_ieee80211.h @@ -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 _NET80211__IEEE80211_HH_ +#define _NET80211__IEEE80211_HH_ + +/* + * 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 /* _NET80211__IEEE80211_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211.c b/freebsd/sys/net80211/ieee80211.c new file mode 100644 index 00000000..8770d452 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211.c @@ -0,0 +1,1638 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 generic handler + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_dl.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_types.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_regdomain.h> +#ifdef IEEE80211_SUPPORT_SUPERG +#include <freebsd/net80211/ieee80211_superg.h> +#endif +#include <freebsd/net80211/ieee80211_ratectl.h> + +#include <freebsd/net/bpf.h> + +const char *ieee80211_phymode_name[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = "auto", + [IEEE80211_MODE_11A] = "11a", + [IEEE80211_MODE_11B] = "11b", + [IEEE80211_MODE_11G] = "11g", + [IEEE80211_MODE_FH] = "FH", + [IEEE80211_MODE_TURBO_A] = "turboA", + [IEEE80211_MODE_TURBO_G] = "turboG", + [IEEE80211_MODE_STURBO_A] = "sturboA", + [IEEE80211_MODE_HALF] = "half", + [IEEE80211_MODE_QUARTER] = "quarter", + [IEEE80211_MODE_11NA] = "11na", + [IEEE80211_MODE_11NG] = "11ng", +}; +/* map ieee80211_opmode to the corresponding capability bit */ +const int ieee80211_opcap[IEEE80211_OPMODE_MAX] = { + [IEEE80211_M_IBSS] = IEEE80211_C_IBSS, + [IEEE80211_M_WDS] = IEEE80211_C_WDS, + [IEEE80211_M_STA] = IEEE80211_C_STA, + [IEEE80211_M_AHDEMO] = IEEE80211_C_AHDEMO, + [IEEE80211_M_HOSTAP] = IEEE80211_C_HOSTAP, + [IEEE80211_M_MONITOR] = IEEE80211_C_MONITOR, +#ifdef IEEE80211_SUPPORT_MESH + [IEEE80211_M_MBSS] = IEEE80211_C_MBSS, +#endif +}; + +static const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag); +static void ieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag); +static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); +static int ieee80211_media_setup(struct ieee80211com *ic, + struct ifmedia *media, int caps, int addsta, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); +static void ieee80211com_media_status(struct ifnet *, struct ifmediareq *); +static int ieee80211com_media_change(struct ifnet *); +static int media_status(enum ieee80211_opmode, + const struct ieee80211_channel *); + +MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); + +/* + * Default supported rates for 802.11 operation (in IEEE .5Mb units). + */ +#define B(r) ((r) | IEEE80211_RATE_BASIC) +static const struct ieee80211_rateset ieee80211_rateset_11a = + { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; +static const struct ieee80211_rateset ieee80211_rateset_half = + { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; +static const struct ieee80211_rateset ieee80211_rateset_quarter = + { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; +static const struct ieee80211_rateset ieee80211_rateset_11b = + { 4, { B(2), B(4), B(11), B(22) } }; +/* NB: OFDM rates are handled specially based on mode */ +static const struct ieee80211_rateset ieee80211_rateset_11g = + { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; +#undef B + +/* + * Fill in 802.11 available channel set, mark + * all available channels as active, and pick + * a default channel if not already specified. + */ +static void +ieee80211_chan_init(struct ieee80211com *ic) +{ +#define DEFAULTRATES(m, def) do { \ + if (ic->ic_sup_rates[m].rs_nrates == 0) \ + ic->ic_sup_rates[m] = def; \ +} while (0) + struct ieee80211_channel *c; + int i; + + KASSERT(0 < ic->ic_nchans && ic->ic_nchans <= IEEE80211_CHAN_MAX, + ("invalid number of channels specified: %u", ic->ic_nchans)); + memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); + memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); + setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); + for (i = 0; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + KASSERT(c->ic_flags != 0, ("channel with no flags")); + /* + * Help drivers that work only with frequencies by filling + * in IEEE channel #'s if not already calculated. Note this + * mimics similar work done in ieee80211_setregdomain when + * changing regulatory state. + */ + 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); + /* default max tx power to max regulatory */ + if (c->ic_maxpower == 0) + c->ic_maxpower = 2*c->ic_maxregpower; + setbit(ic->ic_chan_avail, c->ic_ieee); + /* + * Identify mode capabilities. + */ + if (IEEE80211_IS_CHAN_A(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_11A); + if (IEEE80211_IS_CHAN_B(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_11B); + if (IEEE80211_IS_CHAN_ANYG(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_11G); + if (IEEE80211_IS_CHAN_FHSS(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_FH); + if (IEEE80211_IS_CHAN_108A(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); + if (IEEE80211_IS_CHAN_108G(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); + if (IEEE80211_IS_CHAN_ST(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A); + if (IEEE80211_IS_CHAN_HALF(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_HALF); + if (IEEE80211_IS_CHAN_QUARTER(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_QUARTER); + if (IEEE80211_IS_CHAN_HTA(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_11NA); + if (IEEE80211_IS_CHAN_HTG(c)) + setbit(ic->ic_modecaps, IEEE80211_MODE_11NG); + } + /* initialize candidate channels to all available */ + memcpy(ic->ic_chan_active, ic->ic_chan_avail, + sizeof(ic->ic_chan_avail)); + + /* sort channel table to allow lookup optimizations */ + ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); + + /* invalidate any previous state */ + ic->ic_bsschan = IEEE80211_CHAN_ANYC; + ic->ic_prevchan = NULL; + ic->ic_csa_newchan = NULL; + /* arbitrarily pick the first channel */ + ic->ic_curchan = &ic->ic_channels[0]; + ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); + + /* fillin well-known rate sets if driver has not specified */ + DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); + DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); + DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); + DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); + DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); + DEFAULTRATES(IEEE80211_MODE_STURBO_A, ieee80211_rateset_11a); + DEFAULTRATES(IEEE80211_MODE_HALF, ieee80211_rateset_half); + DEFAULTRATES(IEEE80211_MODE_QUARTER, ieee80211_rateset_quarter); + DEFAULTRATES(IEEE80211_MODE_11NA, ieee80211_rateset_11a); + DEFAULTRATES(IEEE80211_MODE_11NG, ieee80211_rateset_11g); + + /* + * Set auto mode to reset active channel state and any desired channel. + */ + (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); +#undef DEFAULTRATES +} + +static void +null_update_mcast(struct ifnet *ifp) +{ + if_printf(ifp, "need multicast update callback\n"); +} + +static void +null_update_promisc(struct ifnet *ifp) +{ + if_printf(ifp, "need promiscuous mode update callback\n"); +} + +static int +null_transmit(struct ifnet *ifp, struct mbuf *m) +{ + m_freem(m); + ifp->if_oerrors++; + return EACCES; /* XXX EIO/EPERM? */ +} + +static int +null_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct route *ro) +{ + if_printf(ifp, "discard raw packet\n"); + return null_transmit(ifp, m); +} + +static void +null_input(struct ifnet *ifp, struct mbuf *m) +{ + if_printf(ifp, "if_input should not be called\n"); + m_freem(m); +} + +/* + * Attach/setup the common net80211 state. Called by + * the driver on attach to prior to creating any vap's. + */ +void +ieee80211_ifattach(struct ieee80211com *ic, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct ifnet *ifp = ic->ic_ifp; + struct sockaddr_dl *sdl; + struct ifaddr *ifa; + + KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type)); + + IEEE80211_LOCK_INIT(ic, ifp->if_xname); + TAILQ_INIT(&ic->ic_vaps); + + /* Create a taskqueue for all state changes */ + ic->ic_tq = taskqueue_create("ic_taskq", M_WAITOK | M_ZERO, + taskqueue_thread_enqueue, &ic->ic_tq); + taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s taskq", + ifp->if_xname); + /* + * Fill in 802.11 available channel set, mark all + * available channels as active, and pick a default + * channel if not already specified. + */ + ieee80211_media_init(ic); + + ic->ic_update_mcast = null_update_mcast; + ic->ic_update_promisc = null_update_promisc; + + ic->ic_hash_key = arc4random(); + ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; + ic->ic_lintval = ic->ic_bintval; + ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; + + ieee80211_crypto_attach(ic); + ieee80211_node_attach(ic); + ieee80211_power_attach(ic); + ieee80211_proto_attach(ic); +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_superg_attach(ic); +#endif + ieee80211_ht_attach(ic); + ieee80211_scan_attach(ic); + ieee80211_regdomain_attach(ic); + ieee80211_dfs_attach(ic); + + ieee80211_sysctl_attach(ic); + + ifp->if_addrlen = IEEE80211_ADDR_LEN; + ifp->if_hdrlen = 0; + if_attach(ifp); + ifp->if_mtu = IEEE80211_MTU_MAX; + ifp->if_broadcastaddr = ieee80211broadcastaddr; + ifp->if_output = null_output; + ifp->if_input = null_input; /* just in case */ + ifp->if_resolvemulti = NULL; /* NB: callers check */ + + ifa = ifaddr_byindex(ifp->if_index); + KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */ + sdl->sdl_alen = IEEE80211_ADDR_LEN; + IEEE80211_ADDR_COPY(LLADDR(sdl), macaddr); + ifa_free(ifa); +} + +/* + * Detach net80211 state on device detach. Tear down + * all vap's and reclaim all common state prior to the + * device state going away. Note we may call back into + * driver; it must be prepared for this. + */ +void +ieee80211_ifdetach(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211vap *vap; + + if_detach(ifp); + + while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) + ieee80211_vap_destroy(vap); + ieee80211_waitfor_parent(ic); + + ieee80211_sysctl_detach(ic); + ieee80211_dfs_detach(ic); + ieee80211_regdomain_detach(ic); + ieee80211_scan_detach(ic); +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_superg_detach(ic); +#endif + ieee80211_ht_detach(ic); + /* NB: must be called before ieee80211_node_detach */ + ieee80211_proto_detach(ic); + ieee80211_crypto_detach(ic); + ieee80211_power_detach(ic); + ieee80211_node_detach(ic); + + ifmedia_removeall(&ic->ic_media); + taskqueue_free(ic->ic_tq); + IEEE80211_LOCK_DESTROY(ic); +} + +/* + * Default reset method for use with the ioctl support. This + * method is invoked after any state change in the 802.11 + * layer that should be propagated to the hardware but not + * require re-initialization of the 802.11 state machine (e.g + * rescanning for an ap). We always return ENETRESET which + * should cause the driver to re-initialize the device. Drivers + * can override this method to implement more optimized support. + */ +static int +default_reset(struct ieee80211vap *vap, u_long cmd) +{ + return ENETRESET; +} + +/* + * Prepare a vap for use. Drivers use this call to + * setup net80211 state in new vap's prior attaching + * them with ieee80211_vap_attach (below). + */ +int +ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct ifnet *ifp; + + ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + if_printf(ic->ic_ifp, "%s: unable to allocate ifnet\n", + __func__); + return ENOMEM; + } + if_initname(ifp, name, unit); + ifp->if_softc = vap; /* back pointer */ + ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; + ifp->if_start = ieee80211_start; + ifp->if_ioctl = ieee80211_ioctl; + ifp->if_init = ieee80211_init; + /* NB: input+output filled in by ether_ifattach */ + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + + vap->iv_ifp = ifp; + vap->iv_ic = ic; + vap->iv_flags = ic->ic_flags; /* propagate common flags */ + vap->iv_flags_ext = ic->ic_flags_ext; + vap->iv_flags_ven = ic->ic_flags_ven; + vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; + vap->iv_htcaps = ic->ic_htcaps; + vap->iv_opmode = opmode; + vap->iv_caps |= ieee80211_opcap[opmode]; + switch (opmode) { + case IEEE80211_M_WDS: + /* + * WDS links must specify the bssid of the far end. + * For legacy operation this is a static relationship. + * For non-legacy operation the station must associate + * and be authorized to pass traffic. Plumbing the + * vap to the proper node happens when the vap + * transitions to RUN state. + */ + IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); + vap->iv_flags |= IEEE80211_F_DESBSSID; + if (flags & IEEE80211_CLONE_WDSLEGACY) + vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; + break; +#ifdef IEEE80211_SUPPORT_TDMA + case IEEE80211_M_AHDEMO: + if (flags & IEEE80211_CLONE_TDMA) { + /* NB: checked before clone operation allowed */ + KASSERT(ic->ic_caps & IEEE80211_C_TDMA, + ("not TDMA capable, ic_caps 0x%x", ic->ic_caps)); + /* + * Propagate TDMA capability to mark vap; this + * cannot be removed and is used to distinguish + * regular ahdemo operation from ahdemo+tdma. + */ + vap->iv_caps |= IEEE80211_C_TDMA; + } + break; +#endif + } + /* auto-enable s/w beacon miss support */ + if (flags & IEEE80211_CLONE_NOBEACONS) + vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; + /* auto-generated or user supplied MAC address */ + if (flags & (IEEE80211_CLONE_BSSID|IEEE80211_CLONE_MACADDR)) + vap->iv_flags_ext |= IEEE80211_FEXT_UNIQMAC; + /* + * Enable various functionality by default if we're + * capable; the driver can override us if it knows better. + */ + if (vap->iv_caps & IEEE80211_C_WME) + vap->iv_flags |= IEEE80211_F_WME; + if (vap->iv_caps & IEEE80211_C_BURST) + vap->iv_flags |= IEEE80211_F_BURST; + /* NB: bg scanning only makes sense for station mode right now */ + if (vap->iv_opmode == IEEE80211_M_STA && + (vap->iv_caps & IEEE80211_C_BGSCAN)) + vap->iv_flags |= IEEE80211_F_BGSCAN; + vap->iv_flags |= IEEE80211_F_DOTH; /* XXX no cap, just ena */ + /* NB: DFS support only makes sense for ap mode right now */ + if (vap->iv_opmode == IEEE80211_M_HOSTAP && + (vap->iv_caps & IEEE80211_C_DFS)) + vap->iv_flags_ext |= IEEE80211_FEXT_DFS; + + vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ + vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; + vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; + /* + * Install a default reset method for the ioctl support; + * the driver can override this. + */ + vap->iv_reset = default_reset; + + IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); + + ieee80211_sysctl_vattach(vap); + ieee80211_crypto_vattach(vap); + ieee80211_node_vattach(vap); + ieee80211_power_vattach(vap); + ieee80211_proto_vattach(vap); +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_superg_vattach(vap); +#endif + ieee80211_ht_vattach(vap); + ieee80211_scan_vattach(vap); + ieee80211_regdomain_vattach(vap); + ieee80211_radiotap_vattach(vap); + ieee80211_ratectl_set(vap, IEEE80211_RATECTL_NONE); + + return 0; +} + +/* + * Activate a vap. State should have been prepared with a + * call to ieee80211_vap_setup and by the driver. On return + * from this call the vap is ready for use. + */ +int +ieee80211_vap_attach(struct ieee80211vap *vap, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) +{ + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211com *ic = vap->iv_ic; + struct ifmediareq imr; + int maxrate; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", + __func__, ieee80211_opmode_name[vap->iv_opmode], + ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext); + + /* + * Do late attach work that cannot happen until after + * the driver has had a chance to override defaults. + */ + ieee80211_node_latevattach(vap); + ieee80211_power_latevattach(vap); + + maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, + vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); + ieee80211_media_status(ifp, &imr); + /* NB: strip explicit mode; we're actually in autoselect */ + ifmedia_set(&vap->iv_media, + imr.ifm_active &~ (IFM_MMASK | IFM_IEEE80211_TURBO)); + if (maxrate) + ifp->if_baudrate = IF_Mbps(maxrate); + + ether_ifattach(ifp, vap->iv_myaddr); + if (vap->iv_opmode == IEEE80211_M_MONITOR) { + /* NB: disallow transmit */ + ifp->if_transmit = null_transmit; + ifp->if_output = null_output; + } else { + /* hook output method setup by ether_ifattach */ + vap->iv_output = ifp->if_output; + ifp->if_output = ieee80211_output; + } + /* NB: if_mtu set by ether_ifattach to ETHERMTU */ + + IEEE80211_LOCK(ic); + TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); + ieee80211_syncflag_locked(ic, IEEE80211_F_WME); +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); +#endif + ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); + ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); + ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); + ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); + IEEE80211_UNLOCK(ic); + + return 1; +} + +/* + * Tear down vap state and reclaim the ifnet. + * The driver is assumed to have prepared for + * this; e.g. by turning off interrupts for the + * underlying device. + */ +void +ieee80211_vap_detach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", + __func__, ieee80211_opmode_name[vap->iv_opmode], + ic->ic_ifp->if_xname); + + /* NB: bpfdetach is called by ether_ifdetach and claims all taps */ + ether_ifdetach(ifp); + + ieee80211_stop(vap); + + /* + * Flush any deferred vap tasks. + */ + ieee80211_draintask(ic, &vap->iv_nstate_task); + ieee80211_draintask(ic, &vap->iv_swbmiss_task); + + /* XXX band-aid until ifnet handles this for us */ + taskqueue_drain(taskqueue_swi, &ifp->if_linktask); + + IEEE80211_LOCK(ic); + KASSERT(vap->iv_state == IEEE80211_S_INIT , ("vap still running")); + TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); + ieee80211_syncflag_locked(ic, IEEE80211_F_WME); +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); +#endif + ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); + ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); + ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); + ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); + /* NB: this handles the bpfdetach done below */ + ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); + IEEE80211_UNLOCK(ic); + + ifmedia_removeall(&vap->iv_media); + + ieee80211_radiotap_vdetach(vap); + ieee80211_regdomain_vdetach(vap); + ieee80211_scan_vdetach(vap); +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_superg_vdetach(vap); +#endif + ieee80211_ht_vdetach(vap); + /* NB: must be before ieee80211_node_vdetach */ + ieee80211_proto_vdetach(vap); + ieee80211_crypto_vdetach(vap); + ieee80211_power_vdetach(vap); + ieee80211_node_vdetach(vap); + ieee80211_sysctl_vdetach(vap); + + if_free(ifp); +} + +/* + * Synchronize flag bit state in the parent ifnet structure + * according to the state of all vap ifnet's. This is used, + * for example, to handle IFF_PROMISC and IFF_ALLMULTI. + */ +void +ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211vap *vap; + int bit, oflags; + + IEEE80211_LOCK_ASSERT(ic); + + bit = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_ifp->if_flags & flag) { + /* + * XXX the bridge sets PROMISC but we don't want to + * enable it on the device, discard here so all the + * drivers don't need to special-case it + */ + if (flag == IFF_PROMISC && + !(vap->iv_opmode == IEEE80211_M_MONITOR || + (vap->iv_opmode == IEEE80211_M_AHDEMO && + (vap->iv_caps & IEEE80211_C_TDMA) == 0))) + continue; + bit = 1; + break; + } + oflags = ifp->if_flags; + if (bit) + ifp->if_flags |= flag; + else + ifp->if_flags &= ~flag; + if ((ifp->if_flags ^ oflags) & flag) { + /* XXX should we return 1/0 and let caller do this? */ + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if (flag == IFF_PROMISC) + ieee80211_runtask(ic, &ic->ic_promisc_task); + else if (flag == IFF_ALLMULTI) + ieee80211_runtask(ic, &ic->ic_mcast_task); + } + } +} + +/* + * Synchronize flag bit state in the com structure + * according to the state of all vap's. This is used, + * for example, to handle state changes via ioctls. + */ +static void +ieee80211_syncflag_locked(struct ieee80211com *ic, int flag) +{ + struct ieee80211vap *vap; + int bit; + + IEEE80211_LOCK_ASSERT(ic); + + bit = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_flags & flag) { + bit = 1; + break; + } + if (bit) + ic->ic_flags |= flag; + else + ic->ic_flags &= ~flag; +} + +void +ieee80211_syncflag(struct ieee80211vap *vap, int flag) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + if (flag < 0) { + flag = -flag; + vap->iv_flags &= ~flag; + } else + vap->iv_flags |= flag; + ieee80211_syncflag_locked(ic, flag); + IEEE80211_UNLOCK(ic); +} + +/* + * Synchronize flags_ht bit state in the com structure + * according to the state of all vap's. This is used, + * for example, to handle state changes via ioctls. + */ +static void +ieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag) +{ + struct ieee80211vap *vap; + int bit; + + IEEE80211_LOCK_ASSERT(ic); + + bit = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_flags_ht & flag) { + bit = 1; + break; + } + if (bit) + ic->ic_flags_ht |= flag; + else + ic->ic_flags_ht &= ~flag; +} + +void +ieee80211_syncflag_ht(struct ieee80211vap *vap, int flag) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + if (flag < 0) { + flag = -flag; + vap->iv_flags_ht &= ~flag; + } else + vap->iv_flags_ht |= flag; + ieee80211_syncflag_ht_locked(ic, flag); + IEEE80211_UNLOCK(ic); +} + +/* + * Synchronize flags_ext bit state in the com structure + * according to the state of all vap's. This is used, + * for example, to handle state changes via ioctls. + */ +static void +ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) +{ + struct ieee80211vap *vap; + int bit; + + IEEE80211_LOCK_ASSERT(ic); + + bit = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_flags_ext & flag) { + bit = 1; + break; + } + if (bit) + ic->ic_flags_ext |= flag; + else + ic->ic_flags_ext &= ~flag; +} + +void +ieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + if (flag < 0) { + flag = -flag; + vap->iv_flags_ext &= ~flag; + } else + vap->iv_flags_ext |= flag; + ieee80211_syncflag_ext_locked(ic, flag); + IEEE80211_UNLOCK(ic); +} + +static __inline int +mapgsm(u_int freq, u_int flags) +{ + freq *= 10; + if (flags & IEEE80211_CHAN_QUARTER) + freq += 5; + else if (flags & IEEE80211_CHAN_HALF) + freq += 10; + else + freq += 20; + /* NB: there is no 907/20 wide but leave room */ + return (freq - 906*10) / 5; +} + +static __inline int +mappsb(u_int freq, u_int flags) +{ + return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; +} + +/* + * Convert MHz frequency to IEEE channel number. + */ +int +ieee80211_mhz2ieee(u_int freq, u_int flags) +{ +#define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) + if (flags & IEEE80211_CHAN_GSM) + return mapgsm(freq, flags); + if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ + if (freq == 2484) + return 14; + if (freq < 2484) + return ((int) freq - 2407) / 5; + else + return 15 + ((freq - 2512) / 20); + } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ + if (freq <= 5000) { + /* XXX check regdomain? */ + if (IS_FREQ_IN_PSB(freq)) + return mappsb(freq, flags); + return (freq - 4000) / 5; + } else + return (freq - 5000) / 5; + } else { /* either, guess */ + if (freq == 2484) + return 14; + if (freq < 2484) { + if (907 <= freq && freq <= 922) + return mapgsm(freq, flags); + return ((int) freq - 2407) / 5; + } + if (freq < 5000) { + if (IS_FREQ_IN_PSB(freq)) + return mappsb(freq, flags); + else if (freq > 4900) + return (freq - 4000) / 5; + else + return 15 + ((freq - 2512) / 20); + } + return (freq - 5000) / 5; + } +#undef IS_FREQ_IN_PSB +} + +/* + * Convert channel to IEEE channel number. + */ +int +ieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) +{ + if (c == NULL) { + if_printf(ic->ic_ifp, "invalid channel (NULL)\n"); + return 0; /* XXX */ + } + return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); +} + +/* + * Convert IEEE channel number to MHz frequency. + */ +u_int +ieee80211_ieee2mhz(u_int chan, u_int flags) +{ + if (flags & IEEE80211_CHAN_GSM) + return 907 + 5 * (chan / 10); + if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ + if (chan == 14) + return 2484; + if (chan < 14) + return 2407 + chan*5; + else + return 2512 + ((chan-15)*20); + } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ + if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { + chan -= 37; + return 4940 + chan*5 + (chan % 5 ? 2 : 0); + } + return 5000 + (chan*5); + } else { /* either, guess */ + /* XXX can't distinguish PSB+GSM channels */ + if (chan == 14) + return 2484; + if (chan < 14) /* 0-13 */ + return 2407 + chan*5; + if (chan < 27) /* 15-26 */ + return 2512 + ((chan-15)*20); + return 5000 + (chan*5); + } +} + +/* + * Locate a channel given a frequency+flags. We cache + * the previous lookup to optimize switching between two + * channels--as happens with dynamic turbo. + */ +struct ieee80211_channel * +ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) +{ + struct ieee80211_channel *c; + int i; + + flags &= IEEE80211_CHAN_ALLTURBO; + c = ic->ic_prevchan; + if (c != NULL && c->ic_freq == freq && + (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) + return c; + /* brute force search */ + for (i = 0; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (c->ic_freq == freq && + (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) + return c; + } + return NULL; +} + +/* + * Locate a channel given a channel number+flags. We cache + * the previous lookup to optimize switching between two + * channels--as happens with dynamic turbo. + */ +struct ieee80211_channel * +ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) +{ + struct ieee80211_channel *c; + int i; + + flags &= IEEE80211_CHAN_ALLTURBO; + c = ic->ic_prevchan; + if (c != NULL && c->ic_ieee == ieee && + (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) + return c; + /* brute force search */ + for (i = 0; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (c->ic_ieee == ieee && + (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) + return c; + } + return NULL; +} + +static void +addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) +{ +#define ADD(_ic, _s, _o) \ + ifmedia_add(media, \ + IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) + static const u_int mopts[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = IFM_AUTO, + [IEEE80211_MODE_11A] = IFM_IEEE80211_11A, + [IEEE80211_MODE_11B] = IFM_IEEE80211_11B, + [IEEE80211_MODE_11G] = IFM_IEEE80211_11G, + [IEEE80211_MODE_FH] = IFM_IEEE80211_FH, + [IEEE80211_MODE_TURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, + [IEEE80211_MODE_TURBO_G] = IFM_IEEE80211_11G|IFM_IEEE80211_TURBO, + [IEEE80211_MODE_STURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, + [IEEE80211_MODE_HALF] = IFM_IEEE80211_11A, /* XXX */ + [IEEE80211_MODE_QUARTER] = IFM_IEEE80211_11A, /* XXX */ + [IEEE80211_MODE_11NA] = IFM_IEEE80211_11NA, + [IEEE80211_MODE_11NG] = IFM_IEEE80211_11NG, + }; + u_int mopt; + + mopt = mopts[mode]; + if (addsta) + ADD(ic, mword, mopt); /* STA mode has no cap */ + if (caps & IEEE80211_C_IBSS) + ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); + if (caps & IEEE80211_C_HOSTAP) + ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); + if (caps & IEEE80211_C_AHDEMO) + ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); + if (caps & IEEE80211_C_MONITOR) + ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); + if (caps & IEEE80211_C_WDS) + ADD(media, mword, mopt | IFM_IEEE80211_WDS); + if (caps & IEEE80211_C_MBSS) + ADD(media, mword, mopt | IFM_IEEE80211_MBSS); +#undef ADD +} + +/* + * Setup the media data structures according to the channel and + * rate tables. + */ +static int +ieee80211_media_setup(struct ieee80211com *ic, + struct ifmedia *media, int caps, int addsta, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) +{ + int i, j, mode, rate, maxrate, mword, r; + const struct ieee80211_rateset *rs; + struct ieee80211_rateset allrates; + + /* + * Fill in media characteristics. + */ + ifmedia_init(media, 0, media_change, media_stat); + maxrate = 0; + /* + * Add media for legacy operating modes. + */ + memset(&allrates, 0, sizeof(allrates)); + for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { + if (isclr(ic->ic_modecaps, mode)) + continue; + addmedia(media, caps, addsta, mode, IFM_AUTO); + if (mode == IEEE80211_MODE_AUTO) + continue; + rs = &ic->ic_sup_rates[mode]; + for (i = 0; i < rs->rs_nrates; i++) { + rate = rs->rs_rates[i]; + mword = ieee80211_rate2media(ic, rate, mode); + if (mword == 0) + continue; + addmedia(media, caps, addsta, mode, mword); + /* + * Add legacy rate to the collection of all rates. + */ + r = rate & IEEE80211_RATE_VAL; + for (j = 0; j < allrates.rs_nrates; j++) + if (allrates.rs_rates[j] == r) + break; + if (j == allrates.rs_nrates) { + /* unique, add to the set */ + allrates.rs_rates[j] = r; + allrates.rs_nrates++; + } + rate = (rate & IEEE80211_RATE_VAL) / 2; + if (rate > maxrate) + maxrate = rate; + } + } + for (i = 0; i < allrates.rs_nrates; i++) { + mword = ieee80211_rate2media(ic, allrates.rs_rates[i], + IEEE80211_MODE_AUTO); + if (mword == 0) + continue; + /* NB: remove media options from mword */ + addmedia(media, caps, addsta, + IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); + } + /* + * Add HT/11n media. Note that we do not have enough + * bits in the media subtype to express the MCS so we + * use a "placeholder" media subtype and any fixed MCS + * must be specified with a different mechanism. + */ + for (; mode <= IEEE80211_MODE_11NG; mode++) { + if (isclr(ic->ic_modecaps, mode)) + continue; + addmedia(media, caps, addsta, mode, IFM_AUTO); + addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); + } + if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || + isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { + addmedia(media, caps, addsta, + IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); + /* XXX could walk htrates */ + /* XXX known array size */ + if (ieee80211_htrates[15].ht40_rate_400ns > maxrate) + maxrate = ieee80211_htrates[15].ht40_rate_400ns; + } + return maxrate; +} + +void +ieee80211_media_init(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + int maxrate; + + /* NB: this works because the structure is initialized to zero */ + if (!LIST_EMPTY(&ic->ic_media.ifm_list)) { + /* + * We are re-initializing the channel list; clear + * the existing media state as the media routines + * don't suppress duplicates. + */ + ifmedia_removeall(&ic->ic_media); + } + ieee80211_chan_init(ic); + + /* + * Recalculate media settings in case new channel list changes + * the set of available modes. + */ + maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1, + ieee80211com_media_change, ieee80211com_media_status); + /* NB: strip explicit mode; we're actually in autoselect */ + ifmedia_set(&ic->ic_media, + media_status(ic->ic_opmode, ic->ic_curchan) &~ + (IFM_MMASK | IFM_IEEE80211_TURBO)); + if (maxrate) + ifp->if_baudrate = IF_Mbps(maxrate); + + /* XXX need to propagate new media settings to vap's */ +} + +/* XXX inline or eliminate? */ +const struct ieee80211_rateset * +ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) +{ + /* XXX does this work for 11ng basic rates? */ + return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; +} + +void +ieee80211_announce(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + int i, mode, rate, mword; + const struct ieee80211_rateset *rs; + + /* NB: skip AUTO since it has no rates */ + for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { + if (isclr(ic->ic_modecaps, mode)) + continue; + if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]); + rs = &ic->ic_sup_rates[mode]; + for (i = 0; i < rs->rs_nrates; i++) { + mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); + if (mword == 0) + continue; + rate = ieee80211_media2rate(mword); + printf("%s%d%sMbps", (i != 0 ? " " : ""), + rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); + } + printf("\n"); + } + ieee80211_ht_announce(ic); +} + +void +ieee80211_announce_channels(struct ieee80211com *ic) +{ + const struct ieee80211_channel *c; + char type; + int i, cw; + + printf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); + for (i = 0; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (IEEE80211_IS_CHAN_ST(c)) + type = 'S'; + else if (IEEE80211_IS_CHAN_108A(c)) + type = 'T'; + else if (IEEE80211_IS_CHAN_108G(c)) + type = 'G'; + else if (IEEE80211_IS_CHAN_HT(c)) + type = 'n'; + else if (IEEE80211_IS_CHAN_A(c)) + type = 'a'; + else if (IEEE80211_IS_CHAN_ANYG(c)) + type = 'g'; + else if (IEEE80211_IS_CHAN_B(c)) + type = 'b'; + else + type = 'f'; + if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) + cw = 40; + else if (IEEE80211_IS_CHAN_HALF(c)) + cw = 10; + else if (IEEE80211_IS_CHAN_QUARTER(c)) + cw = 5; + else + cw = 20; + printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" + , c->ic_ieee, c->ic_freq, type + , cw + , IEEE80211_IS_CHAN_HT40U(c) ? '+' : + IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' + , c->ic_maxregpower + , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 + , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 + ); + } +} + +static int +media2mode(const struct ifmedia_entry *ime, uint32_t flags, uint16_t *mode) +{ + switch (IFM_MODE(ime->ifm_media)) { + case IFM_IEEE80211_11A: + *mode = IEEE80211_MODE_11A; + break; + case IFM_IEEE80211_11B: + *mode = IEEE80211_MODE_11B; + break; + case IFM_IEEE80211_11G: + *mode = IEEE80211_MODE_11G; + break; + case IFM_IEEE80211_FH: + *mode = IEEE80211_MODE_FH; + break; + case IFM_IEEE80211_11NA: + *mode = IEEE80211_MODE_11NA; + break; + case IFM_IEEE80211_11NG: + *mode = IEEE80211_MODE_11NG; + break; + case IFM_AUTO: + *mode = IEEE80211_MODE_AUTO; + break; + default: + return 0; + } + /* + * Turbo mode is an ``option''. + * XXX does not apply to AUTO + */ + if (ime->ifm_media & IFM_IEEE80211_TURBO) { + if (*mode == IEEE80211_MODE_11A) { + if (flags & IEEE80211_F_TURBOP) + *mode = IEEE80211_MODE_TURBO_A; + else + *mode = IEEE80211_MODE_STURBO_A; + } else if (*mode == IEEE80211_MODE_11G) + *mode = IEEE80211_MODE_TURBO_G; + else + return 0; + } + /* XXX HT40 +/- */ + return 1; +} + +/* + * Handle a media change request on the underlying interface. + */ +int +ieee80211com_media_change(struct ifnet *ifp) +{ + return EINVAL; +} + +/* + * Handle a media change request on the vap interface. + */ +int +ieee80211_media_change(struct ifnet *ifp) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ifmedia_entry *ime = vap->iv_media.ifm_cur; + uint16_t newmode; + + if (!media2mode(ime, vap->iv_flags, &newmode)) + return EINVAL; + if (vap->iv_des_mode != newmode) { + vap->iv_des_mode = newmode; + /* XXX kick state machine if up+running */ + } + return 0; +} + +/* + * Common code to calculate the media status word + * from the operating mode and channel state. + */ +static int +media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) +{ + int status; + + status = IFM_IEEE80211; + switch (opmode) { + case IEEE80211_M_STA: + break; + case IEEE80211_M_IBSS: + status |= IFM_IEEE80211_ADHOC; + break; + case IEEE80211_M_HOSTAP: + status |= IFM_IEEE80211_HOSTAP; + break; + case IEEE80211_M_MONITOR: + status |= IFM_IEEE80211_MONITOR; + break; + case IEEE80211_M_AHDEMO: + status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; + break; + case IEEE80211_M_WDS: + status |= IFM_IEEE80211_WDS; + break; + case IEEE80211_M_MBSS: + status |= IFM_IEEE80211_MBSS; + break; + } + if (IEEE80211_IS_CHAN_HTA(chan)) { + status |= IFM_IEEE80211_11NA; + } else if (IEEE80211_IS_CHAN_HTG(chan)) { + status |= IFM_IEEE80211_11NG; + } else if (IEEE80211_IS_CHAN_A(chan)) { + status |= IFM_IEEE80211_11A; + } else if (IEEE80211_IS_CHAN_B(chan)) { + status |= IFM_IEEE80211_11B; + } else if (IEEE80211_IS_CHAN_ANYG(chan)) { + status |= IFM_IEEE80211_11G; + } else if (IEEE80211_IS_CHAN_FHSS(chan)) { + status |= IFM_IEEE80211_FH; + } + /* XXX else complain? */ + + if (IEEE80211_IS_CHAN_TURBO(chan)) + status |= IFM_IEEE80211_TURBO; +#if 0 + if (IEEE80211_IS_CHAN_HT20(chan)) + status |= IFM_IEEE80211_HT20; + if (IEEE80211_IS_CHAN_HT40(chan)) + status |= IFM_IEEE80211_HT40; +#endif + return status; +} + +static void +ieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr) +{ + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap; + + imr->ifm_status = IFM_AVALID; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_ifp->if_flags & IFF_UP) { + imr->ifm_status |= IFM_ACTIVE; + break; + } + imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan); + if (imr->ifm_status & IFM_ACTIVE) + imr->ifm_current = imr->ifm_active; +} + +void +ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + enum ieee80211_phymode mode; + + imr->ifm_status = IFM_AVALID; + /* + * NB: use the current channel's mode to lock down a xmit + * rate only when running; otherwise we may have a mismatch + * in which case the rate will not be convertible. + */ + if (vap->iv_state == IEEE80211_S_RUN) { + imr->ifm_status |= IFM_ACTIVE; + mode = ieee80211_chan2mode(ic->ic_curchan); + } else + mode = IEEE80211_MODE_AUTO; + imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); + /* + * Calculate a current rate if possible. + */ + if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { + /* + * A fixed rate is set, report that. + */ + imr->ifm_active |= ieee80211_rate2media(ic, + vap->iv_txparms[mode].ucastrate, mode); + } else if (vap->iv_opmode == IEEE80211_M_STA) { + /* + * In station mode report the current transmit rate. + */ + imr->ifm_active |= ieee80211_rate2media(ic, + vap->iv_bss->ni_txrate, mode); + } else + imr->ifm_active |= IFM_AUTO; + if (imr->ifm_status & IFM_ACTIVE) + imr->ifm_current = imr->ifm_active; +} + +/* + * Set the current phy mode and recalculate the active channel + * set based on the available channels for this mode. Also + * select a new default/current channel if the current one is + * inappropriate for this mode. + */ +int +ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) +{ + /* + * Adjust basic rates in 11b/11g supported rate set. + * Note that if operating on a hal/quarter rate channel + * this is a noop as those rates sets are different + * and used instead. + */ + if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) + ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); + + ic->ic_curmode = mode; + ieee80211_reset_erp(ic); /* reset ERP state */ + + return 0; +} + +/* + * Return the phy mode for with the specified channel. + */ +enum ieee80211_phymode +ieee80211_chan2mode(const struct ieee80211_channel *chan) +{ + + if (IEEE80211_IS_CHAN_HTA(chan)) + return IEEE80211_MODE_11NA; + else if (IEEE80211_IS_CHAN_HTG(chan)) + return IEEE80211_MODE_11NG; + else if (IEEE80211_IS_CHAN_108G(chan)) + return IEEE80211_MODE_TURBO_G; + else if (IEEE80211_IS_CHAN_ST(chan)) + return IEEE80211_MODE_STURBO_A; + else if (IEEE80211_IS_CHAN_TURBO(chan)) + return IEEE80211_MODE_TURBO_A; + else if (IEEE80211_IS_CHAN_HALF(chan)) + return IEEE80211_MODE_HALF; + else if (IEEE80211_IS_CHAN_QUARTER(chan)) + return IEEE80211_MODE_QUARTER; + else if (IEEE80211_IS_CHAN_A(chan)) + return IEEE80211_MODE_11A; + else if (IEEE80211_IS_CHAN_ANYG(chan)) + return IEEE80211_MODE_11G; + else if (IEEE80211_IS_CHAN_B(chan)) + return IEEE80211_MODE_11B; + else if (IEEE80211_IS_CHAN_FHSS(chan)) + return IEEE80211_MODE_FH; + + /* NB: should not get here */ + printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", + __func__, chan->ic_freq, chan->ic_flags); + return IEEE80211_MODE_11B; +} + +struct ratemedia { + u_int match; /* rate + mode */ + u_int media; /* if_media rate */ +}; + +static int +findmedia(const struct ratemedia rates[], int n, u_int match) +{ + int i; + + for (i = 0; i < n; i++) + if (rates[i].match == match) + return rates[i].media; + return IFM_AUTO; +} + +/* + * Convert IEEE80211 rate value to ifmedia subtype. + * Rate is either a legacy rate in units of 0.5Mbps + * or an MCS index. + */ +int +ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const struct ratemedia rates[] = { + { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, + { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, + { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, + { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, + { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, + { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, + { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, + { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, + { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, + { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, + { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, + { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, + { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, + { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, + { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, + { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, + { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, + { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, + { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, + { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, + { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, + { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, + { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, + { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, + { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, + { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, + { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, + { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, + { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, + { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, + /* NB: OFDM72 doesn't realy exist so we don't handle it */ + }; + static const struct ratemedia htrates[] = { + { 0, IFM_IEEE80211_MCS }, + { 1, IFM_IEEE80211_MCS }, + { 2, IFM_IEEE80211_MCS }, + { 3, IFM_IEEE80211_MCS }, + { 4, IFM_IEEE80211_MCS }, + { 5, IFM_IEEE80211_MCS }, + { 6, IFM_IEEE80211_MCS }, + { 7, IFM_IEEE80211_MCS }, + { 8, IFM_IEEE80211_MCS }, + { 9, IFM_IEEE80211_MCS }, + { 10, IFM_IEEE80211_MCS }, + { 11, IFM_IEEE80211_MCS }, + { 12, IFM_IEEE80211_MCS }, + { 13, IFM_IEEE80211_MCS }, + { 14, IFM_IEEE80211_MCS }, + { 15, IFM_IEEE80211_MCS }, + }; + int m; + + /* + * Check 11n rates first for match as an MCS. + */ + if (mode == IEEE80211_MODE_11NA) { + if (rate & IEEE80211_RATE_MCS) { + rate &= ~IEEE80211_RATE_MCS; + m = findmedia(htrates, N(htrates), rate); + if (m != IFM_AUTO) + return m | IFM_IEEE80211_11NA; + } + } else if (mode == IEEE80211_MODE_11NG) { + /* NB: 12 is ambiguous, it will be treated as an MCS */ + if (rate & IEEE80211_RATE_MCS) { + rate &= ~IEEE80211_RATE_MCS; + m = findmedia(htrates, N(htrates), rate); + if (m != IFM_AUTO) + return m | IFM_IEEE80211_11NG; + } + } + rate &= IEEE80211_RATE_VAL; + switch (mode) { + case IEEE80211_MODE_11A: + case IEEE80211_MODE_HALF: /* XXX good 'nuf */ + case IEEE80211_MODE_QUARTER: + case IEEE80211_MODE_11NA: + case IEEE80211_MODE_TURBO_A: + case IEEE80211_MODE_STURBO_A: + return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A); + case IEEE80211_MODE_11B: + return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B); + case IEEE80211_MODE_FH: + return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH); + case IEEE80211_MODE_AUTO: + /* NB: ic may be NULL for some drivers */ + if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH) + return findmedia(rates, N(rates), + rate | IFM_IEEE80211_FH); + /* NB: hack, 11g matches both 11b+11a rates */ + /* fall thru... */ + case IEEE80211_MODE_11G: + case IEEE80211_MODE_11NG: + case IEEE80211_MODE_TURBO_G: + return findmedia(rates, N(rates), rate | IFM_IEEE80211_11G); + } + return IFM_AUTO; +#undef N +} + +int +ieee80211_media2rate(int mword) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const int ieeerates[] = { + -1, /* IFM_AUTO */ + 0, /* IFM_MANUAL */ + 0, /* IFM_NONE */ + 2, /* IFM_IEEE80211_FH1 */ + 4, /* IFM_IEEE80211_FH2 */ + 2, /* IFM_IEEE80211_DS1 */ + 4, /* IFM_IEEE80211_DS2 */ + 11, /* IFM_IEEE80211_DS5 */ + 22, /* IFM_IEEE80211_DS11 */ + 44, /* IFM_IEEE80211_DS22 */ + 12, /* IFM_IEEE80211_OFDM6 */ + 18, /* IFM_IEEE80211_OFDM9 */ + 24, /* IFM_IEEE80211_OFDM12 */ + 36, /* IFM_IEEE80211_OFDM18 */ + 48, /* IFM_IEEE80211_OFDM24 */ + 72, /* IFM_IEEE80211_OFDM36 */ + 96, /* IFM_IEEE80211_OFDM48 */ + 108, /* IFM_IEEE80211_OFDM54 */ + 144, /* IFM_IEEE80211_OFDM72 */ + 0, /* IFM_IEEE80211_DS354k */ + 0, /* IFM_IEEE80211_DS512k */ + 6, /* IFM_IEEE80211_OFDM3 */ + 9, /* IFM_IEEE80211_OFDM4 */ + 54, /* IFM_IEEE80211_OFDM27 */ + -1, /* IFM_IEEE80211_MCS */ + }; + return IFM_SUBTYPE(mword) < N(ieeerates) ? + ieeerates[IFM_SUBTYPE(mword)] : 0; +#undef N +} + +/* + * The following hash function is adapted from "Hash Functions" by Bob Jenkins + * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). + */ +#define mix(a, b, c) \ +do { \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ +} while (/*CONSTCOND*/0) + +uint32_t +ieee80211_mac_hash(const struct ieee80211com *ic, + const uint8_t addr[IEEE80211_ADDR_LEN]) +{ + uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = ic->ic_hash_key; + + b += addr[5] << 8; + b += addr[4]; + a += addr[3] << 24; + a += addr[2] << 16; + a += addr[1] << 8; + a += addr[0]; + + mix(a, b, c); + + return c; +} +#undef mix diff --git a/freebsd/sys/net80211/ieee80211.h b/freebsd/sys/net80211/ieee80211.h new file mode 100644 index 00000000..6019e0ed --- /dev/null +++ b/freebsd/sys/net80211/ieee80211.h @@ -0,0 +1,1087 @@ +/*- + * 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 _NET80211_IEEE80211_HH_ +#define _NET80211_IEEE80211_HH_ + +/* + * 802.11 protocol definitions. + */ + +#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */ +/* is 802.11 address multicast/broadcast? */ +#define IEEE80211_IS_MULTICAST(_a) (*(_a) & 0x01) + +typedef uint16_t ieee80211_seq; + +/* IEEE 802.11 PLCP header */ +struct ieee80211_plcp_hdr { + uint16_t i_sfd; + uint8_t i_signal; + uint8_t i_service; + uint16_t i_length; + uint16_t i_crc; +} __packed; + +#define IEEE80211_PLCP_SFD 0xF3A0 +#define IEEE80211_PLCP_SERVICE 0x00 +#define IEEE80211_PLCP_SERVICE_LOCKED 0x04 +#define IEEE80211_PLCL_SERVICE_PBCC 0x08 +#define IEEE80211_PLCP_SERVICE_LENEXT5 0x20 +#define IEEE80211_PLCP_SERVICE_LENEXT6 0x40 +#define IEEE80211_PLCP_SERVICE_LENEXT7 0x80 + +/* + * generic definitions for IEEE 802.11 frames + */ +struct ieee80211_frame { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; + uint8_t i_addr3[IEEE80211_ADDR_LEN]; + uint8_t i_seq[2]; + /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ + /* see below */ +} __packed; + +struct ieee80211_qosframe { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; + uint8_t i_addr3[IEEE80211_ADDR_LEN]; + uint8_t i_seq[2]; + uint8_t i_qos[2]; + /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ + /* see below */ +} __packed; + +struct ieee80211_qoscntl { + uint8_t i_qos[2]; +}; + +struct ieee80211_frame_addr4 { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; + uint8_t i_addr3[IEEE80211_ADDR_LEN]; + uint8_t i_seq[2]; + uint8_t i_addr4[IEEE80211_ADDR_LEN]; +} __packed; + + +struct ieee80211_qosframe_addr4 { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; + uint8_t i_addr3[IEEE80211_ADDR_LEN]; + uint8_t i_seq[2]; + uint8_t i_addr4[IEEE80211_ADDR_LEN]; + uint8_t i_qos[2]; +} __packed; + +#define IEEE80211_FC0_VERSION_MASK 0x03 +#define IEEE80211_FC0_VERSION_SHIFT 0 +#define IEEE80211_FC0_VERSION_0 0x00 +#define IEEE80211_FC0_TYPE_MASK 0x0c +#define IEEE80211_FC0_TYPE_SHIFT 2 +#define IEEE80211_FC0_TYPE_MGT 0x00 +#define IEEE80211_FC0_TYPE_CTL 0x04 +#define IEEE80211_FC0_TYPE_DATA 0x08 + +#define IEEE80211_FC0_SUBTYPE_MASK 0xf0 +#define IEEE80211_FC0_SUBTYPE_SHIFT 4 +/* for TYPE_MGT */ +#define IEEE80211_FC0_SUBTYPE_ASSOC_REQ 0x00 +#define IEEE80211_FC0_SUBTYPE_ASSOC_RESP 0x10 +#define IEEE80211_FC0_SUBTYPE_REASSOC_REQ 0x20 +#define IEEE80211_FC0_SUBTYPE_REASSOC_RESP 0x30 +#define IEEE80211_FC0_SUBTYPE_PROBE_REQ 0x40 +#define IEEE80211_FC0_SUBTYPE_PROBE_RESP 0x50 +#define IEEE80211_FC0_SUBTYPE_BEACON 0x80 +#define IEEE80211_FC0_SUBTYPE_ATIM 0x90 +#define IEEE80211_FC0_SUBTYPE_DISASSOC 0xa0 +#define IEEE80211_FC0_SUBTYPE_AUTH 0xb0 +#define IEEE80211_FC0_SUBTYPE_DEAUTH 0xc0 +#define IEEE80211_FC0_SUBTYPE_ACTION 0xd0 +/* for TYPE_CTL */ +#define IEEE80211_FC0_SUBTYPE_BAR 0x80 +#define IEEE80211_FC0_SUBTYPE_BA 0x90 +#define IEEE80211_FC0_SUBTYPE_PS_POLL 0xa0 +#define IEEE80211_FC0_SUBTYPE_RTS 0xb0 +#define IEEE80211_FC0_SUBTYPE_CTS 0xc0 +#define IEEE80211_FC0_SUBTYPE_ACK 0xd0 +#define IEEE80211_FC0_SUBTYPE_CF_END 0xe0 +#define IEEE80211_FC0_SUBTYPE_CF_END_ACK 0xf0 +/* for TYPE_DATA (bit combination) */ +#define IEEE80211_FC0_SUBTYPE_DATA 0x00 +#define IEEE80211_FC0_SUBTYPE_CF_ACK 0x10 +#define IEEE80211_FC0_SUBTYPE_CF_POLL 0x20 +#define IEEE80211_FC0_SUBTYPE_CF_ACPL 0x30 +#define IEEE80211_FC0_SUBTYPE_NODATA 0x40 +#define IEEE80211_FC0_SUBTYPE_CFACK 0x50 +#define IEEE80211_FC0_SUBTYPE_CFPOLL 0x60 +#define IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK 0x70 +#define IEEE80211_FC0_SUBTYPE_QOS 0x80 +#define IEEE80211_FC0_SUBTYPE_QOS_NULL 0xc0 + +#define IEEE80211_FC1_DIR_MASK 0x03 +#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */ +#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */ +#define IEEE80211_FC1_DIR_FROMDS 0x02 /* AP ->STA */ +#define IEEE80211_FC1_DIR_DSTODS 0x03 /* AP ->AP */ + +#define IEEE80211_IS_DSTODS(wh) \ + (((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) + +#define IEEE80211_FC1_MORE_FRAG 0x04 +#define IEEE80211_FC1_RETRY 0x08 +#define IEEE80211_FC1_PWR_MGT 0x10 +#define IEEE80211_FC1_MORE_DATA 0x20 +#define IEEE80211_FC1_WEP 0x40 +#define IEEE80211_FC1_ORDER 0x80 + +#define IEEE80211_SEQ_FRAG_MASK 0x000f +#define IEEE80211_SEQ_FRAG_SHIFT 0 +#define IEEE80211_SEQ_SEQ_MASK 0xfff0 +#define IEEE80211_SEQ_SEQ_SHIFT 4 +#define IEEE80211_SEQ_RANGE 4096 + +#define IEEE80211_SEQ_ADD(seq, incr) \ + (((seq) + (incr)) & (IEEE80211_SEQ_RANGE-1)) +#define IEEE80211_SEQ_INC(seq) IEEE80211_SEQ_ADD(seq,1) +#define IEEE80211_SEQ_SUB(a, b) \ + (((a) + IEEE80211_SEQ_RANGE - (b)) & (IEEE80211_SEQ_RANGE-1)) + +#define IEEE80211_SEQ_BA_RANGE 2048 /* 2^11 */ +#define IEEE80211_SEQ_BA_BEFORE(a, b) \ + (IEEE80211_SEQ_SUB(b, a+1) < IEEE80211_SEQ_BA_RANGE-1) + +#define IEEE80211_NWID_LEN 32 +#define IEEE80211_MESHID_LEN 32 + +#define IEEE80211_QOS_TXOP 0x00ff +/* bit 8 is reserved */ +#define IEEE80211_QOS_AMSDU 0x80 +#define IEEE80211_QOS_AMSDU_S 7 +#define IEEE80211_QOS_ACKPOLICY 0x60 +#define IEEE80211_QOS_ACKPOLICY_S 5 +#define IEEE80211_QOS_ACKPOLICY_NOACK 0x20 /* No ACK required */ +#define IEEE80211_QOS_ACKPOLICY_BA 0x60 /* Block ACK */ +#define IEEE80211_QOS_EOSP 0x10 /* EndOfService Period*/ +#define IEEE80211_QOS_EOSP_S 4 +#define IEEE80211_QOS_TID 0x0f + +/* does frame have QoS sequence control data */ +#define IEEE80211_QOS_HAS_SEQ(wh) \ + (((wh)->i_fc[0] & \ + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \ + (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) + +/* + * WME/802.11e information element. + */ +struct ieee80211_wme_info { + uint8_t wme_id; /* IEEE80211_ELEMID_VENDOR */ + uint8_t wme_len; /* length in bytes */ + uint8_t wme_oui[3]; /* 0x00, 0x50, 0xf2 */ + uint8_t wme_type; /* OUI type */ + uint8_t wme_subtype; /* OUI subtype */ + uint8_t wme_version; /* spec revision */ + uint8_t wme_info; /* QoS info */ +} __packed; + +/* + * WME/802.11e Tspec Element + */ +struct ieee80211_wme_tspec { + uint8_t ts_id; + uint8_t ts_len; + uint8_t ts_oui[3]; + uint8_t ts_oui_type; + uint8_t ts_oui_subtype; + uint8_t ts_version; + uint8_t ts_tsinfo[3]; + uint8_t ts_nom_msdu[2]; + uint8_t ts_max_msdu[2]; + uint8_t ts_min_svc[4]; + uint8_t ts_max_svc[4]; + uint8_t ts_inactv_intv[4]; + uint8_t ts_susp_intv[4]; + uint8_t ts_start_svc[4]; + uint8_t ts_min_rate[4]; + uint8_t ts_mean_rate[4]; + uint8_t ts_max_burst[4]; + uint8_t ts_min_phy[4]; + uint8_t ts_peak_rate[4]; + uint8_t ts_delay[4]; + uint8_t ts_surplus[2]; + uint8_t ts_medium_time[2]; +} __packed; + +/* + * WME AC parameter field + */ +struct ieee80211_wme_acparams { + uint8_t acp_aci_aifsn; + uint8_t acp_logcwminmax; + uint16_t acp_txop; +} __packed; + +#define WME_NUM_AC 4 /* 4 AC categories */ +#define WME_NUM_TID 16 /* 16 tids */ + +#define WME_PARAM_ACI 0x60 /* Mask for ACI field */ +#define WME_PARAM_ACI_S 5 /* Shift for ACI field */ +#define WME_PARAM_ACM 0x10 /* Mask for ACM bit */ +#define WME_PARAM_ACM_S 4 /* Shift for ACM bit */ +#define WME_PARAM_AIFSN 0x0f /* Mask for aifsn field */ +#define WME_PARAM_AIFSN_S 0 /* Shift for aifsn field */ +#define WME_PARAM_LOGCWMIN 0x0f /* Mask for CwMin field (in log) */ +#define WME_PARAM_LOGCWMIN_S 0 /* Shift for CwMin field */ +#define WME_PARAM_LOGCWMAX 0xf0 /* Mask for CwMax field (in log) */ +#define WME_PARAM_LOGCWMAX_S 4 /* Shift for CwMax field */ + +#define WME_AC_TO_TID(_ac) ( \ + ((_ac) == WME_AC_VO) ? 6 : \ + ((_ac) == WME_AC_VI) ? 5 : \ + ((_ac) == WME_AC_BK) ? 1 : \ + 0) + +#define TID_TO_WME_AC(_tid) ( \ + ((_tid) == 0 || (_tid) == 3) ? WME_AC_BE : \ + ((_tid) < 3) ? WME_AC_BK : \ + ((_tid) < 6) ? WME_AC_VI : \ + WME_AC_VO) + +/* + * WME Parameter Element + */ +struct ieee80211_wme_param { + uint8_t param_id; + uint8_t param_len; + uint8_t param_oui[3]; + uint8_t param_oui_type; + uint8_t param_oui_subtype; + uint8_t param_version; + uint8_t param_qosInfo; +#define WME_QOSINFO_COUNT 0x0f /* Mask for param count field */ + uint8_t param_reserved; + struct ieee80211_wme_acparams params_acParams[WME_NUM_AC]; +} __packed; + +/* + * Management Notification Frame + */ +struct ieee80211_mnf { + uint8_t mnf_category; + uint8_t mnf_action; + uint8_t mnf_dialog; + uint8_t mnf_status; +} __packed; +#define MNF_SETUP_REQ 0 +#define MNF_SETUP_RESP 1 +#define MNF_TEARDOWN 2 + +/* + * 802.11n Management Action Frames + */ +/* generic frame format */ +struct ieee80211_action { + uint8_t ia_category; + uint8_t ia_action; +} __packed; + +#define IEEE80211_ACTION_CAT_SM 0 /* Spectrum Management */ +#define IEEE80211_ACTION_CAT_QOS 1 /* QoS */ +#define IEEE80211_ACTION_CAT_DLS 2 /* DLS */ +#define IEEE80211_ACTION_CAT_BA 3 /* BA */ +#define IEEE80211_ACTION_CAT_HT 7 /* HT */ +#define IEEE80211_ACTION_CAT_VENDOR 127 /* Vendor Specific */ + +#define IEEE80211_ACTION_HT_TXCHWIDTH 0 /* recommended xmit chan width*/ +#define IEEE80211_ACTION_HT_MIMOPWRSAVE 1 /* MIMO power save */ + +/* HT - recommended transmission channel width */ +struct ieee80211_action_ht_txchwidth { + struct ieee80211_action at_header; + uint8_t at_chwidth; +} __packed; + +#define IEEE80211_A_HT_TXCHWIDTH_20 0 +#define IEEE80211_A_HT_TXCHWIDTH_2040 1 + +/* HT - MIMO Power Save (NB: D2.04) */ +struct ieee80211_action_ht_mimopowersave { + struct ieee80211_action am_header; + uint8_t am_control; +} __packed; + +#define IEEE80211_A_HT_MIMOPWRSAVE_ENA 0x01 /* PS enabled */ +#define IEEE80211_A_HT_MIMOPWRSAVE_MODE 0x02 +#define IEEE80211_A_HT_MIMOPWRSAVE_MODE_S 1 +#define IEEE80211_A_HT_MIMOPWRSAVE_DYNAMIC 0x02 /* Dynamic Mode */ +#define IEEE80211_A_HT_MIMOPWRSAVE_STATIC 0x00 /* no SM packets */ +/* bits 2-7 reserved */ + +/* Block Ack actions */ +#define IEEE80211_ACTION_BA_ADDBA_REQUEST 0 /* ADDBA request */ +#define IEEE80211_ACTION_BA_ADDBA_RESPONSE 1 /* ADDBA response */ +#define IEEE80211_ACTION_BA_DELBA 2 /* DELBA */ + +/* Block Ack Parameter Set */ +#define IEEE80211_BAPS_BUFSIZ 0xffc0 /* buffer size */ +#define IEEE80211_BAPS_BUFSIZ_S 6 +#define IEEE80211_BAPS_TID 0x003c /* TID */ +#define IEEE80211_BAPS_TID_S 2 +#define IEEE80211_BAPS_POLICY 0x0002 /* block ack policy */ +#define IEEE80211_BAPS_POLICY_S 1 + +#define IEEE80211_BAPS_POLICY_DELAYED (0<<IEEE80211_BAPS_POLICY_S) +#define IEEE80211_BAPS_POLICY_IMMEDIATE (1<<IEEE80211_BAPS_POLICY_S) + +/* Block Ack Sequence Control */ +#define IEEE80211_BASEQ_START 0xfff0 /* starting seqnum */ +#define IEEE80211_BASEQ_START_S 4 +#define IEEE80211_BASEQ_FRAG 0x000f /* fragment number */ +#define IEEE80211_BASEQ_FRAG_S 0 + +/* Delayed Block Ack Parameter Set */ +#define IEEE80211_DELBAPS_TID 0xf000 /* TID */ +#define IEEE80211_DELBAPS_TID_S 12 +#define IEEE80211_DELBAPS_INIT 0x0800 /* initiator */ +#define IEEE80211_DELBAPS_INIT_S 11 + +/* BA - ADDBA request */ +struct ieee80211_action_ba_addbarequest { + struct ieee80211_action rq_header; + uint8_t rq_dialogtoken; + uint16_t rq_baparamset; + uint16_t rq_batimeout; /* in TUs */ + uint16_t rq_baseqctl; +} __packed; + +/* BA - ADDBA response */ +struct ieee80211_action_ba_addbaresponse { + struct ieee80211_action rs_header; + uint8_t rs_dialogtoken; + uint16_t rs_statuscode; + uint16_t rs_baparamset; + uint16_t rs_batimeout; /* in TUs */ +} __packed; + +/* BA - DELBA */ +struct ieee80211_action_ba_delba { + struct ieee80211_action dl_header; + uint16_t dl_baparamset; + uint16_t dl_reasoncode; +} __packed; + +/* BAR Control */ +#define IEEE80211_BAR_TID 0xf000 /* TID */ +#define IEEE80211_BAR_TID_S 12 +#define IEEE80211_BAR_COMP 0x0004 /* Compressed Bitmap */ +#define IEEE80211_BAR_MTID 0x0002 /* Multi-TID */ +#define IEEE80211_BAR_NOACK 0x0001 /* No-Ack policy */ + +/* BAR Starting Sequence Control */ +#define IEEE80211_BAR_SEQ_START 0xfff0 /* starting seqnum */ +#define IEEE80211_BAR_SEQ_START_S 4 + +struct ieee80211_ba_request { + uint16_t rq_barctl; + uint16_t rq_barseqctl; +} __packed; + +/* + * Control frames. + */ +struct ieee80211_frame_min { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_rts { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_ra[IEEE80211_ADDR_LEN]; + uint8_t i_ta[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_cts { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_ra[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_ack { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_ra[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_pspoll { + uint8_t i_fc[2]; + uint8_t i_aid[2]; + uint8_t i_bssid[IEEE80211_ADDR_LEN]; + uint8_t i_ta[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ + uint8_t i_fc[2]; + uint8_t i_dur[2]; /* should be zero */ + uint8_t i_ra[IEEE80211_ADDR_LEN]; + uint8_t i_bssid[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_bar { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_ra[IEEE80211_ADDR_LEN]; + uint8_t i_ta[IEEE80211_ADDR_LEN]; + uint16_t i_ctl; + uint16_t i_seq; + /* FCS */ +} __packed; + +/* + * BEACON management packets + * + * octet timestamp[8] + * octet beacon interval[2] + * octet capability information[2] + * information element + * octet elemid + * octet length + * octet information[length] + */ + +#define IEEE80211_BEACON_INTERVAL(beacon) \ + ((beacon)[8] | ((beacon)[9] << 8)) +#define IEEE80211_BEACON_CAPABILITY(beacon) \ + ((beacon)[10] | ((beacon)[11] << 8)) + +#define IEEE80211_CAPINFO_ESS 0x0001 +#define IEEE80211_CAPINFO_IBSS 0x0002 +#define IEEE80211_CAPINFO_CF_POLLABLE 0x0004 +#define IEEE80211_CAPINFO_CF_POLLREQ 0x0008 +#define IEEE80211_CAPINFO_PRIVACY 0x0010 +#define IEEE80211_CAPINFO_SHORT_PREAMBLE 0x0020 +#define IEEE80211_CAPINFO_PBCC 0x0040 +#define IEEE80211_CAPINFO_CHNL_AGILITY 0x0080 +#define IEEE80211_CAPINFO_SPECTRUM_MGMT 0x0100 +/* bit 9 is reserved */ +#define IEEE80211_CAPINFO_SHORT_SLOTTIME 0x0400 +#define IEEE80211_CAPINFO_RSN 0x0800 +/* bit 12 is reserved */ +#define IEEE80211_CAPINFO_DSSSOFDM 0x2000 +/* bits 14-15 are reserved */ + +#define IEEE80211_CAPINFO_BITS \ + "\20\1ESS\2IBSS\3CF_POLLABLE\4CF_POLLREQ\5PRIVACY\6SHORT_PREAMBLE" \ + "\7PBCC\10CHNL_AGILITY\11SPECTRUM_MGMT\13SHORT_SLOTTIME\14RSN" \ + "\16DSSOFDM" + +/* + * 802.11i/WPA information element (maximally sized). + */ +struct ieee80211_ie_wpa { + uint8_t wpa_id; /* IEEE80211_ELEMID_VENDOR */ + uint8_t wpa_len; /* length in bytes */ + uint8_t wpa_oui[3]; /* 0x00, 0x50, 0xf2 */ + uint8_t wpa_type; /* OUI type */ + uint16_t wpa_version; /* spec revision */ + uint32_t wpa_mcipher[1]; /* multicast/group key cipher */ + uint16_t wpa_uciphercnt; /* # pairwise key ciphers */ + uint32_t wpa_uciphers[8];/* ciphers */ + uint16_t wpa_authselcnt; /* authentication selector cnt*/ + uint32_t wpa_authsels[8];/* selectors */ + uint16_t wpa_caps; /* 802.11i capabilities */ + uint16_t wpa_pmkidcnt; /* 802.11i pmkid count */ + uint16_t wpa_pmkids[8]; /* 802.11i pmkids */ +} __packed; + +/* + * 802.11n HT Capability IE + * NB: these reflect D1.10 + */ +struct ieee80211_ie_htcap { + uint8_t hc_id; /* element ID */ + uint8_t hc_len; /* length in bytes */ + uint16_t hc_cap; /* HT caps (see below) */ + uint8_t hc_param; /* HT params (see below) */ + uint8_t hc_mcsset[16]; /* supported MCS set */ + uint16_t hc_extcap; /* extended HT capabilities */ + uint32_t hc_txbf; /* txbf capabilities */ + uint8_t hc_antenna; /* antenna capabilities */ +} __packed; + +/* HT capability flags (ht_cap) */ +#define IEEE80211_HTCAP_LDPC 0x0001 /* LDPC supported */ +#define IEEE80211_HTCAP_CHWIDTH40 0x0002 /* 20/40 supported */ +#define IEEE80211_HTCAP_SMPS 0x000c /* SM Power Save mode */ +#define IEEE80211_HTCAP_SMPS_OFF 0x000c /* disabled */ +#define IEEE80211_HTCAP_SMPS_DYNAMIC 0x0004 /* send RTS first */ +/* NB: SMPS value 2 is reserved */ +#define IEEE80211_HTCAP_SMPS_ENA 0x0000 /* enabled (static mode) */ +#define IEEE80211_HTCAP_GREENFIELD 0x0010 /* Greenfield supported */ +#define IEEE80211_HTCAP_SHORTGI20 0x0020 /* Short GI in 20MHz */ +#define IEEE80211_HTCAP_SHORTGI40 0x0040 /* Short GI in 40MHz */ +#define IEEE80211_HTCAP_TXSTBC 0x0080 /* STBC tx ok */ +#define IEEE80211_HTCAP_RXSTBC 0x0300 /* STBC rx support */ +#define IEEE80211_HTCAP_RXSTBC_S 8 +#define IEEE80211_HTCAP_RXSTBC_1STREAM 0x0100 /* 1 spatial stream */ +#define IEEE80211_HTCAP_RXSTBC_2STREAM 0x0200 /* 1-2 spatial streams*/ +#define IEEE80211_HTCAP_RXSTBC_3STREAM 0x0300 /* 1-3 spatial streams*/ +#define IEEE80211_HTCAP_DELBA 0x0400 /* HT DELBA supported */ +#define IEEE80211_HTCAP_MAXAMSDU 0x0800 /* max A-MSDU length */ +#define IEEE80211_HTCAP_MAXAMSDU_7935 0x0800 /* 7935 octets */ +#define IEEE80211_HTCAP_MAXAMSDU_3839 0x0000 /* 3839 octets */ +#define IEEE80211_HTCAP_DSSSCCK40 0x1000 /* DSSS/CCK in 40MHz */ +#define IEEE80211_HTCAP_PSMP 0x2000 /* PSMP supported */ +#define IEEE80211_HTCAP_40INTOLERANT 0x4000 /* 40MHz intolerant */ +#define IEEE80211_HTCAP_LSIGTXOPPROT 0x8000 /* L-SIG TXOP prot */ + +#define IEEE80211_HTCAP_BITS \ + "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \ + "\13DELBA\14AMSDU(7935)\15DSSSCCK40\16PSMP\1740INTOLERANT" \ + "\20LSIGTXOPPROT" + +/* HT parameters (hc_param) */ +#define IEEE80211_HTCAP_MAXRXAMPDU 0x03 /* max rx A-MPDU factor */ +#define IEEE80211_HTCAP_MAXRXAMPDU_S 0 +#define IEEE80211_HTCAP_MAXRXAMPDU_8K 0 +#define IEEE80211_HTCAP_MAXRXAMPDU_16K 1 +#define IEEE80211_HTCAP_MAXRXAMPDU_32K 2 +#define IEEE80211_HTCAP_MAXRXAMPDU_64K 3 +#define IEEE80211_HTCAP_MPDUDENSITY 0x1c /* min MPDU start spacing */ +#define IEEE80211_HTCAP_MPDUDENSITY_S 2 +#define IEEE80211_HTCAP_MPDUDENSITY_NA 0 /* no time restriction */ +#define IEEE80211_HTCAP_MPDUDENSITY_025 1 /* 1/4 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_05 2 /* 1/2 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_1 3 /* 1 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_2 4 /* 2 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_4 5 /* 4 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_8 6 /* 8 us */ +#define IEEE80211_HTCAP_MPDUDENSITY_16 7 /* 16 us */ + +/* HT extended capabilities (hc_extcap) */ +#define IEEE80211_HTCAP_PCO 0x0001 /* PCO capable */ +#define IEEE80211_HTCAP_PCOTRANS 0x0006 /* PCO transition time */ +#define IEEE80211_HTCAP_PCOTRANS_S 1 +#define IEEE80211_HTCAP_PCOTRANS_04 0x0002 /* 400 us */ +#define IEEE80211_HTCAP_PCOTRANS_15 0x0004 /* 1.5 ms */ +#define IEEE80211_HTCAP_PCOTRANS_5 0x0006 /* 5 ms */ +/* bits 3-7 reserved */ +#define IEEE80211_HTCAP_MCSFBACK 0x0300 /* MCS feedback */ +#define IEEE80211_HTCAP_MCSFBACK_S 8 +#define IEEE80211_HTCAP_MCSFBACK_NONE 0x0000 /* nothing provided */ +#define IEEE80211_HTCAP_MCSFBACK_UNSOL 0x0200 /* unsolicited feedback */ +#define IEEE80211_HTCAP_MCSFBACK_MRQ 0x0300 /* " "+respond to MRQ */ +#define IEEE80211_HTCAP_HTC 0x0400 /* +HTC support */ +#define IEEE80211_HTCAP_RDR 0x0800 /* reverse direction responder*/ +/* bits 12-15 reserved */ + +/* + * 802.11n HT Information IE + */ +struct ieee80211_ie_htinfo { + uint8_t hi_id; /* element ID */ + uint8_t hi_len; /* length in bytes */ + uint8_t hi_ctrlchannel; /* primary channel */ + uint8_t hi_byte1; /* ht ie byte 1 */ + uint8_t hi_byte2; /* ht ie byte 2 */ + uint8_t hi_byte3; /* ht ie byte 3 */ + uint16_t hi_byte45; /* ht ie bytes 4+5 */ + uint8_t hi_basicmcsset[16]; /* basic MCS set */ +} __packed; + +/* byte1 */ +#define IEEE80211_HTINFO_2NDCHAN 0x03 /* secondary/ext chan offset */ +#define IEEE80211_HTINFO_2NDCHAN_S 0 +#define IEEE80211_HTINFO_2NDCHAN_NONE 0x00 /* no secondary/ext channel */ +#define IEEE80211_HTINFO_2NDCHAN_ABOVE 0x01 /* above private channel */ +/* NB: 2 is reserved */ +#define IEEE80211_HTINFO_2NDCHAN_BELOW 0x03 /* below primary channel */ +#define IEEE80211_HTINFO_TXWIDTH 0x04 /* tx channel width */ +#define IEEE80211_HTINFO_TXWIDTH_20 0x00 /* 20MHz width */ +#define IEEE80211_HTINFO_TXWIDTH_2040 0x04 /* any supported width */ +#define IEEE80211_HTINFO_RIFSMODE 0x08 /* Reduced IFS (RIFS) use */ +#define IEEE80211_HTINFO_RIFSMODE_PROH 0x00 /* RIFS use prohibited */ +#define IEEE80211_HTINFO_RIFSMODE_PERM 0x08 /* RIFS use permitted */ +#define IEEE80211_HTINFO_PMSPONLY 0x10 /* PSMP required to associate */ +#define IEEE80211_HTINFO_SIGRAN 0xe0 /* shortest Service Interval */ +#define IEEE80211_HTINFO_SIGRAN_S 5 +#define IEEE80211_HTINFO_SIGRAN_5 0x00 /* 5 ms */ +/* XXX add rest */ + +/* bytes 2+3 */ +#define IEEE80211_HTINFO_OPMODE 0x03 /* operating mode */ +#define IEEE80211_HTINFO_OPMODE_S 0 +#define IEEE80211_HTINFO_OPMODE_PURE 0x00 /* no protection */ +#define IEEE80211_HTINFO_OPMODE_PROTOPT 0x01 /* protection optional */ +#define IEEE80211_HTINFO_OPMODE_HT20PR 0x02 /* protection for HT20 sta's */ +#define IEEE80211_HTINFO_OPMODE_MIXED 0x03 /* protection for legacy sta's*/ +#define IEEE80211_HTINFO_NONGF_PRESENT 0x04 /* non-GF sta's present */ +#define IEEE80211_HTINFO_TXBL 0x08 /* transmit burst limit */ +#define IEEE80211_HTINFO_NONHT_PRESENT 0x10 /* non-HT sta's present */ +/* bits 5-15 reserved */ + +/* bytes 4+5 */ +#define IEEE80211_HTINFO_2NDARYBEACON 0x01 +#define IEEE80211_HTINFO_LSIGTXOPPROT 0x02 +#define IEEE80211_HTINFO_PCO_ACTIVE 0x04 +#define IEEE80211_HTINFO_40MHZPHASE 0x08 + +/* byte5 */ +#define IEEE80211_HTINFO_BASIC_STBCMCS 0x7f +#define IEEE80211_HTINFO_BASIC_STBCMCS_S 0 +#define IEEE80211_HTINFO_DUALPROTECTED 0x80 + +/* + * Management information element payloads. + */ + +enum { + IEEE80211_ELEMID_SSID = 0, + IEEE80211_ELEMID_RATES = 1, + IEEE80211_ELEMID_FHPARMS = 2, + IEEE80211_ELEMID_DSPARMS = 3, + IEEE80211_ELEMID_CFPARMS = 4, + IEEE80211_ELEMID_TIM = 5, + IEEE80211_ELEMID_IBSSPARMS = 6, + IEEE80211_ELEMID_COUNTRY = 7, + IEEE80211_ELEMID_CHALLENGE = 16, + /* 17-31 reserved for challenge text extension */ + IEEE80211_ELEMID_PWRCNSTR = 32, + IEEE80211_ELEMID_PWRCAP = 33, + IEEE80211_ELEMID_TPCREQ = 34, + IEEE80211_ELEMID_TPCREP = 35, + IEEE80211_ELEMID_SUPPCHAN = 36, + IEEE80211_ELEMID_CSA = 37, + IEEE80211_ELEMID_MEASREQ = 38, + IEEE80211_ELEMID_MEASREP = 39, + IEEE80211_ELEMID_QUIET = 40, + IEEE80211_ELEMID_IBSSDFS = 41, + IEEE80211_ELEMID_ERP = 42, + IEEE80211_ELEMID_HTCAP = 45, + IEEE80211_ELEMID_RSN = 48, + IEEE80211_ELEMID_XRATES = 50, + IEEE80211_ELEMID_HTINFO = 61, + IEEE80211_ELEMID_TPC = 150, + IEEE80211_ELEMID_CCKM = 156, + IEEE80211_ELEMID_VENDOR = 221, /* vendor private */ + + /* + * 802.11s IEs based on D3.03 spec and were not assigned by + * ANA. Beware changing them because some of them are being + * kept compatible with Linux. + */ + IEEE80211_ELEMID_MESHCONF = 51, + IEEE80211_ELEMID_MESHID = 52, + IEEE80211_ELEMID_MESHLINK = 35, + IEEE80211_ELEMID_MESHCNGST = 36, + IEEE80211_ELEMID_MESHPEER = 55, + IEEE80211_ELEMID_MESHCSA = 38, + IEEE80211_ELEMID_MESHTIM = 39, + IEEE80211_ELEMID_MESHAWAKEW = 40, + IEEE80211_ELEMID_MESHBEACONT = 41, + IEEE80211_ELEMID_MESHPANN = 48, + IEEE80211_ELEMID_MESHRANN = 49, + IEEE80211_ELEMID_MESHPREQ = 68, + IEEE80211_ELEMID_MESHPREP = 69, + IEEE80211_ELEMID_MESHPERR = 70, + IEEE80211_ELEMID_MESHPXU = 53, + IEEE80211_ELEMID_MESHPXUC = 54, + IEEE80211_ELEMID_MESHAH = 60, /* Abbreviated Handshake */ +}; + +struct ieee80211_tim_ie { + uint8_t tim_ie; /* IEEE80211_ELEMID_TIM */ + uint8_t tim_len; + uint8_t tim_count; /* DTIM count */ + uint8_t tim_period; /* DTIM period */ + uint8_t tim_bitctl; /* bitmap control */ + uint8_t tim_bitmap[1]; /* variable-length bitmap */ +} __packed; + +struct ieee80211_country_ie { + uint8_t ie; /* IEEE80211_ELEMID_COUNTRY */ + uint8_t len; + uint8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */ + struct { + uint8_t schan; /* starting channel */ + uint8_t nchan; /* number channels */ + uint8_t maxtxpwr; /* tx power cap */ + } __packed band[1]; /* sub bands (NB: var size) */ +} __packed; + +#define IEEE80211_COUNTRY_MAX_BANDS 84 /* max possible bands */ +#define IEEE80211_COUNTRY_MAX_SIZE \ + (sizeof(struct ieee80211_country_ie) + 3*(IEEE80211_COUNTRY_MAX_BANDS-1)) + +/* + * 802.11h Channel Switch Announcement (CSA). + */ +struct ieee80211_csa_ie { + uint8_t csa_ie; /* IEEE80211_ELEMID_CHANSWITCHANN */ + uint8_t csa_len; + uint8_t csa_mode; /* Channel Switch Mode */ + uint8_t csa_newchan; /* New Channel Number */ + uint8_t csa_count; /* Channel Switch Count */ +} __packed; + +/* + * Note the min acceptable CSA count is used to guard against + * malicious CSA injection in station mode. Defining this value + * as other than 0 violates the 11h spec. + */ +#define IEEE80211_CSA_COUNT_MIN 2 +#define IEEE80211_CSA_COUNT_MAX 255 + +/* rate set entries are in .5 Mb/s units, and potentially marked as basic */ +#define IEEE80211_RATE_BASIC 0x80 +#define IEEE80211_RATE_VAL 0x7f + +/* EPR information element flags */ +#define IEEE80211_ERP_NON_ERP_PRESENT 0x01 +#define IEEE80211_ERP_USE_PROTECTION 0x02 +#define IEEE80211_ERP_LONG_PREAMBLE 0x04 + +#define IEEE80211_ERP_BITS \ + "\20\1NON_ERP_PRESENT\2USE_PROTECTION\3LONG_PREAMBLE" + +#define ATH_OUI 0x7f0300 /* Atheros OUI */ +#define ATH_OUI_TYPE 0x01 /* Atheros protocol ie */ + +/* NB: Atheros allocated the OUI for this purpose ~2005 but beware ... */ +#define TDMA_OUI ATH_OUI +#define TDMA_OUI_TYPE 0x02 /* TDMA protocol ie */ + +#define BCM_OUI 0x4c9000 /* Broadcom OUI */ +#define BCM_OUI_HTCAP 51 /* pre-draft HTCAP ie */ +#define BCM_OUI_HTINFO 52 /* pre-draft HTINFO ie */ + +#define WPA_OUI 0xf25000 +#define WPA_OUI_TYPE 0x01 +#define WPA_VERSION 1 /* current supported version */ + +#define WPA_CSE_NULL 0x00 +#define WPA_CSE_WEP40 0x01 +#define WPA_CSE_TKIP 0x02 +#define WPA_CSE_CCMP 0x04 +#define WPA_CSE_WEP104 0x05 + +#define WPA_ASE_NONE 0x00 +#define WPA_ASE_8021X_UNSPEC 0x01 +#define WPA_ASE_8021X_PSK 0x02 + +#define WPS_OUI_TYPE 0x04 + +#define RSN_OUI 0xac0f00 +#define RSN_VERSION 1 /* current supported version */ + +#define RSN_CSE_NULL 0x00 +#define RSN_CSE_WEP40 0x01 +#define RSN_CSE_TKIP 0x02 +#define RSN_CSE_WRAP 0x03 +#define RSN_CSE_CCMP 0x04 +#define RSN_CSE_WEP104 0x05 + +#define RSN_ASE_NONE 0x00 +#define RSN_ASE_8021X_UNSPEC 0x01 +#define RSN_ASE_8021X_PSK 0x02 + +#define RSN_CAP_PREAUTH 0x01 + +#define WME_OUI 0xf25000 +#define WME_OUI_TYPE 0x02 +#define WME_INFO_OUI_SUBTYPE 0x00 +#define WME_PARAM_OUI_SUBTYPE 0x01 +#define WME_VERSION 1 + +/* WME stream classes */ +#define WME_AC_BE 0 /* best effort */ +#define WME_AC_BK 1 /* background */ +#define WME_AC_VI 2 /* video */ +#define WME_AC_VO 3 /* voice */ + +/* + * AUTH management packets + * + * octet algo[2] + * octet seq[2] + * octet status[2] + * octet chal.id + * octet chal.length + * octet chal.text[253] NB: 1-253 bytes + */ + +/* challenge length for shared key auth */ +#define IEEE80211_CHALLENGE_LEN 128 + +#define IEEE80211_AUTH_ALG_OPEN 0x0000 +#define IEEE80211_AUTH_ALG_SHARED 0x0001 +#define IEEE80211_AUTH_ALG_LEAP 0x0080 + +enum { + IEEE80211_AUTH_OPEN_REQUEST = 1, + IEEE80211_AUTH_OPEN_RESPONSE = 2, +}; + +enum { + IEEE80211_AUTH_SHARED_REQUEST = 1, + IEEE80211_AUTH_SHARED_CHALLENGE = 2, + IEEE80211_AUTH_SHARED_RESPONSE = 3, + IEEE80211_AUTH_SHARED_PASS = 4, +}; + +/* + * Reason and status codes. + * + * Reason codes are used in management frames to indicate why an + * action took place (e.g. on disassociation). Status codes are + * used in management frames to indicate the result of an operation. + * + * Unlisted codes are reserved + */ + +enum { + IEEE80211_REASON_UNSPECIFIED = 1, + IEEE80211_REASON_AUTH_EXPIRE = 2, + IEEE80211_REASON_AUTH_LEAVE = 3, + IEEE80211_REASON_ASSOC_EXPIRE = 4, + IEEE80211_REASON_ASSOC_TOOMANY = 5, + IEEE80211_REASON_NOT_AUTHED = 6, + IEEE80211_REASON_NOT_ASSOCED = 7, + IEEE80211_REASON_ASSOC_LEAVE = 8, + IEEE80211_REASON_ASSOC_NOT_AUTHED = 9, + IEEE80211_REASON_DISASSOC_PWRCAP_BAD = 10, /* 11h */ + IEEE80211_REASON_DISASSOC_SUPCHAN_BAD = 11, /* 11h */ + IEEE80211_REASON_IE_INVALID = 13, /* 11i */ + IEEE80211_REASON_MIC_FAILURE = 14, /* 11i */ + IEEE80211_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, /* 11i */ + IEEE80211_REASON_GROUP_KEY_UPDATE_TIMEOUT = 16, /* 11i */ + IEEE80211_REASON_IE_IN_4WAY_DIFFERS = 17, /* 11i */ + IEEE80211_REASON_GROUP_CIPHER_INVALID = 18, /* 11i */ + IEEE80211_REASON_PAIRWISE_CIPHER_INVALID= 19, /* 11i */ + IEEE80211_REASON_AKMP_INVALID = 20, /* 11i */ + IEEE80211_REASON_UNSUPP_RSN_IE_VERSION = 21, /* 11i */ + IEEE80211_REASON_INVALID_RSN_IE_CAP = 22, /* 11i */ + IEEE80211_REASON_802_1X_AUTH_FAILED = 23, /* 11i */ + IEEE80211_REASON_CIPHER_SUITE_REJECTED = 24, /* 11i */ + IEEE80211_REASON_UNSPECIFIED_QOS = 32, /* 11e */ + IEEE80211_REASON_INSUFFICIENT_BW = 33, /* 11e */ + IEEE80211_REASON_TOOMANY_FRAMES = 34, /* 11e */ + IEEE80211_REASON_OUTSIDE_TXOP = 35, /* 11e */ + IEEE80211_REASON_LEAVING_QBSS = 36, /* 11e */ + IEEE80211_REASON_BAD_MECHANISM = 37, /* 11e */ + IEEE80211_REASON_SETUP_NEEDED = 38, /* 11e */ + IEEE80211_REASON_TIMEOUT = 39, /* 11e */ + + /* values not yet allocated by ANA */ + IEEE80211_REASON_PEER_LINK_CANCELED = 2, /* 11s */ + IEEE80211_REASON_MESH_MAX_PEERS = 3, /* 11s */ + IEEE80211_REASON_MESH_CPVIOLATION = 4, /* 11s */ + IEEE80211_REASON_MESH_CLOSE_RCVD = 5, /* 11s */ + IEEE80211_REASON_MESH_MAX_RETRIES = 6, /* 11s */ + IEEE80211_REASON_MESH_CONFIRM_TIMEOUT = 7, /* 11s */ + IEEE80211_REASON_MESH_INVALID_GTK = 8, /* 11s */ + IEEE80211_REASON_MESH_INCONS_PARAMS = 9, /* 11s */ + IEEE80211_REASON_MESH_INVALID_SECURITY = 10, /* 11s */ + IEEE80211_REASON_MESH_PERR_UNSPEC = 11, /* 11s */ + IEEE80211_REASON_MESH_PERR_NO_FI = 12, /* 11s */ + IEEE80211_REASON_MESH_PERR_DEST_UNREACH = 13, /* 11s */ + + IEEE80211_STATUS_SUCCESS = 0, + IEEE80211_STATUS_UNSPECIFIED = 1, + IEEE80211_STATUS_CAPINFO = 10, + IEEE80211_STATUS_NOT_ASSOCED = 11, + IEEE80211_STATUS_OTHER = 12, + IEEE80211_STATUS_ALG = 13, + IEEE80211_STATUS_SEQUENCE = 14, + IEEE80211_STATUS_CHALLENGE = 15, + IEEE80211_STATUS_TIMEOUT = 16, + IEEE80211_STATUS_TOOMANY = 17, + IEEE80211_STATUS_BASIC_RATE = 18, + IEEE80211_STATUS_SP_REQUIRED = 19, /* 11b */ + IEEE80211_STATUS_PBCC_REQUIRED = 20, /* 11b */ + IEEE80211_STATUS_CA_REQUIRED = 21, /* 11b */ + IEEE80211_STATUS_SPECMGMT_REQUIRED = 22, /* 11h */ + IEEE80211_STATUS_PWRCAP_REQUIRED = 23, /* 11h */ + IEEE80211_STATUS_SUPCHAN_REQUIRED = 24, /* 11h */ + IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25, /* 11g */ + IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26, /* 11g */ + IEEE80211_STATUS_MISSING_HT_CAPS = 27, /* 11n D3.0 */ + IEEE80211_STATUS_INVALID_IE = 40, /* 11i */ + IEEE80211_STATUS_GROUP_CIPHER_INVALID = 41, /* 11i */ + IEEE80211_STATUS_PAIRWISE_CIPHER_INVALID = 42, /* 11i */ + IEEE80211_STATUS_AKMP_INVALID = 43, /* 11i */ + IEEE80211_STATUS_UNSUPP_RSN_IE_VERSION = 44, /* 11i */ + IEEE80211_STATUS_INVALID_RSN_IE_CAP = 45, /* 11i */ + IEEE80211_STATUS_CIPHER_SUITE_REJECTED = 46, /* 11i */ +}; + +#define IEEE80211_WEP_KEYLEN 5 /* 40bit */ +#define IEEE80211_WEP_IVLEN 3 /* 24bit */ +#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ +#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ +#define IEEE80211_WEP_TOTLEN (IEEE80211_WEP_IVLEN + \ + IEEE80211_WEP_KIDLEN + \ + IEEE80211_WEP_CRCLEN) +#define IEEE80211_WEP_NKID 4 /* number of key ids */ + +/* + * 802.11i defines an extended IV for use with non-WEP ciphers. + * When the EXTIV bit is set in the key id byte an additional + * 4 bytes immediately follow the IV for TKIP. For CCMP the + * EXTIV bit is likewise set but the 8 bytes represent the + * CCMP header rather than IV+extended-IV. + */ +#define IEEE80211_WEP_EXTIV 0x20 +#define IEEE80211_WEP_EXTIVLEN 4 /* extended IV length */ +#define IEEE80211_WEP_MICLEN 8 /* trailing MIC */ + +#define IEEE80211_CRC_LEN 4 + +/* + * Maximum acceptable MTU is: + * IEEE80211_MAX_LEN - WEP overhead - CRC - + * QoS overhead - RSN/WPA overhead + * Min is arbitrarily chosen > IEEE80211_MIN_LEN. The default + * mtu is Ethernet-compatible; it's set by ether_ifattach. + */ +#define IEEE80211_MTU_MAX 2290 +#define IEEE80211_MTU_MIN 32 + +#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \ + (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN)) +#define IEEE80211_ACK_LEN \ + (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN) +#define IEEE80211_MIN_LEN \ + (sizeof(struct ieee80211_frame_min) + IEEE80211_CRC_LEN) + +/* + * 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 IEEE80211_AID_DEF. + * This number may be overridden by the driver and/or by + * user configuration but may not be less than IEEE80211_AID_MIN + * (see _ieee80211.h for implementation-specific settings). + */ +#define IEEE80211_AID_MAX 2007 + +#define IEEE80211_AID(b) ((b) &~ 0xc000) + +/* + * RTS frame length parameters. The default is specified in + * the 802.11 spec as 512; we treat it as implementation-dependent + * so it's defined in ieee80211_var.h. The max may be wrong + * for jumbo frames. + */ +#define IEEE80211_RTS_MIN 1 +#define IEEE80211_RTS_MAX 2346 + +/* + * TX fragmentation parameters. As above for RTS, we treat + * default as implementation-dependent so define it elsewhere. + */ +#define IEEE80211_FRAG_MIN 256 +#define IEEE80211_FRAG_MAX 2346 + +/* + * Beacon interval (TU's). Min+max come from WiFi requirements. + * As above, we treat default as implementation-dependent so + * define it elsewhere. + */ +#define IEEE80211_BINTVAL_MAX 1000 /* max beacon interval (TU's) */ +#define IEEE80211_BINTVAL_MIN 25 /* min beacon interval (TU's) */ + +/* + * DTIM period (beacons). Min+max are not really defined + * by the protocol but we want them publicly visible so + * define them here. + */ +#define IEEE80211_DTIM_MAX 15 /* max DTIM period */ +#define IEEE80211_DTIM_MIN 1 /* min DTIM period */ + +/* + * Beacon miss threshold (beacons). As for DTIM, we define + * them here to be publicly visible. Note the max may be + * clamped depending on device capabilities. + */ +#define IEEE80211_HWBMISS_MIN 1 +#define IEEE80211_HWBMISS_MAX 255 + +/* + * 802.11 frame duration definitions. + */ + +struct ieee80211_duration { + uint16_t d_rts_dur; + uint16_t d_data_dur; + uint16_t d_plcp_len; + uint8_t d_residue; /* unused octets in time slot */ +}; + +/* One Time Unit (TU) is 1Kus = 1024 microseconds. */ +#define IEEE80211_DUR_TU 1024 + +/* IEEE 802.11b durations for DSSS PHY in microseconds */ +#define IEEE80211_DUR_DS_LONG_PREAMBLE 144 +#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72 + +#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48 +#define IEEE80211_DUR_DS_FAST_PLCPHDR 24 +#define IEEE80211_DUR_DS_SLOW_ACK 112 +#define IEEE80211_DUR_DS_FAST_ACK 56 +#define IEEE80211_DUR_DS_SLOW_CTS 112 +#define IEEE80211_DUR_DS_FAST_CTS 56 + +#define IEEE80211_DUR_DS_SLOT 20 +#define IEEE80211_DUR_DS_SIFS 10 +#define IEEE80211_DUR_DS_PIFS (IEEE80211_DUR_DS_SIFS + IEEE80211_DUR_DS_SLOT) +#define IEEE80211_DUR_DS_DIFS (IEEE80211_DUR_DS_SIFS + \ + 2 * IEEE80211_DUR_DS_SLOT) +#define IEEE80211_DUR_DS_EIFS (IEEE80211_DUR_DS_SIFS + \ + IEEE80211_DUR_DS_SLOW_ACK + \ + IEEE80211_DUR_DS_LONG_PREAMBLE + \ + IEEE80211_DUR_DS_SLOW_PLCPHDR + \ + IEEE80211_DUR_DIFS) + +#endif /* _NET80211_IEEE80211_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_acl.c b/freebsd/sys/net80211/ieee80211_acl.c new file mode 100644 index 00000000..955989a6 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_acl.c @@ -0,0 +1,341 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/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 <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/module.h> +#include <freebsd/sys/queue.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> +#include <freebsd/net/route.h> + +#include <freebsd/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); diff --git a/freebsd/sys/net80211/ieee80211_action.c b/freebsd/sys/net80211/ieee80211_action.c new file mode 100644 index 00000000..4bd5c700 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_action.c @@ -0,0 +1,281 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 send/recv action frame support. + */ + +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/systm.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_action.h> +#include <freebsd/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 +} diff --git a/freebsd/sys/net80211/ieee80211_action.h b/freebsd/sys/net80211/ieee80211_action.h new file mode 100644 index 00000000..943d145b --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_action.h @@ -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 _NET80211_IEEE80211_ACTION_HH_ +#define _NET80211_IEEE80211_ACTION_HH_ + +/* + * 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 /* _NET80211_IEEE80211_ACTION_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_adhoc.c b/freebsd/sys/net80211/ieee80211_adhoc.c new file mode 100644 index 00000000..44d91041 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_adhoc.c @@ -0,0 +1,929 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 IBSS mode support. + */ +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/errno.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net/bpf.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_adhoc.h> +#include <freebsd/net80211/ieee80211_input.h> +#ifdef IEEE80211_SUPPORT_SUPERG +#include <freebsd/net80211/ieee80211_superg.h> +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include <freebsd/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 = 1; /* mbuf need to be tapped. */ + 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; + + 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) +{ +} diff --git a/freebsd/sys/net80211/ieee80211_adhoc.h b/freebsd/sys/net80211/ieee80211_adhoc.h new file mode 100644 index 00000000..2fad984e --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_adhoc.h @@ -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 _NET80211_IEEE80211_ADHOC_HH_ +#define _NET80211_IEEE80211_ADHOC_HH_ + +/* + * Adhoc-mode (ibss+ahdemo) implementation definitions. + */ +void ieee80211_adhoc_attach(struct ieee80211com *); +void ieee80211_adhoc_detach(struct ieee80211com *); +#endif /* !_NET80211_IEEE80211_STA_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_ageq.c b/freebsd/sys/net80211/ieee80211_ageq.c new file mode 100644 index 00000000..2e838e35 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_ageq.c @@ -0,0 +1,239 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 age queue support. + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/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; +} diff --git a/freebsd/sys/net80211/ieee80211_ageq.h b/freebsd/sys/net80211/ieee80211_ageq.h new file mode 100644 index 00000000..8aecae05 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_ageq.h @@ -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 _NET80211_IEEE80211_STAGEQ_HH_ +#define _NET80211_IEEE80211_STAGEQ_HH_ + +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 /* _NET80211_IEEE80211_STAGEQ_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_amrr.c b/freebsd/sys/net80211/ieee80211_amrr.c new file mode 100644 index 00000000..33f6c5f3 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_amrr.c @@ -0,0 +1,319 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/* $OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $ */ + +/*- + * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org> + * Copyright (c) 2006 + * Damien Bergamini <damien.bergamini@free.fr> + * + * 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 <freebsd/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 <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/module.h> +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> + +#ifdef INET +#include <freebsd/netinet/in.h> +#include <freebsd/netinet/if_ether.h> +#endif + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_amrr.h> +#include <freebsd/net80211/ieee80211_ratectl.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_setinterval(const struct ieee80211vap *, int); +static void amrr_init(struct ieee80211vap *); +static void amrr_deinit(struct ieee80211vap *); +static void amrr_node_init(struct ieee80211_node *); +static void amrr_node_deinit(struct ieee80211_node *); +static int amrr_update(struct ieee80211_amrr *, + struct ieee80211_amrr_node *, struct ieee80211_node *); +static int amrr_rate(struct ieee80211_node *, void *, uint32_t); +static void amrr_tx_complete(const struct ieee80211vap *, + const struct ieee80211_node *, int, + void *, void *); +static void amrr_tx_update(const struct ieee80211vap *vap, + const struct ieee80211_node *, void *, void *, void *); +static void amrr_sysctlattach(struct ieee80211vap *, + struct sysctl_ctx_list *, struct sysctl_oid *); + +/* number of references from net80211 layer */ +static int nrefs = 0; + +static const struct ieee80211_ratectl amrr = { + .ir_name = "amrr", + .ir_attach = NULL, + .ir_detach = NULL, + .ir_init = amrr_init, + .ir_deinit = amrr_deinit, + .ir_node_init = amrr_node_init, + .ir_node_deinit = amrr_node_deinit, + .ir_rate = amrr_rate, + .ir_tx_complete = amrr_tx_complete, + .ir_tx_update = amrr_tx_update, + .ir_setinterval = amrr_setinterval, +}; +IEEE80211_RATECTL_MODULE(amrr, 1); +IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr); + +static void +amrr_setinterval(const struct ieee80211vap *vap, int msecs) +{ + struct ieee80211_amrr *amrr = vap->iv_rs; + int t; + + if (msecs < 100) + msecs = 100; + t = msecs_to_ticks(msecs); + amrr->amrr_interval = (t < 1) ? 1 : t; +} + +static void +amrr_init(struct ieee80211vap *vap) +{ + struct ieee80211_amrr *amrr; + + KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__)); + + amrr = vap->iv_rs = malloc(sizeof(struct ieee80211_amrr), + M_80211_RATECTL, M_NOWAIT|M_ZERO); + if (amrr == NULL) { + if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); + return; + } + amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD; + amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD; + amrr_setinterval(vap, 500 /* ms */); + amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid); +} + +static void +amrr_deinit(struct ieee80211vap *vap) +{ + free(vap->iv_rs, M_80211_RATECTL); +} + +static void +amrr_node_init(struct ieee80211_node *ni) +{ + const struct ieee80211_rateset *rs = &ni->ni_rates; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_amrr *amrr = vap->iv_rs; + struct ieee80211_amrr_node *amn; + + if (ni->ni_rctls == NULL) { + ni->ni_rctls = amn = malloc(sizeof(struct ieee80211_amrr_node), + M_80211_RATECTL, M_NOWAIT|M_ZERO); + if (amn == NULL) { + if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " + "structure\n"); + return; + } + } else + amn = ni->ni_rctls; + 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 void +amrr_node_deinit(struct ieee80211_node *ni) +{ + free(ni->ni_rctls, M_80211_RATECTL); +} + +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. + */ +static int +amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) +{ + struct ieee80211_amrr_node *amn = ni->ni_rctls; + 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; +} + +/* + * 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 void +amrr_tx_complete(const struct ieee80211vap *vap, + const struct ieee80211_node *ni, int ok, + void *arg1, void *arg2 __unused) +{ + struct ieee80211_amrr_node *amn = ni->ni_rctls; + int retries = *(int *)arg1; + + 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 void +amrr_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni, + void *arg1, void *arg2, void *arg3) +{ + struct ieee80211_amrr_node *amn = ni->ni_rctls; + int txcnt = *(int *)arg1, success = *(int *)arg2, retrycnt = *(int *)arg3; + + amn->amn_txcnt = txcnt; + amn->amn_success = success; + amn->amn_retrycnt = retrycnt; +} + +static int +amrr_sysctl_interval(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211vap *vap = arg1; + struct ieee80211_amrr *amrr = vap->iv_rs; + int msecs = ticks_to_msecs(amrr->amrr_interval); + int error; + + error = sysctl_handle_int(oidp, &msecs, 0, req); + if (error || !req->newptr) + return error; + amrr_setinterval(vap, msecs); + return 0; +} + +static void +amrr_sysctlattach(struct ieee80211vap *vap, + struct sysctl_ctx_list *ctx, struct sysctl_oid *tree) +{ + struct ieee80211_amrr *amrr = vap->iv_rs; + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap, + 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, ""); +} diff --git a/freebsd/sys/net80211/ieee80211_amrr.h b/freebsd/sys/net80211/ieee80211_amrr.h new file mode 100644 index 00000000..b425e268 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_amrr.h @@ -0,0 +1,61 @@ +/* $FreeBSD$ */ +/* $OpenBSD: ieee80211_amrr.h,v 1.3 2006/06/17 19:34:31 damien Exp $ */ + +/*- + * Copyright (c) 2006 + * Damien Bergamini <damien.bergamini@free.fr> + * + * 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 _NET80211_IEEE80211_AMRR_HH_ +#define _NET80211_IEEE80211_AMRR_HH_ + +/*- + * 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; +}; + +#endif /* _NET80211_IEEE80211_AMRR_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_crypto.c b/freebsd/sys/net80211/ieee80211_crypto.c new file mode 100644 index 00000000..c3a263c8 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_crypto.c @@ -0,0 +1,663 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 generic crypto support. + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/mbuf.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> /* XXX ETHER_HDR_LEN */ + +#include <freebsd/net80211/ieee80211_var.h> + +MALLOC_DEFINE(M_80211_CRYPTO, "80211crypto", "802.11 crypto state"); + +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); +} diff --git a/freebsd/sys/net80211/ieee80211_crypto.h b/freebsd/sys/net80211/ieee80211_crypto.h new file mode 100644 index 00000000..b9e8e25b --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_crypto.h @@ -0,0 +1,245 @@ +/*- + * 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 _NET80211_IEEE80211_CRYPTO_HH_ +#define _NET80211_IEEE80211_CRYPTO_HH_ + +/* + * 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; + +MALLOC_DECLARE(M_80211_CRYPTO); + +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 /* _NET80211_IEEE80211_CRYPTO_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_crypto_ccmp.c b/freebsd/sys/net80211/ieee80211_crypto_ccmp.c new file mode 100644 index 00000000..82443508 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_crypto_ccmp.c @@ -0,0 +1,636 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/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 <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/module.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net80211/ieee80211_var.h> + +#include <freebsd/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 <jkmaline@cc.hut.fi> + * + * 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); diff --git a/freebsd/sys/net80211/ieee80211_crypto_none.c b/freebsd/sys/net80211/ieee80211_crypto_none.c new file mode 100644 index 00000000..f9112a9f --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_crypto_none.c @@ -0,0 +1,146 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 NULL crypto support. + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/module.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/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; +} diff --git a/freebsd/sys/net80211/ieee80211_crypto_tkip.c b/freebsd/sys/net80211/ieee80211_crypto_tkip.c new file mode 100644 index 00000000..6b0656e2 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_crypto_tkip.c @@ -0,0 +1,1000 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/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 <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/module.h> +#include <freebsd/sys/endian.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/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 */ + ctx->rx_phase1_done = 0; + 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 <jkmaline@cc.hut.fi> + * + * 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); diff --git a/freebsd/sys/net80211/ieee80211_crypto_wep.c b/freebsd/sys/net80211/ieee80211_crypto_wep.c new file mode 100644 index 00000000..01b3c37d --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_crypto_wep.c @@ -0,0 +1,482 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 WEP crypto support. + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/module.h> +#include <freebsd/sys/endian.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/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); diff --git a/freebsd/sys/net80211/ieee80211_ddb.c b/freebsd/sys/net80211/ieee80211_ddb.c new file mode 100644 index 00000000..f3b30431 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_ddb.c @@ -0,0 +1,881 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <freebsd/local/opt_ddb.h> +#include <freebsd/local/opt_wlan.h> + +#ifdef DDB +/* + * IEEE 802.11 DDB support + */ +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_dl.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_types.h> +#include <freebsd/net/ethernet.h> +#include <freebsd/net/vnet.h> + +#include <freebsd/net80211/ieee80211_var.h> +#ifdef IEEE80211_SUPPORT_TDMA +#include <freebsd/net80211/ieee80211_tdma.h> +#endif +#ifdef IEEE80211_SUPPORT_MESH +#include <freebsd/net80211/ieee80211_mesh.h> +#endif + +#include <freebsd/ddb/ddb.h> +#include <freebsd/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 */ diff --git a/freebsd/sys/net80211/ieee80211_dfs.c b/freebsd/sys/net80211/ieee80211_dfs.c new file mode 100644 index 00000000..803ed337 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_dfs.c @@ -0,0 +1,379 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 DFS/Radar support. + */ +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/errno.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> + +#include <freebsd/net80211/ieee80211_var.h> + +MALLOC_DEFINE(M_80211_DFS, "80211dfs", "802.11 DFS state"); + +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; +} diff --git a/freebsd/sys/net80211/ieee80211_dfs.h b/freebsd/sys/net80211/ieee80211_dfs.h new file mode 100644 index 00000000..474b2078 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_dfs.h @@ -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 _NET80211_IEEE80211_DFS_HH_ +#define _NET80211_IEEE80211_DFS_HH_ + +/* + * 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 /* _NET80211_IEEE80211_DFS_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_freebsd.c b/freebsd/sys/net80211/ieee80211_freebsd.c new file mode 100644 index 00000000..4188f112 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_freebsd.c @@ -0,0 +1,831 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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. + */ + +#include <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 support (FreeBSD-specific code) + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/linker.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/module.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/bpf.h> +#include <freebsd/net/if.h> +#include <freebsd/net/if_dl.h> +#include <freebsd/net/if_clone.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_types.h> +#include <freebsd/net/ethernet.h> +#include <freebsd/net/route.h> +#include <freebsd/net/vnet.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_input.h> + +SYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters"); + +#ifdef IEEE80211_DEBUG +int ieee80211_debug = 0; +SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug, + 0, "debugging printfs"); +#endif + +MALLOC_DEFINE(M_80211_COM, "80211com", "802.11 com state"); + +/* + * Allocate/free com structure in conjunction with ifnet; + * these routines are registered with if_register_com_alloc + * below and are called automatically by the ifnet code + * when the ifnet of the parent device is created. + */ +static void * +wlan_alloc(u_char type, struct ifnet *ifp) +{ + struct ieee80211com *ic; + + ic = malloc(sizeof(struct ieee80211com), M_80211_COM, M_WAITOK|M_ZERO); + ic->ic_ifp = ifp; + + return (ic); +} + +static void +wlan_free(void *ic, u_char type) +{ + free(ic, M_80211_COM); +} + +static int +wlan_clone_create(struct if_clone *ifc, int unit, caddr_t params) +{ + struct ieee80211_clone_params cp; + struct ieee80211vap *vap; + struct ieee80211com *ic; + struct ifnet *ifp; + int error; + + error = copyin(params, &cp, sizeof(cp)); + if (error) + return error; + ifp = ifunit(cp.icp_parent); + if (ifp == NULL) + return ENXIO; + /* XXX move printfs to DIAGNOSTIC before release */ + if (ifp->if_type != IFT_IEEE80211) { + if_printf(ifp, "%s: reject, not an 802.11 device\n", __func__); + return ENXIO; + } + if (cp.icp_opmode >= IEEE80211_OPMODE_MAX) { + if_printf(ifp, "%s: invalid opmode %d\n", + __func__, cp.icp_opmode); + return EINVAL; + } + ic = ifp->if_l2com; + if ((ic->ic_caps & ieee80211_opcap[cp.icp_opmode]) == 0) { + if_printf(ifp, "%s mode not supported\n", + ieee80211_opmode_name[cp.icp_opmode]); + return EOPNOTSUPP; + } + if ((cp.icp_flags & IEEE80211_CLONE_TDMA) && +#ifdef IEEE80211_SUPPORT_TDMA + (ic->ic_caps & IEEE80211_C_TDMA) == 0 +#else + (1) +#endif + ) { + if_printf(ifp, "TDMA not supported\n"); + return EOPNOTSUPP; + } + vap = ic->ic_vap_create(ic, ifc->ifc_name, unit, + cp.icp_opmode, cp.icp_flags, cp.icp_bssid, + cp.icp_flags & IEEE80211_CLONE_MACADDR ? + cp.icp_macaddr : (const uint8_t *)IF_LLADDR(ifp)); + return (vap == NULL ? EIO : 0); +} + +static void +wlan_clone_destroy(struct ifnet *ifp) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + + ic->ic_vap_delete(vap); +} +IFC_SIMPLE_DECLARE(wlan, 0); + +void +ieee80211_vap_destroy(struct ieee80211vap *vap) +{ + if_clone_destroyif(&wlan_cloner, vap->iv_ifp); +} + +int +ieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS) +{ + int msecs = ticks_to_msecs(*(int *)arg1); + int error, t; + + error = sysctl_handle_int(oidp, &msecs, 0, req); + if (error || !req->newptr) + return error; + t = msecs_to_ticks(msecs); + *(int *)arg1 = (t < 1) ? 1 : t; + return 0; +} + +static int +ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS) +{ + int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT; + int error; + + error = sysctl_handle_int(oidp, &inact, 0, req); + if (error || !req->newptr) + return error; + *(int *)arg1 = inact / IEEE80211_INACT_WAIT; + return 0; +} + +static int +ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211com *ic = arg1; + const char *name = ic->ic_ifp->if_xname; + + return SYSCTL_OUT(req, name, strlen(name)); +} + +static int +ieee80211_sysctl_radar(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211com *ic = arg1; + int t = 0, error; + + error = sysctl_handle_int(oidp, &t, 0, req); + if (error || !req->newptr) + return error; + IEEE80211_LOCK(ic); + ieee80211_dfs_notify_radar(ic, ic->ic_curchan); + IEEE80211_UNLOCK(ic); + return 0; +} + +void +ieee80211_sysctl_attach(struct ieee80211com *ic) +{ +} + +void +ieee80211_sysctl_detach(struct ieee80211com *ic) +{ +} + +void +ieee80211_sysctl_vattach(struct ieee80211vap *vap) +{ + struct ifnet *ifp = vap->iv_ifp; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *oid; + char num[14]; /* sufficient for 32 bits */ + + ctx = (struct sysctl_ctx_list *) malloc(sizeof(struct sysctl_ctx_list), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) { + if_printf(ifp, "%s: cannot allocate sysctl context!\n", + __func__); + return; + } + sysctl_ctx_init(ctx); + snprintf(num, sizeof(num), "%u", ifp->if_dunit); + oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan), + OID_AUTO, num, CTLFLAG_RD, NULL, ""); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "%parent", CTLFLAG_RD, vap->iv_ic, 0, + ieee80211_sysctl_parent, "A", "parent device"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "driver_caps", CTLFLAG_RW, &vap->iv_caps, 0, + "driver capabilities"); +#ifdef IEEE80211_DEBUG + vap->iv_debug = ieee80211_debug; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "debug", CTLFLAG_RW, &vap->iv_debug, 0, + "control debugging printfs"); +#endif + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "bmiss_max", CTLFLAG_RW, &vap->iv_bmiss_max, 0, + "consecutive beacon misses before scanning"); + /* XXX inherit from tunables */ + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_run", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_run, 0, + ieee80211_sysctl_inact, "I", + "station inactivity timeout (sec)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_probe, 0, + ieee80211_sysctl_inact, "I", + "station inactivity probe timeout (sec)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_auth, 0, + ieee80211_sysctl_inact, "I", + "station authentication timeout (sec)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "inact_init", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_init, 0, + ieee80211_sysctl_inact, "I", + "station initial state timeout (sec)"); + if (vap->iv_htcaps & IEEE80211_HTC_HT) { + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_bk", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_BK], 0, + "BK traffic tx aggr threshold (pps)"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_be", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_BE], 0, + "BE traffic tx aggr threshold (pps)"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_vo", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_VO], 0, + "VO traffic tx aggr threshold (pps)"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "ampdu_mintraffic_vi", CTLFLAG_RW, + &vap->iv_ampdu_mintraffic[WME_AC_VI], 0, + "VI traffic tx aggr threshold (pps)"); + } + if (vap->iv_caps & IEEE80211_C_DFS) { + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + "radar", CTLTYPE_INT | CTLFLAG_RW, vap->iv_ic, 0, + ieee80211_sysctl_radar, "I", "simulate radar event"); + } + vap->iv_sysctl = ctx; + vap->iv_oid = oid; +} + +void +ieee80211_sysctl_vdetach(struct ieee80211vap *vap) +{ + + if (vap->iv_sysctl != NULL) { + sysctl_ctx_free(vap->iv_sysctl); + free(vap->iv_sysctl, M_DEVBUF); + vap->iv_sysctl = NULL; + } +} + +int +ieee80211_node_dectestref(struct ieee80211_node *ni) +{ + /* XXX need equivalent of atomic_dec_and_test */ + atomic_subtract_int(&ni->ni_refcnt, 1); + return atomic_cmpset_int(&ni->ni_refcnt, 0, 1); +} + +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); +} + +/* + * As above, for mbufs allocated with m_gethdr/MGETHDR + * or initialized by M_COPY_PKTHDR. + */ +#define MC_ALIGN(m, len) \ +do { \ + (m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1); \ +} while (/* CONSTCOND */ 0) + +/* + * Allocate and setup a management frame of the specified + * size. We return the mbuf and a pointer to the start + * of the contiguous data area that's been reserved based + * on the packet length. The data area is forced to 32-bit + * alignment and the buffer length to a multiple of 4 bytes. + * This is done mainly so beacon frames (that require this) + * can use this interface too. + */ +struct mbuf * +ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen) +{ + struct mbuf *m; + u_int len; + + /* + * NB: we know the mbuf routines will align the data area + * so we don't need to do anything special. + */ + 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); + /* + * Align the data in case additional headers are added. + * This should only happen when a WEP header is added + * which only happens for shared key authentication mgt + * frames which all fit in MHLEN. + */ + 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; +} + +/* + * Re-align the payload in the mbuf. This is mainly used (right now) + * to handle IP header alignment requirements on certain architectures. + */ +struct mbuf * +ieee80211_realign(struct ieee80211vap *vap, struct mbuf *m, size_t align) +{ + int pktlen, space; + struct mbuf *n; + + pktlen = m->m_pkthdr.len; + space = pktlen + align; + if (space < MINCLSIZE) + n = m_gethdr(M_DONTWAIT, MT_DATA); + else { + n = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, + space <= MCLBYTES ? MCLBYTES : +#if MJUMPAGESIZE != MCLBYTES + space <= MJUMPAGESIZE ? MJUMPAGESIZE : +#endif + space <= MJUM9BYTES ? MJUM9BYTES : MJUM16BYTES); + } + if (__predict_true(n != NULL)) { + m_move_pkthdr(n, m); + n->m_data = (caddr_t)(ALIGN(n->m_data + align) - align); + m_copydata(m, 0, pktlen, mtod(n, caddr_t)); + n->m_len = pktlen; + } else { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + mtod(m, const struct ieee80211_frame *), NULL, + "%s", "no mbuf to realign"); + vap->iv_stats.is_rx_badalign++; + } + m_freem(m); + return n; +} + +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); + } +} + +#include <freebsd/sys/libkern.h> + +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; + } +} + +/* + * Helper function for events that pass just a single mac address. + */ +static void +notify_macaddr(struct ifnet *ifp, int op, const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_join_event iev; + + CURVNET_SET(ifp->if_vnet); + memset(&iev, 0, sizeof(iev)); + IEEE80211_ADDR_COPY(iev.iev_addr, mac); + rt_ieee80211msg(ifp, op, &iev, sizeof(iev)); + CURVNET_RESTORE(); +} + +void +ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + CURVNET_SET_QUIET(ifp->if_vnet); + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode join", + (ni == vap->iv_bss) ? "bss " : ""); + + if (ni == vap->iv_bss) { + notify_macaddr(ifp, newassoc ? + RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, ni->ni_bssid); + if_link_state_change(ifp, LINK_STATE_UP); + } else { + notify_macaddr(ifp, newassoc ? + RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, ni->ni_macaddr); + } + CURVNET_RESTORE(); +} + +void +ieee80211_notify_node_leave(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + CURVNET_SET_QUIET(ifp->if_vnet); + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode leave", + (ni == vap->iv_bss) ? "bss " : ""); + + if (ni == vap->iv_bss) { + rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0); + if_link_state_change(ifp, LINK_STATE_DOWN); + } else { + /* fire off wireless event station leaving */ + notify_macaddr(ifp, RTM_IEEE80211_LEAVE, ni->ni_macaddr); + } + CURVNET_RESTORE(); +} + +void +ieee80211_notify_scan_done(struct ieee80211vap *vap) +{ + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done"); + + /* dispatch wireless event indicating scan completed */ + CURVNET_SET(ifp->if_vnet); + rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0); + CURVNET_RESTORE(); +} + +void +ieee80211_notify_replay_failure(struct ieee80211vap *vap, + const struct ieee80211_frame *wh, const struct ieee80211_key *k, + u_int64_t rsc, int tid) +{ + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "%s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>", + k->wk_cipher->ic_name, (intmax_t) rsc, + (intmax_t) k->wk_keyrsc[tid], + k->wk_keyix, k->wk_rxkeyix); + + if (ifp != NULL) { /* NB: for cipher test modules */ + struct ieee80211_replay_event iev; + + IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); + IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); + iev.iev_cipher = k->wk_cipher->ic_cipher; + if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE) + iev.iev_keyix = k->wk_rxkeyix; + else + iev.iev_keyix = k->wk_keyix; + iev.iev_keyrsc = k->wk_keyrsc[tid]; + iev.iev_rsc = rsc; + CURVNET_SET(ifp->if_vnet); + rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev)); + CURVNET_RESTORE(); + } +} + +void +ieee80211_notify_michael_failure(struct ieee80211vap *vap, + const struct ieee80211_frame *wh, u_int keyix) +{ + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, + "michael MIC verification failed <keyix %u>", keyix); + vap->iv_stats.is_rx_tkipmic++; + + if (ifp != NULL) { /* NB: for cipher test modules */ + struct ieee80211_michael_event iev; + + IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1); + IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2); + iev.iev_cipher = IEEE80211_CIPHER_TKIP; + iev.iev_keyix = keyix; + CURVNET_SET(ifp->if_vnet); + rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev)); + CURVNET_RESTORE(); + } +} + +void +ieee80211_notify_wds_discover(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + notify_macaddr(ifp, RTM_IEEE80211_WDS, ni->ni_macaddr); +} + +void +ieee80211_notify_csa(struct ieee80211com *ic, + const struct ieee80211_channel *c, int mode, int count) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_csa_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_flags = c->ic_flags; + iev.iev_freq = c->ic_freq; + iev.iev_ieee = c->ic_ieee; + iev.iev_mode = mode; + iev.iev_count = count; + rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev)); +} + +void +ieee80211_notify_radar(struct ieee80211com *ic, + const struct ieee80211_channel *c) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_radar_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_flags = c->ic_flags; + iev.iev_freq = c->ic_freq; + iev.iev_ieee = c->ic_ieee; + rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev)); +} + +void +ieee80211_notify_cac(struct ieee80211com *ic, + const struct ieee80211_channel *c, enum ieee80211_notify_cac_event type) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_cac_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_flags = c->ic_flags; + iev.iev_freq = c->ic_freq; + iev.iev_ieee = c->ic_ieee; + iev.iev_type = type; + rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev)); +} + +void +ieee80211_notify_node_deauth(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node deauth"); + + notify_macaddr(ifp, RTM_IEEE80211_DEAUTH, ni->ni_macaddr); +} + +void +ieee80211_notify_node_auth(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + + IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node auth"); + + notify_macaddr(ifp, RTM_IEEE80211_AUTH, ni->ni_macaddr); +} + +void +ieee80211_notify_country(struct ieee80211vap *vap, + const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2]) +{ + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_country_event iev; + + memset(&iev, 0, sizeof(iev)); + IEEE80211_ADDR_COPY(iev.iev_addr, bssid); + iev.iev_cc[0] = cc[0]; + iev.iev_cc[1] = cc[1]; + rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev)); +} + +void +ieee80211_notify_radio(struct ieee80211com *ic, int state) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_radio_event iev; + + memset(&iev, 0, sizeof(iev)); + iev.iev_state = state; + rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev)); +} + +void +ieee80211_load_module(const char *modname) +{ + +#ifdef notyet + (void)kern_kldload(curthread, modname, NULL); +#else + printf("%s: load the %s module by hand for now.\n", __func__, modname); +#endif +} + +static eventhandler_tag wlan_bpfevent; +static eventhandler_tag wlan_ifllevent; + +static void +bpf_track(void *arg, struct ifnet *ifp, int dlt, int attach) +{ + /* NB: identify vap's by if_start */ + if (dlt == DLT_IEEE802_11_RADIO && ifp->if_start == ieee80211_start) { + struct ieee80211vap *vap = ifp->if_softc; + /* + * Track bpf radiotap listener state. We mark the vap + * to indicate if any listener is present and the com + * to indicate if any listener exists on any associated + * vap. This flag is used by drivers to prepare radiotap + * state only when needed. + */ + if (attach) { + ieee80211_syncflag_ext(vap, IEEE80211_FEXT_BPF); + if (vap->iv_opmode == IEEE80211_M_MONITOR) + atomic_add_int(&vap->iv_ic->ic_montaps, 1); + } else if (!bpf_peers_present(vap->iv_rawbpf)) { + ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_BPF); + if (vap->iv_opmode == IEEE80211_M_MONITOR) + atomic_subtract_int(&vap->iv_ic->ic_montaps, 1); + } + } +} + +static void +wlan_iflladdr(void *arg __unused, struct ifnet *ifp) +{ + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap, *next; + + if (ifp->if_type != IFT_IEEE80211 || ic == NULL) + return; + + IEEE80211_LOCK(ic); + TAILQ_FOREACH_SAFE(vap, &ic->ic_vaps, iv_next, next) { + /* + * If the MAC address has changed on the parent and it was + * copied to the vap on creation then re-sync. + */ + if (vap->iv_ic == ic && + (vap->iv_flags_ext & IEEE80211_FEXT_UNIQMAC) == 0) { + IEEE80211_ADDR_COPY(vap->iv_myaddr, IF_LLADDR(ifp)); + IEEE80211_UNLOCK(ic); + if_setlladdr(vap->iv_ifp, IF_LLADDR(ifp), + IEEE80211_ADDR_LEN); + IEEE80211_LOCK(ic); + } + } + IEEE80211_UNLOCK(ic); +} + +/* + * Module glue. + * + * NB: the module name is "wlan" for compatibility with NetBSD. + */ +static int +wlan_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + if (bootverbose) + printf("wlan: <802.11 Link Layer>\n"); + wlan_bpfevent = EVENTHANDLER_REGISTER(bpf_track, + bpf_track, 0, EVENTHANDLER_PRI_ANY); + if (wlan_bpfevent == NULL) + return ENOMEM; + wlan_ifllevent = EVENTHANDLER_REGISTER(iflladdr_event, + wlan_iflladdr, NULL, EVENTHANDLER_PRI_ANY); + if (wlan_ifllevent == NULL) { + EVENTHANDLER_DEREGISTER(bpf_track, wlan_bpfevent); + return ENOMEM; + } + if_clone_attach(&wlan_cloner); + if_register_com_alloc(IFT_IEEE80211, wlan_alloc, wlan_free); + return 0; + case MOD_UNLOAD: + if_deregister_com_alloc(IFT_IEEE80211); + if_clone_detach(&wlan_cloner); + EVENTHANDLER_DEREGISTER(bpf_track, wlan_bpfevent); + EVENTHANDLER_DEREGISTER(iflladdr_event, wlan_ifllevent); + return 0; + } + return EINVAL; +} + +static moduledata_t wlan_mod = { + "wlan", + wlan_modevent, + 0 +}; +DECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan, 1); +MODULE_DEPEND(wlan, ether, 1, 1, 1); diff --git a/freebsd/sys/net80211/ieee80211_freebsd.h b/freebsd/sys/net80211/ieee80211_freebsd.h new file mode 100644 index 00000000..69efecd0 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_freebsd.h @@ -0,0 +1,550 @@ +/*- + * 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 _NET80211_IEEE80211_FREEBSD_HH_ +#define _NET80211_IEEE80211_FREEBSD_HH_ + +#ifdef _KERNEL +#include <freebsd/sys/param.h> +#include <freebsd/sys/lock.h> +#include <freebsd/sys/mutex.h> +#include <freebsd/sys/rwlock.h> +#include <freebsd/sys/sysctl.h> +#include <freebsd/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) + +/* + * 802.1x MAC ACL database locking definitions. + */ +typedef struct mtx acl_lock_t; +#define ACL_LOCK_INIT(_as, _name) \ + mtx_init(&(_as)->as_lock, _name, "802.11 ACL", MTX_DEF) +#define ACL_LOCK_DESTROY(_as) mtx_destroy(&(_as)->as_lock) +#define ACL_LOCK(_as) mtx_lock(&(_as)->as_lock) +#define ACL_UNLOCK(_as) mtx_unlock(&(_as)->as_lock) +#define ACL_LOCK_ASSERT(_as) \ + mtx_assert((&(_as)->as_lock), MA_OWNED) + +/* + * Scan table definitions. + */ +typedef struct mtx ieee80211_scan_table_lock_t; +#define IEEE80211_SCAN_TABLE_LOCK_INIT(_st, _name) \ + mtx_init(&(_st)->st_lock, _name, "802.11 scan table", MTX_DEF) +#define IEEE80211_SCAN_TABLE_LOCK_DESTROY(_st) mtx_destroy(&(_st)->st_lock) +#define IEEE80211_SCAN_TABLE_LOCK(_st) mtx_lock(&(_st)->st_lock) +#define IEEE80211_SCAN_TABLE_UNLOCK(_st) mtx_unlock(&(_st)->st_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 <freebsd/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)(b) - (long)(a) < 0) +#define time_before(a,b) time_after(b,a) +#define time_after_eq(a,b) ((long)(a) - (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) + +#define IEEE80211_MBUF_TX_FLAG_BITS \ + "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_ENCAP\6M_WEP\7M_EAPOL" \ + "\10M_PWR_SAV\11M_MORE_DATA\12M_BCAST\13M_MCAST\14M_FRAG\15M_FIRSTFRAG" \ + "\16M_LASTFRAG\17M_SKIP_FIREWALL\20M_FREELIST\21M_VLANTAG\22M_PROMISC" \ + "\23M_NOFREE\24M_FF\25M_TXCB\26M_AMPDU_MPDU\27M_FLOWID" + +#define IEEE80211_MBUF_RX_FLAG_BITS \ + "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_AMPDU\6M_WEP\7M_PROTO3" \ + "\10M_PROTO4\11M_PROTO5\12M_BCAST\13M_MCAST\14M_FRAG\15M_FIRSTFRAG" \ + "\16M_LASTFRAG\17M_SKIP_FIREWALL\20M_FREELIST\21M_VLANTAG\22M_PROMISC" \ + "\23M_NOFREE\24M_PROTO6\25M_PROTO7\26M_AMPDU_MPDU\27M_FLOWID" + +/* + * 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 *); + +SYSCTL_DECL(_net_wlan); +int ieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS); + +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); \ +static int \ +wlan_##name##_modevent(module_t mod, int type, void *unused) \ +{ \ + policy##_setup * const *iter, f; \ + switch (type) { \ + case MOD_LOAD: \ + SET_FOREACH(iter, policy##_set) { \ + f = (void*) *iter; \ + f(type); \ + } \ + return 0; \ + case MOD_UNLOAD: \ + case MOD_QUIESCE: \ + if (nrefs) { \ + printf("wlan_##name: still in use (%u dynamic refs)\n",\ + nrefs); \ + return EBUSY; \ + } \ + if (type == MOD_UNLOAD) { \ + SET_FOREACH(iter, policy##_set) { \ + f = (void*) *iter; \ + f(type); \ + } \ + } \ + return 0; \ + } \ + return EINVAL; \ +} \ +static moduledata_t name##_mod = { \ + "wlan_" #name, \ + wlan_##name##_modevent, \ + 0 \ +}; \ +DECLARE_MODULE(wlan_##name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\ +MODULE_VERSION(wlan_##name, version); \ +MODULE_DEPEND(wlan_##name, wlan, 1, 1, 1) + +/* + * Crypto modules implement cipher support. + */ +#define IEEE80211_CRYPTO_MODULE(name, version) \ +_IEEE80211_POLICY_MODULE(crypto, name, version); \ +static void \ +name##_modevent(int type) \ +{ \ + if (type == MOD_LOAD) \ + ieee80211_crypto_register(&name); \ + else \ + ieee80211_crypto_unregister(&name); \ +} \ +TEXT_SET(crypto##_set, name##_modevent) + +/* + * 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); \ + +/* + * ACL modules implement acl policy. + */ +#define IEEE80211_ACL_MODULE(name, alg, version) \ +_IEEE80211_POLICY_MODULE(acl, name, version); \ +static void \ +alg##_modevent(int type) \ +{ \ + if (type == MOD_LOAD) \ + ieee80211_aclator_register(&alg); \ + else \ + ieee80211_aclator_unregister(&alg); \ +} \ +TEXT_SET(acl_set, alg##_modevent); \ + +/* + * Authenticator modules handle 802.1x/WPA authentication. + */ +#define IEEE80211_AUTH_MODULE(name, version) \ + _IEEE80211_POLICY_MODULE(auth, name, version) + +#define IEEE80211_AUTH_ALG(name, alg, v) \ +static void \ +name##_modevent(int type) \ +{ \ + if (type == MOD_LOAD) \ + ieee80211_authenticator_register(alg, &v); \ + else \ + ieee80211_authenticator_unregister(alg); \ +} \ +TEXT_SET(auth_set, name##_modevent) + +/* + * Rate control modules provide tx rate control support. + */ +#define IEEE80211_RATECTL_MODULE(alg, version) \ + _IEEE80211_POLICY_MODULE(ratectl, alg, version); \ + +#define IEEE80211_RATECTL_ALG(name, alg, v) \ +static void \ +alg##_modevent(int type) \ +{ \ + if (type == MOD_LOAD) \ + ieee80211_ratectl_register(alg, &v); \ + else \ + ieee80211_ratectl_unregister(alg); \ +} \ +TEXT_SET(ratectl##_set, alg##_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 */ + +/* XXX this stuff belongs elsewhere */ +/* + * Message formats for messages from the net80211 layer to user + * applications via the routing socket. These messages are appended + * to an if_announcemsghdr structure. + */ +struct ieee80211_join_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_leave_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_replay_event { + uint8_t iev_src[6]; /* src MAC */ + uint8_t iev_dst[6]; /* dst MAC */ + uint8_t iev_cipher; /* cipher type */ + uint8_t iev_keyix; /* key id/index */ + uint64_t iev_keyrsc; /* RSC from key */ + uint64_t iev_rsc; /* RSC from frame */ +}; + +struct ieee80211_michael_event { + uint8_t iev_src[6]; /* src MAC */ + uint8_t iev_dst[6]; /* dst MAC */ + uint8_t iev_cipher; /* cipher type */ + uint8_t iev_keyix; /* key id/index */ +}; + +struct ieee80211_wds_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_csa_event { + uint32_t iev_flags; /* channel flags */ + uint16_t iev_freq; /* setting in Mhz */ + uint8_t iev_ieee; /* IEEE channel number */ + uint8_t iev_mode; /* CSA mode */ + uint8_t iev_count; /* CSA count */ +}; + +struct ieee80211_cac_event { + uint32_t iev_flags; /* channel flags */ + uint16_t iev_freq; /* setting in Mhz */ + uint8_t iev_ieee; /* IEEE channel number */ + /* XXX timestamp? */ + uint8_t iev_type; /* IEEE80211_NOTIFY_CAC_* */ +}; + +struct ieee80211_radar_event { + uint32_t iev_flags; /* channel flags */ + uint16_t iev_freq; /* setting in Mhz */ + uint8_t iev_ieee; /* IEEE channel number */ + /* XXX timestamp? */ +}; + +struct ieee80211_auth_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_deauth_event { + uint8_t iev_addr[6]; +}; + +struct ieee80211_country_event { + uint8_t iev_addr[6]; + uint8_t iev_cc[2]; /* ISO country code */ +}; + +struct ieee80211_radio_event { + uint8_t iev_state; /* 1 on, 0 off */ +}; + +#define RTM_IEEE80211_ASSOC 100 /* station associate (bss mode) */ +#define RTM_IEEE80211_REASSOC 101 /* station re-associate (bss mode) */ +#define RTM_IEEE80211_DISASSOC 102 /* station disassociate (bss mode) */ +#define RTM_IEEE80211_JOIN 103 /* station join (ap mode) */ +#define RTM_IEEE80211_LEAVE 104 /* station leave (ap mode) */ +#define RTM_IEEE80211_SCAN 105 /* scan complete, results available */ +#define RTM_IEEE80211_REPLAY 106 /* sequence counter replay detected */ +#define RTM_IEEE80211_MICHAEL 107 /* Michael MIC failure detected */ +#define RTM_IEEE80211_REJOIN 108 /* station re-associate (ap mode) */ +#define RTM_IEEE80211_WDS 109 /* WDS discovery (ap mode) */ +#define RTM_IEEE80211_CSA 110 /* Channel Switch Announcement event */ +#define RTM_IEEE80211_RADAR 111 /* radar event */ +#define RTM_IEEE80211_CAC 112 /* Channel Availability Check event */ +#define RTM_IEEE80211_DEAUTH 113 /* station deauthenticate */ +#define RTM_IEEE80211_AUTH 114 /* station authenticate (ap mode) */ +#define RTM_IEEE80211_COUNTRY 115 /* discovered country code (sta mode) */ +#define RTM_IEEE80211_RADIO 116 /* RF kill switch state change */ + +/* + * 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 /* _NET80211_IEEE80211_FREEBSD_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_hostap.c b/freebsd/sys/net80211/ieee80211_hostap.c new file mode 100644 index 00000000..892957be --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_hostap.c @@ -0,0 +1,2307 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 HOSTAP mode support. + */ +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/errno.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net/bpf.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_hostap.h> +#include <freebsd/net80211/ieee80211_input.h> +#ifdef IEEE80211_SUPPORT_SUPERG +#include <freebsd/net80211/ieee80211_superg.h> +#endif +#include <freebsd/net80211/ieee80211_wds.h> + +#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) + +static void hostap_vattach(struct ieee80211vap *); +static int hostap_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int hostap_input(struct ieee80211_node *ni, struct mbuf *m, + int rssi, int nf); +static void hostap_deliver_data(struct ieee80211vap *, + struct ieee80211_node *, struct mbuf *); +static void hostap_recv_mgmt(struct ieee80211_node *, struct mbuf *, + int subtype, int rssi, int nf); +static void hostap_recv_ctl(struct ieee80211_node *, struct mbuf *, int); +static void hostap_recv_pspoll(struct ieee80211_node *, struct mbuf *); + +void +ieee80211_hostap_attach(struct ieee80211com *ic) +{ + ic->ic_vattach[IEEE80211_M_HOSTAP] = hostap_vattach; +} + +void +ieee80211_hostap_detach(struct ieee80211com *ic) +{ +} + +static void +hostap_vdetach(struct ieee80211vap *vap) +{ +} + +static void +hostap_vattach(struct ieee80211vap *vap) +{ + vap->iv_newstate = hostap_newstate; + vap->iv_input = hostap_input; + vap->iv_recv_mgmt = hostap_recv_mgmt; + vap->iv_recv_ctl = hostap_recv_ctl; + vap->iv_opdetach = hostap_vdetach; + vap->iv_deliver_data = hostap_deliver_data; +} + +static void +sta_disassoc(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = arg; + + if (ni->ni_vap == vap && ni->ni_associd != 0) { + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + ieee80211_node_leave(ni); + } +} + +static void +sta_csa(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = arg; + + if (ni->ni_vap == vap && ni->ni_associd != 0) + if (ni->ni_inact > vap->iv_inact_init) { + ni->ni_inact = vap->iv_inact_init; + IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, + "%s: inact %u", __func__, ni->ni_inact); + } +} + +static void +sta_drop(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = arg; + + if (ni->ni_vap == vap && ni->ni_associd != 0) + ieee80211_node_leave(ni); +} + +/* + * Does a channel change require associated stations to re-associate + * so protocol state is correct. This is used when doing CSA across + * bands or similar (e.g. HT -> legacy). + */ +static int +isbandchange(struct ieee80211com *ic) +{ + return ((ic->ic_bsschan->ic_flags ^ ic->ic_csa_newchan->ic_flags) & + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_HALF | + IEEE80211_CHAN_QUARTER | IEEE80211_CHAN_HT)) != 0; +} + +/* + * IEEE80211_M_HOSTAP vap state machine handler. + */ +static int +hostap_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 (ostate != IEEE80211_S_SCAN) + ieee80211_cancel_scan(vap); /* background scan */ + switch (nstate) { + case IEEE80211_S_INIT: + switch (ostate) { + case IEEE80211_S_SCAN: + ieee80211_cancel_scan(vap); + break; + case IEEE80211_S_CAC: + ieee80211_dfs_cac_stop(vap); + break; + case IEEE80211_S_RUN: + ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap); + break; + default: + break; + } + if (ostate != IEEE80211_S_INIT) { + /* NB: optimize INIT -> INIT case */ + ieee80211_reset_bss(vap); + } + if (vap->iv_auth->ia_detach != NULL) + vap->iv_auth->ia_detach(vap); + break; + case IEEE80211_S_SCAN: + switch (ostate) { + case IEEE80211_S_CSA: + case IEEE80211_S_RUN: + ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap); + /* + * Clear overlapping BSS state; the beacon frame + * will be reconstructed on transition to the RUN + * state and the timeout routines check if the flag + * is set before doing anything so this is sufficient. + */ + ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR; + ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR; + /* fall thru... */ + case IEEE80211_S_CAC: + /* + * NB: We may get here because of a manual channel + * change in which case we need to stop CAC + * XXX no need to stop if ostate RUN but it's ok + */ + ieee80211_dfs_cac_stop(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 will call back to + * move us to RUN state. + */ + 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: + /* + * A state change requires a reset; scan. + */ + ieee80211_check_scan_current(vap); + break; + default: + break; + } + break; + case IEEE80211_S_CAC: + /* + * Start CAC on a DFS channel. We come here when starting + * a bss on a DFS channel (see ieee80211_create_ibss). + */ + ieee80211_dfs_cac_start(vap); + break; + case IEEE80211_S_RUN: + if (vap->iv_flags & IEEE80211_F_WPA) { + /* XXX validate prerequisites */ + } + switch (ostate) { + case IEEE80211_S_INIT: + /* + * Already have a channel; bypass the + * scan and startup immediately. + * Note that ieee80211_create_ibss will call + * back to do a RUN->RUN state change. + */ + ieee80211_create_ibss(vap, + ieee80211_ht_adjust_channel(ic, + ic->ic_curchan, vap->iv_flags_ht)); + /* NB: iv_bss is changed on return */ + break; + case IEEE80211_S_CAC: + /* + * NB: This is the normal state change when CAC + * expires and no radar was detected; no need to + * clear the CAC timer as it's already expired. + */ + /* fall thru... */ + case IEEE80211_S_CSA: + /* + * Shorten inactivity timer of associated stations + * to weed out sta's that don't follow a CSA. + */ + ieee80211_iterate_nodes(&ic->ic_sta, sta_csa, vap); + /* + * Update bss node channel to reflect where + * we landed after CSA. + */ + ieee80211_node_set_chan(vap->iv_bss, + ieee80211_ht_adjust_channel(ic, ic->ic_curchan, + ieee80211_htchanflags(vap->iv_bss->ni_chan))); + /* XXX bypass debug msgs */ + break; + case IEEE80211_S_SCAN: + case IEEE80211_S_RUN: +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_debug(vap)) { + struct ieee80211_node *ni = vap->iv_bss; + ieee80211_note(vap, + "synchronized with %s ssid ", + ether_sprintf(ni->ni_bssid)); + ieee80211_print_essid(ni->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: + break; + } + /* + * Start/stop the authenticator. We delay until here + * to allow configuration to happen out of order. + */ + if (vap->iv_auth->ia_attach != NULL) { + /* XXX check failure */ + vap->iv_auth->ia_attach(vap); + } else if (vap->iv_auth->ia_detach != NULL) { + vap->iv_auth->ia_detach(vap); + } + ieee80211_node_authorize(vap->iv_bss); + break; + case IEEE80211_S_CSA: + if (ostate == IEEE80211_S_RUN && isbandchange(ic)) { + /* + * On a ``band change'' silently drop associated + * stations as they must re-associate before they + * can pass traffic (as otherwise protocol state + * such as capabilities and the negotiated rate + * set may/will be wrong). + */ + ieee80211_iterate_nodes(&ic->ic_sta, sta_drop, vap); + } + break; + default: + break; + } + return 0; +} + +static void +hostap_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); + + KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP, + ("gack, opmode %d", vap->iv_opmode)); + /* + * 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); + + /* perform as a bridge within the AP */ + if ((vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0) { + struct mbuf *mcopy = NULL; + + if (m->m_flags & M_MCAST) { + mcopy = m_dup(m, M_DONTWAIT); + if (mcopy == NULL) + ifp->if_oerrors++; + else + mcopy->m_flags |= M_MCAST; + } else { + /* + * Check if the destination is associated with the + * same vap and authorized to receive traffic. + * Beware of traffic destined for the vap itself; + * sending it will not work; just let it be delivered + * normally. + */ + struct ieee80211_node *sta = ieee80211_find_vap_node( + &vap->iv_ic->ic_sta, vap, eh->ether_dhost); + if (sta != NULL) { + if (ieee80211_node_is_authorized(sta)) { + /* + * Beware of sending to ourself; this + * needs to happen via the normal + * input path. + */ + if (sta != vap->iv_bss) { + mcopy = m; + m = NULL; + } + } else { + vap->iv_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(sta, rx_unauth); + } + ieee80211_free_node(sta); + } + } + if (mcopy != NULL) { + int len, err; + len = mcopy->m_pkthdr.len; + err = ifp->if_transmit(ifp, mcopy); + if (err) { + /* NB: IFQ_HANDOFF reclaims mcopy */ + } else { + ifp->if_opackets++; + } + } + } + if (m != NULL) { + /* + * Mark frame as coming from vap's interface. + */ + m->m_pkthdr.rcvif = ifp; + if (m->m_flags & M_MCAST) { + /* + * Spam DWDS vap's w/ multicast traffic. + */ + /* XXX only if dwds in use? */ + ieee80211_dwds_mcast(vap, m); + } + 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); + } +} + +/* + * 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 0; + } + 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 +hostap_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 = 1; /* mbuf need to be tapped. */ + 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; + + 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 (!(type == IEEE80211_FC0_TYPE_MGT && + subtype == IEEE80211_FC0_SUBTYPE_BEACON) && + !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; + } + + 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_TODS || + (dir == IEEE80211_FC1_DIR_DSTODS && + (vap->iv_flags & IEEE80211_F_DWDS)))) { + if (dir != IEEE80211_FC1_DIR_DSTODS) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_INPUT, wh, "data", + "incorrect dir 0x%x", dir); + } else { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_INPUT | + IEEE80211_MSG_WDS, wh, + "4-address data", + "%s", "DWDS not enabled"); + } + vap->iv_stats.is_rx_wrongdir++; + goto out; + } + /* check if source STA is associated */ + if (ni == vap->iv_bss) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unknown src"); + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_NOT_AUTHED); + vap->iv_stats.is_rx_notassoc++; + goto err; + } + if (ni->ni_associd == 0) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "%s", "unassoc src"); + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_NOT_ASSOCED); + vap->iv_stats.is_rx_notassoc++; + goto err; + } + + /* + * Check for power save state change. + * XXX out-of-order A-MPDU frames? + */ + if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ + (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) + ieee80211_node_pwrsave(ni, + wh->i_fc[1] & IEEE80211_FC1_PWR_MGT); + /* + * For 4-address packets handle WDS discovery + * notifications. Once a WDS link is setup frames + * are just delivered to the WDS vap (see below). + */ + if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap == NULL) { + if (!ieee80211_node_is_authorized(ni)) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_INPUT | + IEEE80211_MSG_WDS, wh, + "4-address data", + "%s", "unauthorized port"); + vap->iv_stats.is_rx_unauth++; + IEEE80211_NODE_STAT(ni, rx_unauth); + goto err; + } + ieee80211_dwds_discover(ni, m); + return type; + } + + /* + * 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 + hostap_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, "mgt", "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; + } + if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { + /* ensure return frames are unicast */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, NULL, "source is multicast: %s", + ether_sprintf(wh->i_addr2)); + vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ + 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) { + if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { + /* + * Only shared key auth frames with a challenge + * should be encrypted, discard all others. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, + "%s", "WEP set but not permitted"); + vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ + goto out; + } + if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "WEP set but PRIVACY off"); + vap->iv_stats.is_rx_noprivacy++; + goto out; + } + hdrspace = ieee80211_hdrspace(ic, wh); + key = ieee80211_crypto_decap(ni, m, hdrspace); + if (key == NULL) { + /* NB: stats+msgs handled in crypto_decap */ + goto out; + } + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + } + /* + * Pass the packet to radiotap before calling iv_recv_mgmt(). + * Otherwise iv_recv_mgmt() might pass another packet to + * radiotap, resulting in out of order packet captures. + */ + if (ieee80211_radiotap_active_vap(vap)) + ieee80211_radiotap_rx(vap, m); + need_tap = 0; + 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 void +hostap_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh, + int rssi, int nf, uint16_t seq, uint16_t status) +{ + struct ieee80211vap *vap = ni->ni_vap; + + KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state)); + + if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "open auth", + "bad sta auth mode %u", ni->ni_authmode); + vap->iv_stats.is_rx_bad_auth++; /* XXX */ + /* + * Clear any challenge text that may be there if + * a previous shared key auth failed and then an + * open auth is attempted. + */ + if (ni->ni_challenge != NULL) { + free(ni->ni_challenge, M_80211_NODE); + ni->ni_challenge = NULL; + } + /* XXX hack to workaround calling convention */ + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq + 1) | (IEEE80211_STATUS_ALG<<16)); + return; + } + if (seq != IEEE80211_AUTH_OPEN_REQUEST) { + vap->iv_stats.is_rx_bad_auth++; + return; + } + /* always accept open authentication requests */ + if (ni == vap->iv_bss) { + ni = ieee80211_dup_bss(vap, wh->i_addr2); + if (ni == NULL) + return; + } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) + (void) ieee80211_ref_node(ni); + /* + * Mark the node as referenced to reflect that it's + * reference count has been bumped to insure it remains + * after the transaction completes. + */ + ni->ni_flags |= IEEE80211_NODE_AREF; + /* + * Mark the node as requiring a valid association id + * before outbound traffic is permitted. + */ + ni->ni_flags |= IEEE80211_NODE_ASSOCID; + + if (vap->iv_acl != NULL && + vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) { + /* + * When the ACL policy is set to RADIUS we defer the + * authorization to a user agent. Dispatch an event, + * a subsequent MLME call will decide the fate of the + * station. If the user agent is not present then the + * node will be reclaimed due to inactivity. + */ + IEEE80211_NOTE_MAC(vap, + IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, ni->ni_macaddr, + "%s", "station authentication defered (radius acl)"); + ieee80211_notify_node_auth(ni); + } else { + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + IEEE80211_NOTE_MAC(vap, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni->ni_macaddr, + "%s", "station authenticated (open)"); + /* + * 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); + } +} + +static void +hostap_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh, + uint8_t *frm, uint8_t *efrm, int rssi, int nf, + uint16_t seq, uint16_t status) +{ + struct ieee80211vap *vap = ni->ni_vap; + uint8_t *challenge; + int allocbs, estatus; + + KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state)); + + /* + * NB: this can happen as we allow pre-shared key + * authentication to be enabled w/o wep being turned + * on so that configuration of these can be done + * in any order. It may be better to enforce the + * ordering in which case this check would just be + * for sanity/consistency. + */ + if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "%s", " PRIVACY is disabled"); + estatus = IEEE80211_STATUS_ALG; + goto bad; + } + /* + * Pre-shared key authentication is evil; accept + * it only if explicitly configured (it is supported + * mainly for compatibility with clients like Mac OS X). + */ + if (ni->ni_authmode != IEEE80211_AUTH_AUTO && + ni->ni_authmode != IEEE80211_AUTH_SHARED) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad sta auth mode %u", ni->ni_authmode); + vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ + estatus = IEEE80211_STATUS_ALG; + goto bad; + } + + challenge = NULL; + if (frm + 1 < efrm) { + if ((frm[1] + 2) > (efrm - frm)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "ie %d/%d too long", + frm[0], (frm[1] + 2) - (efrm - frm)); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (*frm == IEEE80211_ELEMID_CHALLENGE) + challenge = frm; + frm += frm[1] + 2; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_CHALLENGE: + case IEEE80211_AUTH_SHARED_RESPONSE: + if (challenge == NULL) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "%s", "no challenge"); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (challenge[1] != IEEE80211_CHALLENGE_LEN) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad challenge len %d", challenge[1]); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + default: + break; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_REQUEST: + if (ni == vap->iv_bss) { + ni = ieee80211_dup_bss(vap, wh->i_addr2); + if (ni == NULL) { + /* NB: no way to return an error */ + return; + } + allocbs = 1; + } else { + if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0) + (void) ieee80211_ref_node(ni); + allocbs = 0; + } + /* + * Mark the node as referenced to reflect that it's + * reference count has been bumped to insure it remains + * after the transaction completes. + */ + ni->ni_flags |= IEEE80211_NODE_AREF; + /* + * Mark the node as requiring a valid associatio id + * before outbound traffic is permitted. + */ + ni->ni_flags |= IEEE80211_NODE_ASSOCID; + IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); + ni->ni_noise = nf; + if (!ieee80211_alloc_challenge(ni)) { + /* NB: don't return error so they rexmit */ + return; + } + get_random_bytes(ni->ni_challenge, + IEEE80211_CHALLENGE_LEN); + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ni, "shared key %sauth request", allocbs ? "" : "re"); + /* + * When the ACL policy is set to RADIUS we defer the + * authorization to a user agent. Dispatch an event, + * a subsequent MLME call will decide the fate of the + * station. If the user agent is not present then the + * node will be reclaimed due to inactivity. + */ + if (vap->iv_acl != NULL && + vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) { + IEEE80211_NOTE_MAC(vap, + IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, + ni->ni_macaddr, + "%s", "station authentication defered (radius acl)"); + ieee80211_notify_node_auth(ni); + return; + } + break; + case IEEE80211_AUTH_SHARED_RESPONSE: + if (ni == vap->iv_bss) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key response", + "%s", "unknown station"); + /* NB: don't send a response */ + return; + } + if (ni->ni_challenge == NULL) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key response", + "%s", "no challenge recorded"); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (memcmp(ni->ni_challenge, &challenge[2], + challenge[1]) != 0) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key response", + "%s", "challenge mismatch"); + vap->iv_stats.is_rx_auth_fail++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ni, "%s", "station authenticated (shared key)"); + ieee80211_node_authorize(ni); + break; + default: + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad seq %d", seq); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_SEQUENCE; + goto bad; + } + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + return; +bad: + /* + * Send an error response; but only when operating as an AP. + */ + /* XXX hack to workaround calling convention */ + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq + 1) | (estatus<<16)); +} + +/* + * Convert a WPA cipher selector OUI to an internal + * cipher algorithm. Where appropriate we also + * record any key length. + */ +static int +wpa_cipher(const uint8_t *sel, uint8_t *keylen) +{ +#define WPA_SEL(x) (((x)<<24)|WPA_OUI) + uint32_t w = LE_READ_4(sel); + + switch (w) { + case WPA_SEL(WPA_CSE_NULL): + return IEEE80211_CIPHER_NONE; + case WPA_SEL(WPA_CSE_WEP40): + if (keylen) + *keylen = 40 / NBBY; + return IEEE80211_CIPHER_WEP; + case WPA_SEL(WPA_CSE_WEP104): + if (keylen) + *keylen = 104 / NBBY; + return IEEE80211_CIPHER_WEP; + case WPA_SEL(WPA_CSE_TKIP): + return IEEE80211_CIPHER_TKIP; + case WPA_SEL(WPA_CSE_CCMP): + return IEEE80211_CIPHER_AES_CCM; + } + return 32; /* NB: so 1<< is discarded */ +#undef WPA_SEL +} + +/* + * Convert a WPA key management/authentication algorithm + * to an internal code. + */ +static int +wpa_keymgmt(const uint8_t *sel) +{ +#define WPA_SEL(x) (((x)<<24)|WPA_OUI) + uint32_t w = LE_READ_4(sel); + + switch (w) { + case WPA_SEL(WPA_ASE_8021X_UNSPEC): + return WPA_ASE_8021X_UNSPEC; + case WPA_SEL(WPA_ASE_8021X_PSK): + return WPA_ASE_8021X_PSK; + case WPA_SEL(WPA_ASE_NONE): + return WPA_ASE_NONE; + } + return 0; /* NB: so is discarded */ +#undef WPA_SEL +} + +/* + * Parse a WPA information element to collect parameters. + * Note that we do not validate security parameters; that + * is handled by the authenticator; the parsing done here + * is just for internal use in making operational decisions. + */ +static int +ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm, + struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) +{ + uint8_t len = frm[1]; + uint32_t w; + int n; + + /* + * Check the length once for fixed parts: OUI, type, + * version, mcast cipher, and 2 selector counts. + * Other, variable-length data, must be checked separately. + */ + if ((vap->iv_flags & IEEE80211_F_WPA1) == 0) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "not WPA, flags 0x%x", vap->iv_flags); + return IEEE80211_REASON_IE_INVALID; + } + if (len < 14) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "too short, len %u", len); + return IEEE80211_REASON_IE_INVALID; + } + frm += 6, len -= 4; /* NB: len is payload only */ + /* NB: iswpaoui already validated the OUI and type */ + w = LE_READ_2(frm); + if (w != WPA_VERSION) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "bad version %u", w); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2, len -= 2; + + memset(rsn, 0, sizeof(*rsn)); + + /* multicast/group cipher */ + rsn->rsn_mcastcipher = wpa_cipher(frm, &rsn->rsn_mcastkeylen); + frm += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4+2) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "ucast cipher data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen); + frm += 4, len -= 4; + } + if (w & (1<<IEEE80211_CIPHER_TKIP)) + rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; + else + rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; + + /* key management algorithms */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "key mgmt alg data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= wpa_keymgmt(frm); + frm += 4, len -= 4; + } + if (w & WPA_ASE_8021X_UNSPEC) + rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC; + else + rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; + + if (len > 2) /* optional capabilities */ + rsn->rsn_caps = LE_READ_2(frm); + + return 0; +} + +/* + * Convert an RSN cipher selector OUI to an internal + * cipher algorithm. Where appropriate we also + * record any key length. + */ +static int +rsn_cipher(const uint8_t *sel, uint8_t *keylen) +{ +#define RSN_SEL(x) (((x)<<24)|RSN_OUI) + uint32_t w = LE_READ_4(sel); + + switch (w) { + case RSN_SEL(RSN_CSE_NULL): + return IEEE80211_CIPHER_NONE; + case RSN_SEL(RSN_CSE_WEP40): + if (keylen) + *keylen = 40 / NBBY; + return IEEE80211_CIPHER_WEP; + case RSN_SEL(RSN_CSE_WEP104): + if (keylen) + *keylen = 104 / NBBY; + return IEEE80211_CIPHER_WEP; + case RSN_SEL(RSN_CSE_TKIP): + return IEEE80211_CIPHER_TKIP; + case RSN_SEL(RSN_CSE_CCMP): + return IEEE80211_CIPHER_AES_CCM; + case RSN_SEL(RSN_CSE_WRAP): + return IEEE80211_CIPHER_AES_OCB; + } + return 32; /* NB: so 1<< is discarded */ +#undef WPA_SEL +} + +/* + * Convert an RSN key management/authentication algorithm + * to an internal code. + */ +static int +rsn_keymgmt(const uint8_t *sel) +{ +#define RSN_SEL(x) (((x)<<24)|RSN_OUI) + uint32_t w = LE_READ_4(sel); + + switch (w) { + case RSN_SEL(RSN_ASE_8021X_UNSPEC): + return RSN_ASE_8021X_UNSPEC; + case RSN_SEL(RSN_ASE_8021X_PSK): + return RSN_ASE_8021X_PSK; + case RSN_SEL(RSN_ASE_NONE): + return RSN_ASE_NONE; + } + return 0; /* NB: so is discarded */ +#undef RSN_SEL +} + +/* + * Parse a WPA/RSN information element to collect parameters + * and validate the parameters against what has been + * configured for the system. + */ +static int +ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm, + struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) +{ + uint8_t len = frm[1]; + uint32_t w; + int n; + + /* + * Check the length once for fixed parts: + * version, mcast cipher, and 2 selector counts. + * Other, variable-length data, must be checked separately. + */ + if ((vap->iv_flags & IEEE80211_F_WPA2) == 0) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "WPA", "not RSN, flags 0x%x", vap->iv_flags); + return IEEE80211_REASON_IE_INVALID; + } + if (len < 10) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "too short, len %u", len); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2; + w = LE_READ_2(frm); + if (w != RSN_VERSION) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "bad version %u", w); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2, len -= 2; + + memset(rsn, 0, sizeof(*rsn)); + + /* multicast/group cipher */ + rsn->rsn_mcastcipher = rsn_cipher(frm, &rsn->rsn_mcastkeylen); + frm += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4+2) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "ucast cipher data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen); + frm += 4, len -= 4; + } + if (w & (1<<IEEE80211_CIPHER_TKIP)) + rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP; + else + rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; + + /* key management algorithms */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + wh, "RSN", "key mgmt alg data too short; len %u, n %u", + len, n); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= rsn_keymgmt(frm); + frm += 4, len -= 4; + } + if (w & RSN_ASE_8021X_UNSPEC) + rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC; + else + rsn->rsn_keymgmt = RSN_ASE_8021X_PSK; + + /* optional RSN capabilities */ + if (len > 2) + rsn->rsn_caps = LE_READ_2(frm); + /* XXXPMKID */ + + return 0; +} + +/* + * WPA/802.11i assocation request processing. + */ +static int +wpa_assocreq(struct ieee80211_node *ni, struct ieee80211_rsnparms *rsnparms, + const struct ieee80211_frame *wh, const uint8_t *wpa, + const uint8_t *rsn, uint16_t capinfo) +{ + struct ieee80211vap *vap = ni->ni_vap; + uint8_t reason; + int badwparsn; + + ni->ni_flags &= ~(IEEE80211_NODE_WPS|IEEE80211_NODE_TSN); + if (wpa == NULL && rsn == NULL) { + if (vap->iv_flags_ext & IEEE80211_FEXT_WPS) { + /* + * W-Fi Protected Setup (WPS) permits + * clients to associate and pass EAPOL frames + * to establish initial credentials. + */ + ni->ni_flags |= IEEE80211_NODE_WPS; + return 1; + } + if ((vap->iv_flags_ext & IEEE80211_FEXT_TSN) && + (capinfo & IEEE80211_CAPINFO_PRIVACY)) { + /* + * Transitional Security Network. Permits clients + * to associate and use WEP while WPA is configured. + */ + ni->ni_flags |= IEEE80211_NODE_TSN; + return 1; + } + IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, + wh, NULL, "%s", "no WPA/RSN IE in association request"); + vap->iv_stats.is_rx_assoc_badwpaie++; + reason = IEEE80211_REASON_IE_INVALID; + goto bad; + } + /* assert right association security credentials */ + badwparsn = 0; /* NB: to silence compiler */ + switch (vap->iv_flags & IEEE80211_F_WPA) { + case IEEE80211_F_WPA1: + badwparsn = (wpa == NULL); + break; + case IEEE80211_F_WPA2: + badwparsn = (rsn == NULL); + break; + case IEEE80211_F_WPA1|IEEE80211_F_WPA2: + badwparsn = (wpa == NULL && rsn == NULL); + break; + } + if (badwparsn) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, + wh, NULL, + "%s", "missing WPA/RSN IE in association request"); + vap->iv_stats.is_rx_assoc_badwpaie++; + reason = IEEE80211_REASON_IE_INVALID; + goto bad; + } + /* + * Parse WPA/RSN information element. + */ + if (wpa != NULL) + reason = ieee80211_parse_wpa(vap, wpa, rsnparms, wh); + else + reason = ieee80211_parse_rsn(vap, rsn, rsnparms, wh); + if (reason != 0) { + /* XXX distinguish WPA/RSN? */ + vap->iv_stats.is_rx_assoc_badwpaie++; + goto bad; + } + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, ni, + "%s ie: mc %u/%u uc %u/%u key %u caps 0x%x", + wpa != NULL ? "WPA" : "RSN", + rsnparms->rsn_mcastcipher, rsnparms->rsn_mcastkeylen, + rsnparms->rsn_ucastcipher, rsnparms->rsn_ucastkeylen, + rsnparms->rsn_keymgmt, rsnparms->rsn_caps); + + return 1; +bad: + ieee80211_node_deauth(ni, reason); + return 0; +} + +/* XXX find a better place for definition */ +struct l2_update_frame { + struct ether_header eh; + uint8_t dsap; + uint8_t ssap; + uint8_t control; + uint8_t xid[3]; +} __packed; + +/* + * Deliver a TGf L2UF frame on behalf of a station. + * This primes any bridge when the station is roaming + * between ap's on the same wired network. + */ +static void +ieee80211_deliver_l2uf(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ifnet *ifp = vap->iv_ifp; + struct mbuf *m; + struct l2_update_frame *l2uf; + struct ether_header *eh; + + m = m_gethdr(M_NOWAIT, MT_DATA); + if (m == NULL) { + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, + "%s", "no mbuf for l2uf frame"); + vap->iv_stats.is_rx_nobuf++; /* XXX not right */ + return; + } + l2uf = mtod(m, struct l2_update_frame *); + eh = &l2uf->eh; + /* dst: Broadcast address */ + IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr); + /* src: associated STA */ + IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr); + eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh)); + + l2uf->dsap = 0; + l2uf->ssap = 0; + l2uf->control = 0xf5; + l2uf->xid[0] = 0x81; + l2uf->xid[1] = 0x80; + l2uf->xid[2] = 0x00; + + m->m_pkthdr.len = m->m_len = sizeof(*l2uf); + hostap_deliver_data(vap, ni, m); +} + +static void +ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, + int reassoc, int resp, const char *tag, int rate) +{ + IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2, + "deny %s request, %s rate set mismatch, rate/MCS %d", + reassoc ? "reassoc" : "assoc", tag, rate & IEEE80211_RATE_VAL); + IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_BASIC_RATE); + ieee80211_node_leave(ni); +} + +static void +capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, + int reassoc, int resp, const char *tag, int capinfo) +{ + struct ieee80211vap *vap = ni->ni_vap; + + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2, + "deny %s request, %s mismatch 0x%x", + reassoc ? "reassoc" : "assoc", tag, capinfo); + IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_CAPINFO); + ieee80211_node_leave(ni); + vap->iv_stats.is_rx_assoc_capmismatch++; +} + +static void +htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh, + int reassoc, int resp) +{ + IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2, + "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc"); + /* XXX no better code */ + IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_MISSING_HT_CAPS); + ieee80211_node_leave(ni); +} + +static void +authalgreject(struct ieee80211_node *ni, const struct ieee80211_frame *wh, + int algo, int seq, int status) +{ + struct ieee80211vap *vap = ni->ni_vap; + + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, NULL, "unsupported alg %d", algo); + vap->iv_stats.is_rx_auth_unsupported++; + ieee80211_send_error(ni, wh->i_addr2, IEEE80211_FC0_SUBTYPE_AUTH, + seq | (status << 16)); +} + +static __inline int +ishtmixed(const uint8_t *ie) +{ + const struct ieee80211_ie_htinfo *ht = + (const struct ieee80211_ie_htinfo *) ie; + return (ht->hi_byte2 & IEEE80211_HTINFO_OPMODE) == + IEEE80211_HTINFO_OPMODE_MIXED; +} + +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 +hostap_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, *wpa, *rsn, *wme, *ath, *htcap; + int reassoc, resp; + uint8_t rate; + + 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 when scanning; + * otherwise we check beacon frames for overlapping non-ERP + * BSS in 11g and/or overlapping legacy BSS when in HT. + */ + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && + subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + /* NB: accept off-channel frames */ + if (ieee80211_parse_beacon(ni, m0, &scan) &~ IEEE80211_BPARSE_OFFCHAN) + 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 (scan.status == 0 && /* NB: on channel */ + (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; + } + /* + * Check beacon for overlapping bss w/ non ERP stations. + * If we detect one and protection is configured but not + * enabled, enable it and start a timer that'll bring us + * out if we stop seeing the bss. + */ + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && + scan.status == 0 && /* NB: on-channel */ + ((scan.erp & 0x100) == 0 || /* NB: no ERP, 11b sta*/ + (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) { + ic->ic_lastnonerp = ticks; + ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR; + if (ic->ic_protmode != IEEE80211_PROT_NONE && + (ic->ic_flags & IEEE80211_F_USEPROT) == 0) { + IEEE80211_NOTE_FRAME(vap, + IEEE80211_MSG_ASSOC, wh, + "non-ERP present on channel %d " + "(saw erp 0x%x from channel %d), " + "enable use of protection", + ic->ic_curchan->ic_ieee, + scan.erp, scan.chan); + ic->ic_flags |= IEEE80211_F_USEPROT; + ieee80211_notify_erp(ic); + } + } + /* + * Check beacon for non-HT station on HT channel + * and update HT BSS occupancy as appropriate. + */ + if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) { + if (scan.status & IEEE80211_BPARSE_OFFCHAN) { + /* + * Off control channel; only check frames + * that come in the extension channel when + * operating w/ HT40. + */ + if (!IEEE80211_IS_CHAN_HT40(ic->ic_curchan)) + break; + if (scan.chan != ic->ic_curchan->ic_extieee) + break; + } + if (scan.htinfo == NULL) { + ieee80211_htprot_update(ic, + IEEE80211_HTINFO_OPMODE_PROTOPT | + IEEE80211_HTINFO_NONHT_PRESENT); + } else if (ishtmixed(scan.htinfo)) { + /* XXX? take NONHT_PRESENT from beacon? */ + ieee80211_htprot_update(ic, + IEEE80211_HTINFO_OPMODE_MIXED | + IEEE80211_HTINFO_NONHT_PRESENT); + } + } + break; + } + + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + if (vap->iv_state != IEEE80211_S_RUN) { + vap->iv_stats.is_rx_mgtdiscard++; + 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_AUTH: { + uint16_t algo, seq, status; + + if (vap->iv_state != IEEE80211_S_RUN) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, NULL, "%s", "wrong bssid"); + vap->iv_stats.is_rx_wrongbss++; /*XXX unique stat?*/ + return; + } + /* + * auth frame format + * [2] algorithm + * [2] sequence + * [2] status + * [tlv*] challenge + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); + algo = le16toh(*(uint16_t *)frm); + seq = le16toh(*(uint16_t *)(frm + 2)); + status = le16toh(*(uint16_t *)(frm + 4)); + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2, + "recv auth frame with algorithm %d seq %d", algo, seq); + /* + * Consult the ACL policy module if setup. + */ + if (vap->iv_acl != NULL && + !vap->iv_acl->iac_check(vap, wh->i_addr2)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL, + wh, NULL, "%s", "disallowed by ACL"); + vap->iv_stats.is_rx_acl++; + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16)); + return; + } + if (vap->iv_flags & IEEE80211_F_COUNTERM) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, + wh, NULL, "%s", "TKIP countermeasures enabled"); + vap->iv_stats.is_rx_auth_countermeasures++; + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + IEEE80211_REASON_MIC_FAILURE); + return; + } + if (algo == IEEE80211_AUTH_ALG_SHARED) + hostap_auth_shared(ni, wh, frm + 6, efrm, rssi, nf, + seq, status); + else if (algo == IEEE80211_AUTH_ALG_OPEN) + hostap_auth_open(ni, wh, rssi, nf, seq, status); + else if (algo == IEEE80211_AUTH_ALG_LEAP) { + authalgreject(ni, wh, algo, + seq+1, IEEE80211_STATUS_ALG); + return; + } else { + /* + * We assume that an unknown algorithm is the result + * of a decryption failure on a shared key auth frame; + * return a status code appropriate for that instead + * of IEEE80211_STATUS_ALG. + * + * NB: a seq# of 4 is intentional; the decrypted + * frame likely has a bogus seq value. + */ + authalgreject(ni, wh, algo, + 4, IEEE80211_STATUS_CHALLENGE); + return; + } + break; + } + + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { + uint16_t capinfo, lintval; + struct ieee80211_rsnparms rsnparms; + + if (vap->iv_state != IEEE80211_S_RUN) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, NULL, "%s", "wrong bssid"); + vap->iv_stats.is_rx_assoc_bss++; + return; + } + if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { + reassoc = 1; + resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; + } else { + reassoc = 0; + resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; + } + if (ni == vap->iv_bss) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2, + "deny %s request, sta not authenticated", + reassoc ? "reassoc" : "assoc"); + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_ASSOC_NOT_AUTHED); + vap->iv_stats.is_rx_assoc_notauth++; + return; + } + + /* + * asreq frame format + * [2] capability information + * [2] listen interval + * [6*] current AP address (reassoc only) + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] WPA or RSN + * [tlv] HT capabilities + * [tlv] Atheros capabilities + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return); + capinfo = le16toh(*(uint16_t *)frm); frm += 2; + lintval = le16toh(*(uint16_t *)frm); frm += 2; + if (reassoc) + frm += 6; /* ignore current AP info */ + ssid = rates = xrates = wpa = rsn = wme = ath = htcap = 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; + case IEEE80211_ELEMID_RSN: + rsn = frm; + break; + case IEEE80211_ELEMID_HTCAP: + htcap = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(frm)) + wpa = frm; + else if (iswmeinfo(frm)) + wme = frm; +#ifdef IEEE80211_SUPPORT_SUPERG + else if (isatherosoui(frm)) + ath = frm; +#endif + else if (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) { + if (ishtcapoui(frm) && htcap == NULL) + htcap = 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 (htcap != NULL) { + IEEE80211_VERIFY_LENGTH(htcap[1], + htcap[0] == IEEE80211_ELEMID_VENDOR ? + 4 + sizeof(struct ieee80211_ie_htcap)-2 : + sizeof(struct ieee80211_ie_htcap)-2, + return); /* XXX just NULL out? */ + } + + if ((vap->iv_flags & IEEE80211_F_WPA) && + !wpa_assocreq(ni, &rsnparms, wh, wpa, rsn, capinfo)) + return; + /* discard challenge after association */ + if (ni->ni_challenge != NULL) { + free(ni->ni_challenge, M_80211_NODE); + ni->ni_challenge = NULL; + } + /* NB: 802.11 spec says to ignore station's privacy bit */ + if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) { + capinfomismatch(ni, wh, reassoc, resp, + "capability", capinfo); + return; + } + /* + * Disallow re-associate w/ invalid slot time setting. + */ + if (ni->ni_associd != 0 && + IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && + ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) { + capinfomismatch(ni, wh, reassoc, resp, + "slot time", capinfo); + return; + } + rate = ieee80211_setup_rates(ni, rates, xrates, + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (rate & IEEE80211_RATE_BASIC) { + ratesetmismatch(ni, wh, reassoc, resp, "legacy", rate); + vap->iv_stats.is_rx_assoc_norate++; + return; + } + /* + * If constrained to 11g-only stations reject an + * 11b-only station. We cheat a bit here by looking + * at the max negotiated xmit rate and assuming anyone + * with a best rate <24Mb/s is an 11b station. + */ + if ((vap->iv_flags & IEEE80211_F_PUREG) && rate < 48) { + ratesetmismatch(ni, wh, reassoc, resp, "11g", rate); + vap->iv_stats.is_rx_assoc_norate++; + return; + } + /* + * Do HT rate set handling and setup HT node state. + */ + ni->ni_chan = vap->iv_bss->ni_chan; + if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) { + rate = ieee80211_setup_htrates(ni, htcap, + IEEE80211_F_DOFMCS | IEEE80211_F_DONEGO | + IEEE80211_F_DOBRS); + if (rate & IEEE80211_RATE_BASIC) { + ratesetmismatch(ni, wh, reassoc, resp, + "HT", rate); + vap->iv_stats.is_ht_assoc_norate++; + return; + } + ieee80211_ht_node_init(ni); + ieee80211_ht_updatehtcap(ni, htcap); + } else if (ni->ni_flags & IEEE80211_NODE_HT) + ieee80211_ht_node_cleanup(ni); +#ifdef IEEE80211_SUPPORT_SUPERG + else if (ni->ni_ath_flags & IEEE80211_NODE_ATH) + ieee80211_ff_node_cleanup(ni); +#endif + /* + * Allow AMPDU operation only with unencrypted traffic + * or AES-CCM; the 11n spec only specifies these ciphers + * so permitting any others is undefined and can lead + * to interoperability problems. + */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + (((vap->iv_flags & IEEE80211_F_WPA) && + rsnparms.rsn_ucastcipher != IEEE80211_CIPHER_AES_CCM) || + (vap->iv_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) { + IEEE80211_NOTE(vap, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, + "disallow HT use because WEP or TKIP requested, " + "capinfo 0x%x ucastcipher %d", capinfo, + rsnparms.rsn_ucastcipher); + ieee80211_ht_node_cleanup(ni); + vap->iv_stats.is_ht_assoc_downgrade++; + } + /* + * If constrained to 11n-only stations reject legacy stations. + */ + if ((vap->iv_flags_ht & IEEE80211_FHT_PUREN) && + (ni->ni_flags & IEEE80211_NODE_HT) == 0) { + htcapmismatch(ni, wh, reassoc, resp); + vap->iv_stats.is_ht_assoc_nohtcap++; + return; + } + IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); + ni->ni_noise = nf; + ni->ni_intval = lintval; + ni->ni_capinfo = capinfo; + ni->ni_fhdwell = vap->iv_bss->ni_fhdwell; + ni->ni_fhindex = vap->iv_bss->ni_fhindex; + /* + * Store the IEs. + * XXX maybe better to just expand + */ + if (ieee80211_ies_init(&ni->ni_ies, sfrm, efrm - sfrm)) { +#define setie(_ie, _off) ieee80211_ies_setie(ni->ni_ies, _ie, _off) + if (wpa != NULL) + setie(wpa_ie, wpa - sfrm); + if (rsn != NULL) + setie(rsn_ie, rsn - sfrm); + if (htcap != NULL) + setie(htcap_ie, htcap - sfrm); + if (wme != NULL) { + setie(wme_ie, wme - sfrm); + /* + * Mark node as capable of QoS. + */ + ni->ni_flags |= IEEE80211_NODE_QOS; + } else + ni->ni_flags &= ~IEEE80211_NODE_QOS; +#ifdef IEEE80211_SUPPORT_SUPERG + if (ath != NULL) { + setie(ath_ie, ath - sfrm); + /* + * Parse ATH station parameters. + */ + ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); + } else +#endif + ni->ni_ath_flags = 0; +#undef setie + } else { + ni->ni_flags &= ~IEEE80211_NODE_QOS; + ni->ni_ath_flags = 0; + } + ieee80211_node_join(ni, resp); + ieee80211_deliver_l2uf(ni); + break; + } + + case IEEE80211_FC0_SUBTYPE_DEAUTH: + case IEEE80211_FC0_SUBTYPE_DISASSOC: { + uint16_t reason; + + if (vap->iv_state != IEEE80211_S_RUN || + /* NB: can happen when in promiscuous mode */ + !IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + /* + * deauth/disassoc frame format + * [2] reason + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); + reason = le16toh(*(uint16_t *)frm); + if (subtype == IEEE80211_FC0_SUBTYPE_DEAUTH) { + vap->iv_stats.is_rx_deauth++; + IEEE80211_NODE_STAT(ni, rx_deauth); + } else { + vap->iv_stats.is_rx_disassoc++; + IEEE80211_NODE_STAT(ni, rx_disassoc); + } + IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, + "recv %s (reason %d)", ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], reason); + if (ni != vap->iv_bss) + ieee80211_node_leave(ni); + break; + } + + case IEEE80211_FC0_SUBTYPE_ACTION: + if (vap->iv_state == IEEE80211_S_RUN) { + if (ieee80211_parse_action(ni, m0) == 0) + ic->ic_recv_action(ni, wh, frm, efrm); + } else + vap->iv_stats.is_rx_mgtdiscard++; + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "mgt", "subtype 0x%x not handled", subtype); + vap->iv_stats.is_rx_badsubtype++; + break; + } +} + +static void +hostap_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) +{ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PS_POLL: + hostap_recv_pspoll(ni, m); + break; + case IEEE80211_FC0_SUBTYPE_BAR: + ieee80211_recv_bar(ni, m); + break; + } +} + +/* + * Process a received ps-poll frame. + */ +static void +hostap_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_frame_min *wh; + struct ifnet *ifp; + struct mbuf *m; + uint16_t aid; + int qlen; + + wh = mtod(m0, struct ieee80211_frame_min *); + if (ni->ni_associd == 0) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, + (struct ieee80211_frame *) wh, NULL, + "%s", "unassociated station"); + vap->iv_stats.is_ps_unassoc++; + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_NOT_ASSOCED); + return; + } + + aid = le16toh(*(uint16_t *)wh->i_dur); + if (aid != ni->ni_associd) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, + (struct ieee80211_frame *) wh, NULL, + "aid mismatch: sta aid 0x%x poll aid 0x%x", + ni->ni_associd, aid); + vap->iv_stats.is_ps_badaid++; + /* + * NB: We used to deauth the station but it turns out + * the Blackberry Curve 8230 (and perhaps other devices) + * sometimes send the wrong AID when WME is negotiated. + * Being more lenient here seems ok as we already check + * the station is associated and we only return frames + * queued for the station (i.e. we don't use the AID). + */ + return; + } + + /* Okay, take the first queued packet and put it out... */ + m = ieee80211_node_psq_dequeue(ni, &qlen); + if (m == NULL) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_POWER, wh->i_addr2, + "%s", "recv ps-poll, but queue empty"); + ieee80211_send_nulldata(ieee80211_ref_node(ni)); + vap->iv_stats.is_ps_qempty++; /* XXX node stat */ + if (vap->iv_set_tim != NULL) + vap->iv_set_tim(ni, 0); /* just in case */ + return; + } + /* + * If there are more packets, set the more packets bit + * in the packet dispatched to the station; otherwise + * turn off the TIM bit. + */ + if (qlen != 0) { + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, + "recv ps-poll, send packet, %u still queued", qlen); + m->m_flags |= M_MORE_DATA; + } else { + IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, + "%s", "recv ps-poll, send packet, queue empty"); + if (vap->iv_set_tim != NULL) + vap->iv_set_tim(ni, 0); + } + m->m_flags |= M_PWR_SAV; /* bypass PS handling */ + + if (m->m_flags & M_ENCAP) + ifp = vap->iv_ic->ic_ifp; + else + ifp = vap->iv_ifp; + IF_ENQUEUE(&ifp->if_snd, m); + if_start(ifp); +} diff --git a/freebsd/sys/net80211/ieee80211_hostap.h b/freebsd/sys/net80211/ieee80211_hostap.h new file mode 100644 index 00000000..fa35e220 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_hostap.h @@ -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 _NET80211_IEEE80211_HOSTAP_HH_ +#define _NET80211_IEEE80211_HOSTAP_HH_ + +/* + * Hostap implementation definitions. + */ +void ieee80211_hostap_attach(struct ieee80211com *); +void ieee80211_hostap_detach(struct ieee80211com *); +#endif /* !_NET80211_IEEE80211_HOSTAP_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_ht.c b/freebsd/sys/net80211/ieee80211_ht.c new file mode 100644 index 00000000..2b55c067 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_ht.c @@ -0,0 +1,2523 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11n protocol support. + */ + +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/endian.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_action.h> +#include <freebsd/net80211/ieee80211_input.h> + +/* define here, used throughout file */ +#define MS(_v, _f) (((_v) & _f) >> _f##_S) +#define SM(_v, _f) (((_v) << _f##_S) & _f) + +const struct ieee80211_mcs_rates ieee80211_htrates[16] = { + { 13, 14, 27, 30 }, /* MCS 0 */ + { 26, 29, 54, 60 }, /* MCS 1 */ + { 39, 43, 81, 90 }, /* MCS 2 */ + { 52, 58, 108, 120 }, /* MCS 3 */ + { 78, 87, 162, 180 }, /* MCS 4 */ + { 104, 116, 216, 240 }, /* MCS 5 */ + { 117, 130, 243, 270 }, /* MCS 6 */ + { 130, 144, 270, 300 }, /* MCS 7 */ + { 26, 29, 54, 60 }, /* MCS 8 */ + { 52, 58, 108, 120 }, /* MCS 9 */ + { 78, 87, 162, 180 }, /* MCS 10 */ + { 104, 116, 216, 240 }, /* MCS 11 */ + { 156, 173, 324, 360 }, /* MCS 12 */ + { 208, 231, 432, 480 }, /* MCS 13 */ + { 234, 260, 486, 540 }, /* MCS 14 */ + { 260, 289, 540, 600 } /* MCS 15 */ +}; + +static const struct ieee80211_htrateset ieee80211_rateset_11n = + { 16, { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15 } + }; + +#ifdef IEEE80211_AMPDU_AGE +static int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */ +SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I", + "AMPDU max reorder age (ms)"); +#endif + +static int ieee80211_recv_bar_ena = 1; +SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena, + 0, "BAR frame processing (ena/dis)"); + +static int ieee80211_addba_timeout = -1;/* timeout for ADDBA response */ +SYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I", + "ADDBA request timeout (ms)"); +static int ieee80211_addba_backoff = -1;/* backoff after max ADDBA requests */ +SYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I", + "ADDBA request backoff (ms)"); +static int ieee80211_addba_maxtries = 3;/* max ADDBA requests before backoff */ +SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff"); + +static int ieee80211_bar_timeout = -1; /* timeout waiting for BAR response */ +static int ieee80211_bar_maxtries = 50;/* max BAR requests before DELBA */ + +static ieee80211_recv_action_func ht_recv_action_ba_addba_request; +static ieee80211_recv_action_func ht_recv_action_ba_addba_response; +static ieee80211_recv_action_func ht_recv_action_ba_delba; +static ieee80211_recv_action_func ht_recv_action_ht_mimopwrsave; +static ieee80211_recv_action_func ht_recv_action_ht_txchwidth; + +static ieee80211_send_action_func ht_send_action_ba_addba; +static ieee80211_send_action_func ht_send_action_ba_delba; +static ieee80211_send_action_func ht_send_action_ht_txchwidth; + +static void +ieee80211_ht_init(void) +{ + /* + * Setup HT parameters that depends on the clock frequency. + */ +#ifdef IEEE80211_AMPDU_AGE + ieee80211_ampdu_age = msecs_to_ticks(500); +#endif + ieee80211_addba_timeout = msecs_to_ticks(250); + ieee80211_addba_backoff = msecs_to_ticks(10*1000); + ieee80211_bar_timeout = msecs_to_ticks(250); + /* + * Register action frame handlers. + */ + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_recv_action_ba_addba_request); + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_recv_action_ba_addba_response); + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_DELBA, ht_recv_action_ba_delba); + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT, + IEEE80211_ACTION_HT_MIMOPWRSAVE, ht_recv_action_ht_mimopwrsave); + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT, + IEEE80211_ACTION_HT_TXCHWIDTH, ht_recv_action_ht_txchwidth); + + ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_send_action_ba_addba); + ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_send_action_ba_addba); + ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_DELBA, ht_send_action_ba_delba); + ieee80211_send_action_register(IEEE80211_ACTION_CAT_HT, + IEEE80211_ACTION_HT_TXCHWIDTH, ht_send_action_ht_txchwidth); +} +SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_init, NULL); + +static int ieee80211_ampdu_enable(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); +static int ieee80211_addba_request(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int dialogtoken, int baparamset, int batimeout); +static int ieee80211_addba_response(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int code, int baparamset, int batimeout); +static void ieee80211_addba_stop(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); +static void ieee80211_bar_response(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, int status); +static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap); +static void bar_stop_timer(struct ieee80211_tx_ampdu *tap); +static int ampdu_rx_start(struct ieee80211_node *, struct ieee80211_rx_ampdu *, + int baparamset, int batimeout, int baseqctl); +static void ampdu_rx_stop(struct ieee80211_node *, struct ieee80211_rx_ampdu *); + +void +ieee80211_ht_attach(struct ieee80211com *ic) +{ + /* setup default aggregation policy */ + ic->ic_recv_action = ieee80211_recv_action; + ic->ic_send_action = ieee80211_send_action; + ic->ic_ampdu_enable = ieee80211_ampdu_enable; + ic->ic_addba_request = ieee80211_addba_request; + ic->ic_addba_response = ieee80211_addba_response; + ic->ic_addba_stop = ieee80211_addba_stop; + ic->ic_bar_response = ieee80211_bar_response; + ic->ic_ampdu_rx_start = ampdu_rx_start; + ic->ic_ampdu_rx_stop = ampdu_rx_stop; + + ic->ic_htprotmode = IEEE80211_PROT_RTSCTS; + ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE; +} + +void +ieee80211_ht_detach(struct ieee80211com *ic) +{ +} + +void +ieee80211_ht_vattach(struct ieee80211vap *vap) +{ + + /* driver can override defaults */ + vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; + vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; + vap->iv_ampdu_limit = vap->iv_ampdu_rxmax; + vap->iv_amsdu_limit = vap->iv_htcaps & IEEE80211_HTCAP_MAXAMSDU; + /* tx aggregation traffic thresholds */ + vap->iv_ampdu_mintraffic[WME_AC_BK] = 128; + vap->iv_ampdu_mintraffic[WME_AC_BE] = 64; + vap->iv_ampdu_mintraffic[WME_AC_VO] = 32; + vap->iv_ampdu_mintraffic[WME_AC_VI] = 32; + + if (vap->iv_htcaps & IEEE80211_HTC_HT) { + /* + * Device is HT capable; enable all HT-related + * facilities by default. + * XXX these choices may be too aggressive. + */ + vap->iv_flags_ht |= IEEE80211_FHT_HT + | IEEE80211_FHT_HTCOMPAT + ; + if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20) + vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI20; + /* XXX infer from channel list? */ + if (vap->iv_htcaps & IEEE80211_HTCAP_CHWIDTH40) { + vap->iv_flags_ht |= IEEE80211_FHT_USEHT40; + if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40) + vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI40; + } + /* enable RIFS if capable */ + if (vap->iv_htcaps & IEEE80211_HTC_RIFS) + vap->iv_flags_ht |= IEEE80211_FHT_RIFS; + + /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */ + vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_RX; + if (vap->iv_htcaps & IEEE80211_HTC_AMPDU) + vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_TX; + vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_RX; + if (vap->iv_htcaps & IEEE80211_HTC_AMSDU) + vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_TX; + } + /* NB: disable default legacy WDS, too many issues right now */ + if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) + vap->iv_flags_ht &= ~IEEE80211_FHT_HT; +} + +void +ieee80211_ht_vdetach(struct ieee80211vap *vap) +{ +} + +static void +ht_announce(struct ieee80211com *ic, int mode, + const struct ieee80211_htrateset *rs) +{ + struct ifnet *ifp = ic->ic_ifp; + int i, rate, mword; + + if_printf(ifp, "%s MCS: ", ieee80211_phymode_name[mode]); + for (i = 0; i < rs->rs_nrates; i++) { + mword = ieee80211_rate2media(ic, + rs->rs_rates[i] | IEEE80211_RATE_MCS, mode); + if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS) + continue; + rate = ieee80211_htrates[rs->rs_rates[i]].ht40_rate_400ns; + printf("%s%d%sMbps", (i != 0 ? " " : ""), + rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); + } + printf("\n"); +} + +void +ieee80211_ht_announce(struct ieee80211com *ic) +{ + if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA)) + ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n); + if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) + ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n); +} + +const struct ieee80211_htrateset * +ieee80211_get_suphtrates(struct ieee80211com *ic, + const struct ieee80211_channel *c) +{ + return &ieee80211_rateset_11n; +} + +/* + * Receive processing. + */ + +/* + * Decap the encapsulated A-MSDU frames and dispatch all but + * the last for delivery. The last frame is returned for + * delivery via the normal path. + */ +struct mbuf * +ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211vap *vap = ni->ni_vap; + int framelen; + struct mbuf *n; + + /* discard 802.3 header inserted by ieee80211_decap */ + m_adj(m, sizeof(struct ether_header)); + + vap->iv_stats.is_amsdu_decap++; + + for (;;) { + /* + * Decap the first frame, bust it apart from the + * remainder and deliver. We leave the last frame + * delivery to the caller (for consistency with other + * code paths, could also do it here). + */ + m = ieee80211_decap1(m, &framelen); + if (m == NULL) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, "a-msdu", "%s", "decap failed"); + vap->iv_stats.is_amsdu_tooshort++; + return NULL; + } + if (m->m_pkthdr.len == framelen) + break; + n = m_split(m, framelen, M_NOWAIT); + if (n == NULL) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, + ni->ni_macaddr, "a-msdu", + "%s", "unable to split encapsulated frames"); + vap->iv_stats.is_amsdu_split++; + m_freem(m); /* NB: must reclaim */ + return NULL; + } + vap->iv_deliver_data(vap, ni, m); + + /* + * Remove frame contents; each intermediate frame + * is required to be aligned to a 4-byte boundary. + */ + m = n; + m_adj(m, roundup2(framelen, 4) - framelen); /* padding */ + } + return m; /* last delivered by caller */ +} + +/* + * Purge all frames in the A-MPDU re-order queue. + */ +static void +ampdu_rx_purge(struct ieee80211_rx_ampdu *rap) +{ + struct mbuf *m; + int i; + + for (i = 0; i < rap->rxa_wnd; i++) { + m = rap->rxa_m[i]; + if (m != NULL) { + rap->rxa_m[i] = NULL; + rap->rxa_qbytes -= m->m_pkthdr.len; + m_freem(m); + if (--rap->rxa_qframes == 0) + break; + } + } + KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, + ("lost %u data, %u frames on ampdu rx q", + rap->rxa_qbytes, rap->rxa_qframes)); +} + +/* + * Start A-MPDU rx/re-order processing for the specified TID. + */ +static int +ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap, + int baparamset, int batimeout, int baseqctl) +{ + int bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); + + if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) { + /* + * AMPDU previously setup and not terminated with a DELBA, + * flush the reorder q's in case anything remains. + */ + ampdu_rx_purge(rap); + } + memset(rap, 0, sizeof(*rap)); + rap->rxa_wnd = (bufsiz == 0) ? + IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); + rap->rxa_start = MS(baseqctl, IEEE80211_BASEQ_START); + rap->rxa_flags |= IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND; + + return 0; +} + +/* + * Stop A-MPDU rx processing for the specified TID. + */ +static void +ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) +{ + ampdu_rx_purge(rap); + rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND); +} + +/* + * Dispatch a frame from the A-MPDU reorder queue. The + * frame is fed back into ieee80211_input marked with an + * M_AMPDU_MPDU flag so it doesn't come back to us (it also + * permits ieee80211_input to optimize re-processing). + */ +static __inline void +ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m) +{ + m->m_flags |= M_AMPDU_MPDU; /* bypass normal processing */ + /* NB: rssi and noise are ignored w/ M_AMPDU_MPDU set */ + (void) ieee80211_input(ni, m, 0, 0); +} + +/* + * Dispatch as many frames as possible from the re-order queue. + * Frames will always be "at the front"; we process all frames + * up to the first empty slot in the window. On completion we + * cleanup state if there are still pending frames in the current + * BA window. We assume the frame at slot 0 is already handled + * by the caller; we always start at slot 1. + */ +static void +ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct mbuf *m; + int i; + + /* flush run of frames */ + for (i = 1; i < rap->rxa_wnd; i++) { + m = rap->rxa_m[i]; + if (m == NULL) + break; + rap->rxa_m[i] = NULL; + rap->rxa_qbytes -= m->m_pkthdr.len; + rap->rxa_qframes--; + + ampdu_dispatch(ni, m); + } + /* + * If frames remain, copy the mbuf pointers down so + * they correspond to the offsets in the new window. + */ + if (rap->rxa_qframes != 0) { + int n = rap->rxa_qframes, j; + for (j = i+1; j < rap->rxa_wnd; j++) { + if (rap->rxa_m[j] != NULL) { + rap->rxa_m[j-i] = rap->rxa_m[j]; + rap->rxa_m[j] = NULL; + if (--n == 0) + break; + } + } + KASSERT(n == 0, ("lost %d frames", n)); + vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes; + } + /* + * Adjust the start of the BA window to + * reflect the frames just dispatched. + */ + rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); + vap->iv_stats.is_ampdu_rx_oor += i; +} + +#ifdef IEEE80211_AMPDU_AGE +/* + * Dispatch all frames in the A-MPDU re-order queue. + */ +static void +ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct mbuf *m; + int i; + + for (i = 0; i < rap->rxa_wnd; i++) { + m = rap->rxa_m[i]; + if (m == NULL) + continue; + rap->rxa_m[i] = NULL; + rap->rxa_qbytes -= m->m_pkthdr.len; + rap->rxa_qframes--; + vap->iv_stats.is_ampdu_rx_oor++; + + ampdu_dispatch(ni, m); + if (rap->rxa_qframes == 0) + break; + } +} +#endif /* IEEE80211_AMPDU_AGE */ + +/* + * Dispatch all frames in the A-MPDU re-order queue + * preceding the specified sequence number. This logic + * handles window moves due to a received MSDU or BAR. + */ +static void +ampdu_rx_flush_upto(struct ieee80211_node *ni, + struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct mbuf *m; + ieee80211_seq seqno; + int i; + + /* + * Flush any complete MSDU's with a sequence number lower + * than winstart. Gaps may exist. Note that we may actually + * dispatch frames past winstart if a run continues; this is + * an optimization that avoids having to do a separate pass + * to dispatch frames after moving the BA window start. + */ + seqno = rap->rxa_start; + for (i = 0; i < rap->rxa_wnd; i++) { + m = rap->rxa_m[i]; + if (m != NULL) { + rap->rxa_m[i] = NULL; + rap->rxa_qbytes -= m->m_pkthdr.len; + rap->rxa_qframes--; + vap->iv_stats.is_ampdu_rx_oor++; + + ampdu_dispatch(ni, m); + } else { + if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart)) + break; + } + seqno = IEEE80211_SEQ_INC(seqno); + } + /* + * If frames remain, copy the mbuf pointers down so + * they correspond to the offsets in the new window. + */ + if (rap->rxa_qframes != 0) { + int n = rap->rxa_qframes, j; + + /* NB: this loop assumes i > 0 and/or rxa_m[0] is NULL */ + KASSERT(rap->rxa_m[0] == NULL, + ("%s: BA window slot 0 occupied", __func__)); + for (j = i+1; j < rap->rxa_wnd; j++) { + if (rap->rxa_m[j] != NULL) { + rap->rxa_m[j-i] = rap->rxa_m[j]; + rap->rxa_m[j] = NULL; + if (--n == 0) + break; + } + } + KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d " + "BA win <%d:%d> winstart %d", + __func__, n, rap->rxa_qframes, i, rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), + winstart)); + vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes; + } + /* + * Move the start of the BA window; we use the + * sequence number of the last MSDU that was + * passed up the stack+1 or winstart if stopped on + * a gap in the reorder buffer. + */ + rap->rxa_start = seqno; +} + +/* + * Process a received QoS data frame for an HT station. Handle + * A-MPDU reordering: if this frame is received out of order + * and falls within the BA window hold onto it. Otherwise if + * this frame completes a run, flush any pending frames. We + * return 1 if the frame is consumed. A 0 is returned if + * the frame should be processed normally by the caller. + */ +int +ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m) +{ +#define IEEE80211_FC0_QOSDATA \ + (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0) +#define PROCESS 0 /* caller should process frame */ +#define CONSUMED 1 /* frame consumed, caller does nothing */ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_qosframe *wh; + struct ieee80211_rx_ampdu *rap; + ieee80211_seq rxseq; + uint8_t tid; + int off; + + KASSERT((m->m_flags & (M_AMPDU | M_AMPDU_MPDU)) == M_AMPDU, + ("!a-mpdu or already re-ordered, flags 0x%x", m->m_flags)); + KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); + + /* NB: m_len known to be sufficient */ + wh = mtod(m, struct ieee80211_qosframe *); + if (wh->i_fc[0] != IEEE80211_FC0_QOSDATA) { + /* + * Not QoS data, shouldn't get here but just + * return it to the caller for processing. + */ + return PROCESS; + } + if (IEEE80211_IS_DSTODS(wh)) + tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0]; + else + tid = wh->i_qos[0]; + tid &= IEEE80211_QOS_TID; + rap = &ni->ni_rx_ampdu[tid]; + if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { + /* + * No ADDBA request yet, don't touch. + */ + return PROCESS; + } + rxseq = le16toh(*(uint16_t *)wh->i_seq); + if ((rxseq & IEEE80211_SEQ_FRAG_MASK) != 0) { + /* + * Fragments are not allowed; toss. + */ + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, + "A-MPDU", "fragment, rxseq 0x%x tid %u%s", rxseq, tid, + wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); + vap->iv_stats.is_ampdu_rx_drop++; + IEEE80211_NODE_STAT(ni, rx_drop); + m_freem(m); + return CONSUMED; + } + rxseq >>= IEEE80211_SEQ_SEQ_SHIFT; + rap->rxa_nframes++; +again: + if (rxseq == rap->rxa_start) { + /* + * First frame in window. + */ + if (rap->rxa_qframes != 0) { + /* + * Dispatch as many packets as we can. + */ + KASSERT(rap->rxa_m[0] == NULL, ("unexpected dup")); + ampdu_dispatch(ni, m); + ampdu_rx_dispatch(rap, ni); + return CONSUMED; + } else { + /* + * In order; advance window and notify + * caller to dispatch directly. + */ + rap->rxa_start = IEEE80211_SEQ_INC(rxseq); + return PROCESS; + } + } + /* + * Frame is out of order; store if in the BA window. + */ + /* calculate offset in BA window */ + off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); + if (off < rap->rxa_wnd) { + /* + * Common case (hopefully): in the BA window. + * Sec 9.10.7.6 a) (D2.04 p.118 line 47) + */ +#ifdef IEEE80211_AMPDU_AGE + /* + * Check for frames sitting too long in the reorder queue. + * This should only ever happen if frames are not delivered + * without the sender otherwise notifying us (e.g. with a + * BAR to move the window). Typically this happens because + * of vendor bugs that cause the sequence number to jump. + * When this happens we get a gap in the reorder queue that + * leaves frame sitting on the queue until they get pushed + * out due to window moves. When the vendor does not send + * BAR this move only happens due to explicit packet sends + * + * NB: we only track the time of the oldest frame in the + * reorder q; this means that if we flush we might push + * frames that still "new"; if this happens then subsequent + * frames will result in BA window moves which cost something + * but is still better than a big throughput dip. + */ + if (rap->rxa_qframes != 0) { + /* XXX honor batimeout? */ + if (ticks - rap->rxa_age > ieee80211_ampdu_age) { + /* + * Too long since we received the first + * frame; flush the reorder buffer. + */ + if (rap->rxa_qframes != 0) { + vap->iv_stats.is_ampdu_rx_age += + rap->rxa_qframes; + ampdu_rx_flush(ni, rap); + } + rap->rxa_start = IEEE80211_SEQ_INC(rxseq); + return PROCESS; + } + } else { + /* + * First frame, start aging timer. + */ + rap->rxa_age = ticks; + } +#endif /* IEEE80211_AMPDU_AGE */ + /* save packet */ + if (rap->rxa_m[off] == NULL) { + rap->rxa_m[off] = m; + rap->rxa_qframes++; + rap->rxa_qbytes += m->m_pkthdr.len; + vap->iv_stats.is_ampdu_rx_reorder++; + } else { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, + ni->ni_macaddr, "a-mpdu duplicate", + "seqno %u tid %u BA win <%u:%u>", + rxseq, tid, rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1)); + vap->iv_stats.is_rx_dup++; + IEEE80211_NODE_STAT(ni, rx_dup); + m_freem(m); + } + return CONSUMED; + } + if (off < IEEE80211_SEQ_BA_RANGE) { + /* + * Outside the BA window, but within range; + * flush the reorder q and move the window. + * Sec 9.10.7.6 b) (D2.04 p.118 line 60) + */ + IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, + "move BA win <%u:%u> (%u frames) rxseq %u tid %u", + rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), + rap->rxa_qframes, rxseq, tid); + vap->iv_stats.is_ampdu_rx_move++; + + /* + * The spec says to flush frames up to but not including: + * WinStart_B = rxseq - rap->rxa_wnd + 1 + * Then insert the frame or notify the caller to process + * it immediately. We can safely do this by just starting + * over again because we know the frame will now be within + * the BA window. + */ + /* NB: rxa_wnd known to be >0 */ + ampdu_rx_flush_upto(ni, rap, + IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1)); + goto again; + } else { + /* + * Outside the BA window and out of range; toss. + * Sec 9.10.7.6 c) (D2.04 p.119 line 16) + */ + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, + "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", + rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), + rap->rxa_qframes, rxseq, tid, + wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); + vap->iv_stats.is_ampdu_rx_drop++; + IEEE80211_NODE_STAT(ni, rx_drop); + m_freem(m); + return CONSUMED; + } +#undef CONSUMED +#undef PROCESS +#undef IEEE80211_FC0_QOSDATA +} + +/* + * Process a BAR ctl frame. Dispatch all frames up to + * the sequence number of the frame. If this frame is + * out of range it's discarded. + */ +void +ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_frame_bar *wh; + struct ieee80211_rx_ampdu *rap; + ieee80211_seq rxseq; + int tid, off; + + if (!ieee80211_recv_bar_ena) { +#if 0 + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_11N, + ni->ni_macaddr, "BAR", "%s", "processing disabled"); +#endif + vap->iv_stats.is_ampdu_bar_bad++; + return; + } + wh = mtod(m0, struct ieee80211_frame_bar *); + /* XXX check basic BAR */ + tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID); + rap = &ni->ni_rx_ampdu[tid]; + if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { + /* + * No ADDBA request yet, don't touch. + */ + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, + ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid); + vap->iv_stats.is_ampdu_bar_bad++; + return; + } + vap->iv_stats.is_ampdu_bar_rx++; + rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; + if (rxseq == rap->rxa_start) + return; + /* calculate offset in BA window */ + off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); + if (off < IEEE80211_SEQ_BA_RANGE) { + /* + * Flush the reorder q up to rxseq and move the window. + * Sec 9.10.7.6 a) (D2.04 p.119 line 22) + */ + IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, + "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u", + rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), + rap->rxa_qframes, rxseq, tid); + vap->iv_stats.is_ampdu_bar_move++; + + ampdu_rx_flush_upto(ni, rap, rxseq); + if (off >= rap->rxa_wnd) { + /* + * BAR specifies a window start to the right of BA + * window; we must move it explicitly since + * ampdu_rx_flush_upto will not. + */ + rap->rxa_start = rxseq; + } + } else { + /* + * Out of range; toss. + * Sec 9.10.7.6 b) (D2.04 p.119 line 41) + */ + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, + "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", + rap->rxa_start, + IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), + rap->rxa_qframes, rxseq, tid, + wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); + vap->iv_stats.is_ampdu_bar_oow++; + IEEE80211_NODE_STAT(ni, rx_drop); + } +} + +/* + * Setup HT-specific state in a node. Called only + * when HT use is negotiated so we don't do extra + * work for temporary and/or legacy sta's. + */ +void +ieee80211_ht_node_init(struct ieee80211_node *ni) +{ + struct ieee80211_tx_ampdu *tap; + int ac; + + if (ni->ni_flags & IEEE80211_NODE_HT) { + /* + * Clean AMPDU 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_ht_node_cleanup(ni); + } + for (ac = 0; ac < WME_NUM_AC; ac++) { + tap = &ni->ni_tx_ampdu[ac]; + tap->txa_ac = ac; + tap->txa_ni = ni; + /* NB: further initialization deferred */ + } + ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; +} + +/* + * Cleanup HT-specific state in a node. Called only + * when HT use has been marked. + */ +void +ieee80211_ht_node_cleanup(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + int i; + + KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node")); + + /* XXX optimize this */ + for (i = 0; i < WME_NUM_AC; i++) { + struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i]; + if (tap->txa_flags & IEEE80211_AGGR_SETUP) + ampdu_tx_stop(tap); + } + for (i = 0; i < WME_NUM_TID; i++) + ic->ic_ampdu_rx_stop(ni, &ni->ni_rx_ampdu[i]); + + ni->ni_htcap = 0; + ni->ni_flags &= ~IEEE80211_NODE_HT_ALL; +} + +/* + * Age out HT resources for a station. + */ +void +ieee80211_ht_node_age(struct ieee80211_node *ni) +{ +#ifdef IEEE80211_AMPDU_AGE + struct ieee80211vap *vap = ni->ni_vap; + uint8_t tid; +#endif + + KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); + +#ifdef IEEE80211_AMPDU_AGE + for (tid = 0; tid < WME_NUM_TID; tid++) { + struct ieee80211_rx_ampdu *rap; + + rap = &ni->ni_rx_ampdu[tid]; + if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) + continue; + if (rap->rxa_qframes == 0) + continue; + /* + * Check for frames sitting too long in the reorder queue. + * See above for more details on what's happening here. + */ + /* XXX honor batimeout? */ + if (ticks - rap->rxa_age > ieee80211_ampdu_age) { + /* + * Too long since we received the first + * frame; flush the reorder buffer. + */ + vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes; + ampdu_rx_flush(ni, rap); + } + } +#endif /* IEEE80211_AMPDU_AGE */ +} + +static struct ieee80211_channel * +findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags) +{ + return ieee80211_find_channel(ic, c->ic_freq, + (c->ic_flags &~ IEEE80211_CHAN_HT) | htflags); +} + +/* + * Adjust a channel to be HT/non-HT according to the vap's configuration. + */ +struct ieee80211_channel * +ieee80211_ht_adjust_channel(struct ieee80211com *ic, + struct ieee80211_channel *chan, int flags) +{ + struct ieee80211_channel *c; + + if (flags & IEEE80211_FHT_HT) { + /* promote to HT if possible */ + if (flags & IEEE80211_FHT_USEHT40) { + if (!IEEE80211_IS_CHAN_HT40(chan)) { + /* NB: arbitrarily pick ht40+ over ht40- */ + c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U); + if (c == NULL) + c = findhtchan(ic, chan, + IEEE80211_CHAN_HT40D); + if (c == NULL) + c = findhtchan(ic, chan, + IEEE80211_CHAN_HT20); + if (c != NULL) + chan = c; + } + } else if (!IEEE80211_IS_CHAN_HT20(chan)) { + c = findhtchan(ic, chan, IEEE80211_CHAN_HT20); + if (c != NULL) + chan = c; + } + } else if (IEEE80211_IS_CHAN_HT(chan)) { + /* demote to legacy, HT use is disabled */ + c = ieee80211_find_channel(ic, chan->ic_freq, + chan->ic_flags &~ IEEE80211_CHAN_HT); + if (c != NULL) + chan = c; + } + return chan; +} + +/* + * Setup HT-specific state for a legacy WDS peer. + */ +void +ieee80211_ht_wds_init(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_tx_ampdu *tap; + int ac; + + KASSERT(vap->iv_flags_ht & IEEE80211_FHT_HT, ("no HT requested")); + + /* XXX check scan cache in case peer has an ap and we have info */ + /* + * If setup with a legacy channel; locate an HT channel. + * Otherwise if the inherited channel (from a companion + * AP) is suitable use it so we use the same location + * for the extension channel). + */ + ni->ni_chan = ieee80211_ht_adjust_channel(ni->ni_ic, + ni->ni_chan, ieee80211_htchanflags(ni->ni_chan)); + + ni->ni_htcap = 0; + if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) + ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20; + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { + ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40; + ni->ni_chw = 40; + if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) + ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE; + else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) + ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW; + if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) + ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40; + } else { + ni->ni_chw = 20; + ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE; + } + ni->ni_htctlchan = ni->ni_chan->ic_ieee; + if (vap->iv_flags_ht & IEEE80211_FHT_RIFS) + ni->ni_flags |= IEEE80211_NODE_RIFS; + /* XXX does it make sense to enable SMPS? */ + + ni->ni_htopmode = 0; /* XXX need protection state */ + ni->ni_htstbc = 0; /* XXX need info */ + + for (ac = 0; ac < WME_NUM_AC; ac++) { + tap = &ni->ni_tx_ampdu[ac]; + tap->txa_ac = ac; + } + /* NB: AMPDU tx/rx governed by IEEE80211_FHT_AMPDU_{TX,RX} */ + ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; +} + +/* + * Notify hostap vaps of a change in the HTINFO ie. + */ +static void +htinfo_notify(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + int first = 1; + + IEEE80211_LOCK_ASSERT(ic); + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap->iv_opmode != IEEE80211_M_HOSTAP) + continue; + if (vap->iv_state != IEEE80211_S_RUN || + !IEEE80211_IS_CHAN_HT(vap->iv_bss->ni_chan)) + continue; + if (first) { + IEEE80211_NOTE(vap, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, + vap->iv_bss, + "HT bss occupancy change: %d sta, %d ht, " + "%d ht40%s, HT protmode now 0x%x" + , ic->ic_sta_assoc + , ic->ic_ht_sta_assoc + , ic->ic_ht40_sta_assoc + , (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) ? + ", non-HT sta present" : "" + , ic->ic_curhtprotmode); + first = 0; + } + ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO); + } +} + +/* + * Calculate HT protection mode from current + * state and handle updates. + */ +static void +htinfo_update(struct ieee80211com *ic) +{ + uint8_t protmode; + + if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) { + protmode = IEEE80211_HTINFO_OPMODE_MIXED + | IEEE80211_HTINFO_NONHT_PRESENT; + } else if (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) { + protmode = IEEE80211_HTINFO_OPMODE_PROTOPT + | IEEE80211_HTINFO_NONHT_PRESENT; + } else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && + ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) { + protmode = IEEE80211_HTINFO_OPMODE_HT20PR; + } else { + protmode = IEEE80211_HTINFO_OPMODE_PURE; + } + if (protmode != ic->ic_curhtprotmode) { + ic->ic_curhtprotmode = protmode; + htinfo_notify(ic); + } +} + +/* + * Handle an HT station joining a BSS. + */ +void +ieee80211_ht_node_join(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + IEEE80211_LOCK_ASSERT(ic); + + if (ni->ni_flags & IEEE80211_NODE_HT) { + ic->ic_ht_sta_assoc++; + if (ni->ni_chw == 40) + ic->ic_ht40_sta_assoc++; + } + htinfo_update(ic); +} + +/* + * Handle an HT station leaving a BSS. + */ +void +ieee80211_ht_node_leave(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + IEEE80211_LOCK_ASSERT(ic); + + if (ni->ni_flags & IEEE80211_NODE_HT) { + ic->ic_ht_sta_assoc--; + if (ni->ni_chw == 40) + ic->ic_ht40_sta_assoc--; + } + htinfo_update(ic); +} + +/* + * Public version of htinfo_update; used for processing + * beacon frames from overlapping bss. + * + * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED + * (on receipt of a beacon that advertises MIXED) or + * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon + * from an overlapping legacy bss). We treat MIXED with + * a higher precedence than PROTOPT (i.e. we will not change + * change PROTOPT -> MIXED; only MIXED -> PROTOPT). This + * corresponds to how we handle things in htinfo_update. + */ +void +ieee80211_htprot_update(struct ieee80211com *ic, int protmode) +{ +#define OPMODE(x) SM(x, IEEE80211_HTINFO_OPMODE) + IEEE80211_LOCK(ic); + + /* track non-HT station presence */ + KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT, + ("protmode 0x%x", protmode)); + ic->ic_flags_ht |= IEEE80211_FHT_NONHT_PR; + ic->ic_lastnonht = ticks; + + if (protmode != ic->ic_curhtprotmode && + (OPMODE(ic->ic_curhtprotmode) != IEEE80211_HTINFO_OPMODE_MIXED || + OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)) { + /* push beacon update */ + ic->ic_curhtprotmode = protmode; + htinfo_notify(ic); + } + IEEE80211_UNLOCK(ic); +#undef OPMODE +} + +/* + * Time out presence of an overlapping bss with non-HT + * stations. When operating in hostap mode we listen for + * beacons from other stations and if we identify a non-HT + * station is present we update the opmode field of the + * HTINFO ie. To identify when all non-HT stations are + * gone we time out this condition. + */ +void +ieee80211_ht_timeout(struct ieee80211com *ic) +{ + IEEE80211_LOCK_ASSERT(ic); + + if ((ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) && + time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) { +#if 0 + IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, + "%s", "time out non-HT STA present on channel"); +#endif + ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR; + htinfo_update(ic); + } +} + +/* unalligned little endian access */ +#define LE_READ_2(p) \ + ((uint16_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_t *)(p))[1] << 8))) + +/* + * Process an 802.11n HT capabilities ie. + */ +void +ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie) +{ + if (ie[0] == IEEE80211_ELEMID_VENDOR) { + /* + * Station used Vendor OUI ie to associate; + * mark the node so when we respond we'll use + * the Vendor OUI's and not the standard ie's. + */ + ni->ni_flags |= IEEE80211_NODE_HTCOMPAT; + ie += 4; + } else + ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT; + + ni->ni_htcap = LE_READ_2(ie + + __offsetof(struct ieee80211_ie_htcap, hc_cap)); + ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)]; +} + +static void +htinfo_parse(struct ieee80211_node *ni, + const struct ieee80211_ie_htinfo *htinfo) +{ + uint16_t w; + + ni->ni_htctlchan = htinfo->hi_ctrlchannel; + ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN); + w = LE_READ_2(&htinfo->hi_byte2); + ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE); + w = LE_READ_2(&htinfo->hi_byte45); + ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS); +} + +/* + * Parse an 802.11n HT info ie and save useful information + * to the node state. Note this does not effect any state + * changes such as for channel width change. + */ +void +ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) +{ + if (ie[0] == IEEE80211_ELEMID_VENDOR) + ie += 4; + htinfo_parse(ni, (const struct ieee80211_ie_htinfo *) ie); +} + +/* + * Handle 11n channel switch. Use the received HT ie's to + * identify the right channel to use. If we cannot locate it + * in the channel table then fallback to legacy operation. + * Note that we use this information to identify the node's + * channel only; the caller is responsible for insuring any + * required channel change is done (e.g. in sta mode when + * parsing the contents of a beacon frame). + */ +static void +htinfo_update_chw(struct ieee80211_node *ni, int htflags) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_channel *c; + int chanflags; + + chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags; + if (chanflags != ni->ni_chan->ic_flags) { + /* XXX not right for ht40- */ + c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags); + if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) { + /* + * No HT40 channel entry in our table; fall back + * to HT20 operation. This should not happen. + */ + c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20); +#if 0 + IEEE80211_NOTE(ni->ni_vap, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, + "no HT40 channel (freq %u), falling back to HT20", + ni->ni_chan->ic_freq); +#endif + /* XXX stat */ + } + if (c != NULL && c != ni->ni_chan) { + IEEE80211_NOTE(ni->ni_vap, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, + "switch station to HT%d channel %u/0x%x", + IEEE80211_IS_CHAN_HT40(c) ? 40 : 20, + c->ic_freq, c->ic_flags); + ni->ni_chan = c; + } + /* NB: caller responsible for forcing any channel change */ + } + /* update node's tx channel width */ + ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan)? 40 : 20; +} + +/* + * Update 11n MIMO PS state according to received htcap. + */ +static __inline int +htcap_update_mimo_ps(struct ieee80211_node *ni) +{ + uint16_t oflags = ni->ni_flags; + + switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) { + case IEEE80211_HTCAP_SMPS_DYNAMIC: + ni->ni_flags |= IEEE80211_NODE_MIMO_PS; + ni->ni_flags |= IEEE80211_NODE_MIMO_RTS; + break; + case IEEE80211_HTCAP_SMPS_ENA: + ni->ni_flags |= IEEE80211_NODE_MIMO_PS; + ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; + break; + case IEEE80211_HTCAP_SMPS_OFF: + default: /* disable on rx of reserved value */ + ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS; + ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; + break; + } + return (oflags ^ ni->ni_flags); +} + +/* + * Update short GI state according to received htcap + * and local settings. + */ +static __inline void +htcap_update_shortgi(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + + ni->ni_flags &= ~(IEEE80211_NODE_SGI20|IEEE80211_NODE_SGI40); + if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) && + (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20)) + ni->ni_flags |= IEEE80211_NODE_SGI20; + if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) && + (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40)) + ni->ni_flags |= IEEE80211_NODE_SGI40; +} + +/* + * Parse and update HT-related state extracted from + * the HT cap and info ie's. + */ +void +ieee80211_ht_updateparams(struct ieee80211_node *ni, + const uint8_t *htcapie, const uint8_t *htinfoie) +{ + struct ieee80211vap *vap = ni->ni_vap; + const struct ieee80211_ie_htinfo *htinfo; + int htflags; + + ieee80211_parse_htcap(ni, htcapie); + if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS) + htcap_update_mimo_ps(ni); + htcap_update_shortgi(ni); + + if (htinfoie[0] == IEEE80211_ELEMID_VENDOR) + htinfoie += 4; + htinfo = (const struct ieee80211_ie_htinfo *) htinfoie; + htinfo_parse(ni, htinfo); + + htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ? + IEEE80211_CHAN_HT20 : 0; + /* NB: honor operating mode constraint */ + if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) && + (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) { + if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE) + htflags = IEEE80211_CHAN_HT40U; + else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW) + htflags = IEEE80211_CHAN_HT40D; + } + htinfo_update_chw(ni, htflags); + + if ((htinfo->hi_byte1 & IEEE80211_HTINFO_RIFSMODE_PERM) && + (vap->iv_flags_ht & IEEE80211_FHT_RIFS)) + ni->ni_flags |= IEEE80211_NODE_RIFS; + else + ni->ni_flags &= ~IEEE80211_NODE_RIFS; +} + +/* + * Parse and update HT-related state extracted from the HT cap ie + * for a station joining an HT BSS. + */ +void +ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie) +{ + struct ieee80211vap *vap = ni->ni_vap; + int htflags; + + ieee80211_parse_htcap(ni, htcapie); + if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS) + htcap_update_mimo_ps(ni); + htcap_update_shortgi(ni); + + /* NB: honor operating mode constraint */ + /* XXX 40 MHZ intolerant */ + htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ? + IEEE80211_CHAN_HT20 : 0; + if ((ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) && + (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) { + if (IEEE80211_IS_CHAN_HT40U(vap->iv_bss->ni_chan)) + htflags = IEEE80211_CHAN_HT40U; + else if (IEEE80211_IS_CHAN_HT40D(vap->iv_bss->ni_chan)) + htflags = IEEE80211_CHAN_HT40D; + } + htinfo_update_chw(ni, htflags); +} + +/* + * Install received HT rate set by parsing the HT cap ie. + */ +int +ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags) +{ + struct ieee80211vap *vap = ni->ni_vap; + const struct ieee80211_ie_htcap *htcap; + struct ieee80211_htrateset *rs; + int i; + + rs = &ni->ni_htrates; + memset(rs, 0, sizeof(*rs)); + if (ie != NULL) { + if (ie[0] == IEEE80211_ELEMID_VENDOR) + ie += 4; + htcap = (const struct ieee80211_ie_htcap *) ie; + for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { + if (isclr(htcap->hc_mcsset, i)) + continue; + if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) { + IEEE80211_NOTE(vap, + IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, + "WARNING, HT rate set too large; only " + "using %u rates", IEEE80211_HTRATE_MAXSIZE); + vap->iv_stats.is_rx_rstoobig++; + break; + } + rs->rs_rates[rs->rs_nrates++] = i; + } + } + return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags); +} + +/* + * Mark rates in a node's HT rate set as basic according + * to the information in the supplied HT info ie. + */ +void +ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie) +{ + const struct ieee80211_ie_htinfo *htinfo; + struct ieee80211_htrateset *rs; + int i, j; + + if (ie[0] == IEEE80211_ELEMID_VENDOR) + ie += 4; + htinfo = (const struct ieee80211_ie_htinfo *) ie; + rs = &ni->ni_htrates; + if (rs->rs_nrates == 0) { + IEEE80211_NOTE(ni->ni_vap, + IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, + "%s", "WARNING, empty HT rate set"); + return; + } + for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { + if (isclr(htinfo->hi_basicmcsset, i)) + continue; + for (j = 0; j < rs->rs_nrates; j++) + if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i) + rs->rs_rates[j] |= IEEE80211_RATE_BASIC; + } +} + +static void +ampdu_tx_setup(struct ieee80211_tx_ampdu *tap) +{ + callout_init(&tap->txa_timer, CALLOUT_MPSAFE); + tap->txa_flags |= IEEE80211_AGGR_SETUP; +} + +static void +ampdu_tx_stop(struct ieee80211_tx_ampdu *tap) +{ + struct ieee80211_node *ni = tap->txa_ni; + struct ieee80211com *ic = ni->ni_ic; + + KASSERT(tap->txa_flags & IEEE80211_AGGR_SETUP, + ("txa_flags 0x%x ac %d", tap->txa_flags, tap->txa_ac)); + + /* + * Stop BA stream if setup so driver has a chance + * to reclaim any resources it might have allocated. + */ + ic->ic_addba_stop(ni, tap); + /* + * Stop any pending BAR transmit. + */ + bar_stop_timer(tap); + + tap->txa_lastsample = 0; + tap->txa_avgpps = 0; + /* NB: clearing NAK means we may re-send ADDBA */ + tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK); +} + +static void +addba_timeout(void *arg) +{ + struct ieee80211_tx_ampdu *tap = arg; + + /* XXX ? */ + tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; + tap->txa_attempts++; +} + +static void +addba_start_timeout(struct ieee80211_tx_ampdu *tap) +{ + /* XXX use CALLOUT_PENDING instead? */ + callout_reset(&tap->txa_timer, ieee80211_addba_timeout, + addba_timeout, tap); + tap->txa_flags |= IEEE80211_AGGR_XCHGPEND; + tap->txa_nextrequest = ticks + ieee80211_addba_timeout; +} + +static void +addba_stop_timeout(struct ieee80211_tx_ampdu *tap) +{ + /* XXX use CALLOUT_PENDING instead? */ + if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) { + callout_stop(&tap->txa_timer); + tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; + } +} + +/* + * Default method for requesting A-MPDU tx aggregation. + * We setup the specified state block and start a timer + * to wait for an ADDBA response frame. + */ +static int +ieee80211_addba_request(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int dialogtoken, int baparamset, int batimeout) +{ + int bufsiz; + + /* XXX locking */ + tap->txa_token = dialogtoken; + tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE; + bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); + tap->txa_wnd = (bufsiz == 0) ? + IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); + addba_start_timeout(tap); + return 1; +} + +/* + * Default method for processing an A-MPDU tx aggregation + * response. We shutdown any pending timer and update the + * state block according to the reply. + */ +static int +ieee80211_addba_response(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int status, int baparamset, int batimeout) +{ + int bufsiz, tid; + + /* XXX locking */ + addba_stop_timeout(tap); + if (status == IEEE80211_STATUS_SUCCESS) { + bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); + /* XXX override our request? */ + tap->txa_wnd = (bufsiz == 0) ? + IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); + /* XXX AC/TID */ + tid = MS(baparamset, IEEE80211_BAPS_TID); + tap->txa_flags |= IEEE80211_AGGR_RUNNING; + tap->txa_attempts = 0; + } else { + /* mark tid so we don't try again */ + tap->txa_flags |= IEEE80211_AGGR_NAK; + } + return 1; +} + +/* + * Default method for stopping A-MPDU tx aggregation. + * Any timer is cleared and we drain any pending frames. + */ +static void +ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) +{ + /* XXX locking */ + addba_stop_timeout(tap); + if (tap->txa_flags & IEEE80211_AGGR_RUNNING) { + /* XXX clear aggregation queue */ + tap->txa_flags &= ~IEEE80211_AGGR_RUNNING; + } + tap->txa_attempts = 0; +} + +/* + * Process a received action frame using the default aggregation + * policy. We intercept ADDBA-related frames and use them to + * update our aggregation state. All other frames are passed up + * for processing by ieee80211_recv_action. + */ +static int +ht_recv_action_ba_addba_request(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_rx_ampdu *rap; + uint8_t dialogtoken; + uint16_t baparamset, batimeout, baseqctl; + uint16_t args[4]; + int tid; + + dialogtoken = frm[2]; + baparamset = LE_READ_2(frm+3); + batimeout = LE_READ_2(frm+5); + baseqctl = LE_READ_2(frm+7); + + tid = MS(baparamset, IEEE80211_BAPS_TID); + + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "recv ADDBA request: dialogtoken %u baparamset 0x%x " + "(tid %d bufsiz %d) batimeout %d baseqctl %d:%d", + dialogtoken, baparamset, + tid, MS(baparamset, IEEE80211_BAPS_BUFSIZ), + batimeout, + MS(baseqctl, IEEE80211_BASEQ_START), + MS(baseqctl, IEEE80211_BASEQ_FRAG)); + + rap = &ni->ni_rx_ampdu[tid]; + + /* Send ADDBA response */ + args[0] = dialogtoken; + /* + * NB: We ack only if the sta associated with HT and + * the ap is configured to do AMPDU rx (the latter + * violates the 11n spec and is mostly for testing). + */ + if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) && + (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_RX)) { + /* XXX handle ampdu_rx_start failure */ + ic->ic_ampdu_rx_start(ni, rap, + baparamset, batimeout, baseqctl); + + args[1] = IEEE80211_STATUS_SUCCESS; + } else { + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni, "reject ADDBA request: %s", + ni->ni_flags & IEEE80211_NODE_AMPDU_RX ? + "administratively disabled" : + "not negotiated for station"); + vap->iv_stats.is_addba_reject++; + args[1] = IEEE80211_STATUS_UNSPECIFIED; + } + /* XXX honor rap flags? */ + args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE + | SM(tid, IEEE80211_BAPS_TID) + | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ) + ; + args[3] = 0; + ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_ADDBA_RESPONSE, args); + return 0; +} + +static int +ht_recv_action_ba_addba_response(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_tx_ampdu *tap; + uint8_t dialogtoken, policy; + uint16_t baparamset, batimeout, code; + int tid, ac, bufsiz; + + dialogtoken = frm[2]; + code = LE_READ_2(frm+3); + baparamset = LE_READ_2(frm+5); + tid = MS(baparamset, IEEE80211_BAPS_TID); + bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ); + policy = MS(baparamset, IEEE80211_BAPS_POLICY); + batimeout = LE_READ_2(frm+7); + + ac = TID_TO_WME_AC(tid); + tap = &ni->ni_tx_ampdu[ac]; + if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni->ni_macaddr, "ADDBA response", + "no pending ADDBA, tid %d dialogtoken %u " + "code %d", tid, dialogtoken, code); + vap->iv_stats.is_addba_norequest++; + return 0; + } + if (dialogtoken != tap->txa_token) { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni->ni_macaddr, "ADDBA response", + "dialogtoken mismatch: waiting for %d, " + "received %d, tid %d code %d", + tap->txa_token, dialogtoken, tid, code); + vap->iv_stats.is_addba_badtoken++; + return 0; + } + /* NB: assumes IEEE80211_AGGR_IMMEDIATE is 1 */ + if (policy != (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE)) { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni->ni_macaddr, "ADDBA response", + "policy mismatch: expecting %s, " + "received %s, tid %d code %d", + tap->txa_flags & IEEE80211_AGGR_IMMEDIATE, + policy, tid, code); + vap->iv_stats.is_addba_badpolicy++; + return 0; + } +#if 0 + /* XXX we take MIN in ieee80211_addba_response */ + if (bufsiz > IEEE80211_AGGR_BAWMAX) { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni->ni_macaddr, "ADDBA response", + "BA window too large: max %d, " + "received %d, tid %d code %d", + bufsiz, IEEE80211_AGGR_BAWMAX, tid, code); + vap->iv_stats.is_addba_badbawinsize++; + return 0; + } +#endif + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "recv ADDBA response: dialogtoken %u code %d " + "baparamset 0x%x (tid %d bufsiz %d) batimeout %d", + dialogtoken, code, baparamset, tid, bufsiz, + batimeout); + ic->ic_addba_response(ni, tap, code, baparamset, batimeout); + return 0; +} + +static int +ht_recv_action_ba_delba(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_rx_ampdu *rap; + struct ieee80211_tx_ampdu *tap; + uint16_t baparamset, code; + int tid, ac; + + baparamset = LE_READ_2(frm+2); + code = LE_READ_2(frm+4); + + tid = MS(baparamset, IEEE80211_DELBAPS_TID); + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "recv DELBA: baparamset 0x%x (tid %d initiator %d) " + "code %d", baparamset, tid, + MS(baparamset, IEEE80211_DELBAPS_INIT), code); + + if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) { + ac = TID_TO_WME_AC(tid); + tap = &ni->ni_tx_ampdu[ac]; + ic->ic_addba_stop(ni, tap); + } else { + rap = &ni->ni_rx_ampdu[tid]; + ic->ic_ampdu_rx_stop(ni, rap); + } + return 0; +} + +static int +ht_recv_action_ht_txchwidth(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + int chw; + + chw = (frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040) ? 40 : 20; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "%s: HT txchwidth, width %d%s", + __func__, chw, ni->ni_chw != chw ? "*" : ""); + if (chw != ni->ni_chw) { + ni->ni_chw = chw; + /* XXX notify on change */ + } + return 0; +} + +static int +ht_recv_action_ht_mimopwrsave(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + const struct ieee80211_action_ht_mimopowersave *mps = + (const struct ieee80211_action_ht_mimopowersave *) frm; + + /* XXX check iv_htcaps */ + if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_ENA) + ni->ni_flags |= IEEE80211_NODE_MIMO_PS; + else + ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS; + if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_MODE) + ni->ni_flags |= IEEE80211_NODE_MIMO_RTS; + else + ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; + /* XXX notify on change */ + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "%s: HT MIMO PS (%s%s)", __func__, + (ni->ni_flags & IEEE80211_NODE_MIMO_PS) ? "on" : "off", + (ni->ni_flags & IEEE80211_NODE_MIMO_RTS) ? "+rts" : "" + ); + return 0; +} + +/* + * Transmit processing. + */ + +/* + * Check if A-MPDU should be requested/enabled for a stream. + * We require a traffic rate above a per-AC threshold and we + * also handle backoff from previous failed attempts. + * + * Drivers may override this method to bring in information + * such as link state conditions in making the decision. + */ +static int +ieee80211_ampdu_enable(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap) +{ + struct ieee80211vap *vap = ni->ni_vap; + + if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[tap->txa_ac]) + return 0; + /* XXX check rssi? */ + if (tap->txa_attempts >= ieee80211_addba_maxtries && + ticks < tap->txa_nextrequest) { + /* + * Don't retry too often; txa_nextrequest is set + * to the minimum interval we'll retry after + * ieee80211_addba_maxtries failed attempts are made. + */ + return 0; + } + IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, + "enable AMPDU on %s, avgpps %d pkts %d", + ieee80211_wme_acnames[tap->txa_ac], tap->txa_avgpps, tap->txa_pkts); + return 1; +} + +/* + * Request A-MPDU tx aggregation. Setup local state and + * issue an ADDBA request. BA use will only happen after + * the other end replies with ADDBA response. + */ +int +ieee80211_ampdu_request(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap) +{ + struct ieee80211com *ic = ni->ni_ic; + uint16_t args[4]; + int tid, dialogtoken; + static int tokens = 0; /* XXX */ + + /* XXX locking */ + if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) { + /* do deferred setup of state */ + ampdu_tx_setup(tap); + } + /* XXX hack for not doing proper locking */ + tap->txa_flags &= ~IEEE80211_AGGR_NAK; + + dialogtoken = (tokens+1) % 63; /* XXX */ + tid = WME_AC_TO_TID(tap->txa_ac); + tap->txa_start = ni->ni_txseqs[tid]; + + args[0] = dialogtoken; + args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE + | SM(tid, IEEE80211_BAPS_TID) + | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ) + ; + args[2] = 0; /* batimeout */ + /* NB: do first so there's no race against reply */ + if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) { + /* unable to setup state, don't make request */ + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, + ni, "%s: could not setup BA stream for AC %d", + __func__, tap->txa_ac); + /* defer next try so we don't slam the driver with requests */ + tap->txa_attempts = ieee80211_addba_maxtries; + /* NB: check in case driver wants to override */ + if (tap->txa_nextrequest <= ticks) + tap->txa_nextrequest = ticks + ieee80211_addba_backoff; + return 0; + } + tokens = dialogtoken; /* allocate token */ + /* NB: after calling ic_addba_request so driver can set txa_start */ + args[3] = SM(tap->txa_start, IEEE80211_BASEQ_START) + | SM(0, IEEE80211_BASEQ_FRAG) + ; + return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_ADDBA_REQUEST, args); +} + +/* + * Terminate an AMPDU tx stream. State is reclaimed + * and the peer notified with a DelBA Action frame. + */ +void +ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, + int reason) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; + uint16_t args[4]; + + /* XXX locking */ + tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; + if (IEEE80211_AMPDU_RUNNING(tap)) { + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni, "%s: stop BA stream for AC %d (reason %d)", + __func__, tap->txa_ac, reason); + vap->iv_stats.is_ampdu_stop++; + + ic->ic_addba_stop(ni, tap); + args[0] = WME_AC_TO_TID(tap->txa_ac); + args[1] = IEEE80211_DELBAPS_INIT; + args[2] = reason; /* XXX reason code */ + ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, + IEEE80211_ACTION_BA_DELBA, args); + } else { + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, + ni, "%s: BA stream for AC %d not running (reason %d)", + __func__, tap->txa_ac, reason); + vap->iv_stats.is_ampdu_stop_failed++; + } +} + +static void +bar_timeout(void *arg) +{ + struct ieee80211_tx_ampdu *tap = arg; + struct ieee80211_node *ni = tap->txa_ni; + + KASSERT((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0, + ("bar/addba collision, flags 0x%x", tap->txa_flags)); + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, + ni, "%s: tid %u flags 0x%x attempts %d", __func__, + tap->txa_ac, tap->txa_flags, tap->txa_attempts); + + /* guard against race with bar_tx_complete */ + if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0) + return; + /* XXX ? */ + if (tap->txa_attempts >= ieee80211_bar_maxtries) + ieee80211_ampdu_stop(ni, tap, IEEE80211_REASON_TIMEOUT); + else + ieee80211_send_bar(ni, tap, tap->txa_seqpending); +} + +static void +bar_start_timer(struct ieee80211_tx_ampdu *tap) +{ + callout_reset(&tap->txa_timer, ieee80211_bar_timeout, bar_timeout, tap); +} + +static void +bar_stop_timer(struct ieee80211_tx_ampdu *tap) +{ + callout_stop(&tap->txa_timer); +} + +static void +bar_tx_complete(struct ieee80211_node *ni, void *arg, int status) +{ + struct ieee80211_tx_ampdu *tap = arg; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, + ni, "%s: tid %u flags 0x%x pending %d status %d", + __func__, tap->txa_ac, tap->txa_flags, + callout_pending(&tap->txa_timer), status); + + /* XXX locking */ + if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) && + callout_pending(&tap->txa_timer)) { + struct ieee80211com *ic = ni->ni_ic; + + if (status) /* ACK'd */ + bar_stop_timer(tap); + ic->ic_bar_response(ni, tap, status); + /* NB: just let timer expire so we pace requests */ + } +} + +static void +ieee80211_bar_response(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, int status) +{ + + if (status != 0) { /* got ACK */ + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, + ni, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u", + tap->txa_start, + IEEE80211_SEQ_ADD(tap->txa_start, tap->txa_wnd-1), + tap->txa_qframes, tap->txa_seqpending, + WME_AC_TO_TID(tap->txa_ac)); + + /* NB: timer already stopped in bar_tx_complete */ + tap->txa_start = tap->txa_seqpending; + tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; + } +} + +/* + * Transmit a BAR frame to the specified node. The + * BAR contents are drawn from the supplied aggregation + * state associated with the node. + * + * NB: we only handle immediate ACK w/ compressed bitmap. + */ +int +ieee80211_send_bar(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, ieee80211_seq seq) +{ +#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame_bar *bar; + struct mbuf *m; + uint16_t barctl, barseqctl; + uint8_t *frm; + int tid, ret; + + if ((tap->txa_flags & IEEE80211_AGGR_RUNNING) == 0) { + /* no ADDBA response, should not happen */ + /* XXX stat+msg */ + return EINVAL; + } + /* XXX locking */ + bar_stop_timer(tap); + + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, ic->ic_headroom, sizeof(*bar)); + if (m == NULL) + senderr(ENOMEM, is_tx_nobuf); + + if (!ieee80211_add_callback(m, bar_tx_complete, tap)) { + m_freem(m); + senderr(ENOMEM, is_tx_nobuf); /* XXX */ + /* NOTREACHED */ + } + + bar = mtod(m, struct ieee80211_frame_bar *); + bar->i_fc[0] = IEEE80211_FC0_VERSION_0 | + IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR; + bar->i_fc[1] = 0; + IEEE80211_ADDR_COPY(bar->i_ra, ni->ni_macaddr); + IEEE80211_ADDR_COPY(bar->i_ta, vap->iv_myaddr); + + tid = WME_AC_TO_TID(tap->txa_ac); + barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ? + 0 : IEEE80211_BAR_NOACK) + | IEEE80211_BAR_COMP + | SM(tid, IEEE80211_BAR_TID) + ; + barseqctl = SM(seq, IEEE80211_BAR_SEQ_START); + /* NB: known to have proper alignment */ + bar->i_ctl = htole16(barctl); + bar->i_seq = htole16(barseqctl); + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_bar); + + M_WME_SETAC(m, WME_AC_VO); + + IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */ + + /* XXX locking */ + /* init/bump attempts counter */ + if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0) + tap->txa_attempts = 1; + else + tap->txa_attempts++; + tap->txa_seqpending = seq; + tap->txa_flags |= IEEE80211_AGGR_BARPEND; + + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N, + ni, "send BAR: tid %u ctl 0x%x start %u (attempt %d)", + tid, barctl, seq, tap->txa_attempts); + + ret = ic->ic_raw_xmit(ni, m, NULL); + if (ret != 0) { + /* xmit failed, clear state flag */ + tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; + goto bad; + } + /* XXX hack against tx complete happening before timer is started */ + if (tap->txa_flags & IEEE80211_AGGR_BARPEND) + bar_start_timer(tap); + return 0; +bad: + ieee80211_free_node(ni); + return ret; +#undef senderr +} + +static int +ht_action_output(struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211_bpf_params params; + + memset(¶ms, 0, sizeof(params)); + params.ibp_pri = WME_AC_VO; + params.ibp_rate0 = ni->ni_txparms->mgmtrate; + /* NB: we know all frames are unicast */ + params.ibp_try0 = ni->ni_txparms->maxretry; + params.ibp_power = ni->ni_txpower; + return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION, + ¶ms); +} + +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) + +/* + * Send an action management frame. The arguments are stuff + * into a frame without inspection; the caller is assumed to + * prepare them carefully (e.g. based on the aggregation state). + */ +static int +ht_send_action_ba_addba(struct ieee80211_node *ni, + int category, int action, void *arg0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + uint16_t *args = arg0; + struct mbuf *m; + uint8_t *frm; + + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "send ADDBA %s: dialogtoken %d " + "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x", + (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) ? + "request" : "response", + args[0], args[1], MS(args[1], IEEE80211_BAPS_TID), + args[2], args[3]); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) /* action+category */ + /* XXX may action payload */ + + sizeof(struct ieee80211_action_ba_addbaresponse) + ); + if (m != NULL) { + *frm++ = category; + *frm++ = action; + *frm++ = args[0]; /* dialog token */ + ADDSHORT(frm, args[1]); /* baparamset */ + ADDSHORT(frm, args[2]); /* batimeout */ + if (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) + ADDSHORT(frm, args[3]); /* baseqctl */ + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + return ht_action_output(ni, m); + } else { + vap->iv_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + return ENOMEM; + } +} + +static int +ht_send_action_ba_delba(struct ieee80211_node *ni, + int category, int action, void *arg0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + uint16_t *args = arg0; + struct mbuf *m; + uint16_t baparamset; + uint8_t *frm; + + baparamset = SM(args[0], IEEE80211_DELBAPS_TID) + | args[1] + ; + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "send DELBA action: tid %d, initiator %d reason %d", + args[0], args[1], args[2]); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) /* action+category */ + /* XXX may action payload */ + + sizeof(struct ieee80211_action_ba_addbaresponse) + ); + if (m != NULL) { + *frm++ = category; + *frm++ = action; + ADDSHORT(frm, baparamset); + ADDSHORT(frm, args[2]); /* reason code */ + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + return ht_action_output(ni, m); + } else { + vap->iv_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + return ENOMEM; + } +} + +static int +ht_send_action_ht_txchwidth(struct ieee80211_node *ni, + int category, int action, void *arg0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct mbuf *m; + uint8_t *frm; + + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, + "send HT txchwidth: width %d", + IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) /* action+category */ + /* XXX may action payload */ + + sizeof(struct ieee80211_action_ba_addbaresponse) + ); + if (m != NULL) { + *frm++ = category; + *frm++ = action; + *frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? + IEEE80211_A_HT_TXCHWIDTH_2040 : + IEEE80211_A_HT_TXCHWIDTH_20; + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + return ht_action_output(ni, m); + } else { + vap->iv_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + return ENOMEM; + } +} +#undef ADDSHORT + +/* + * Construct the MCS bit mask for inclusion + * in an HT information element. + */ +static void +ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) +{ + int i; + + for (i = 0; i < rs->rs_nrates; i++) { + int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; + if (r < IEEE80211_HTRATE_MAXSIZE) { /* XXX? */ + /* NB: this assumes a particular implementation */ + setbit(frm, r); + } + } +} + +/* + * Add body of an HTCAP information element. + */ +static uint8_t * +ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) +{ +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) + struct ieee80211vap *vap = ni->ni_vap; + uint16_t caps; + int rxmax, density; + + /* HT capabilities */ + caps = vap->iv_htcaps & 0xffff; + /* + * Note channel width depends on whether we are operating as + * a sta or not. When operating as a sta we are generating + * a request based on our desired configuration. Otherwise + * we are operational and the channel attributes identify + * how we've been setup (which might be different if a fixed + * channel is specified). + */ + if (vap->iv_opmode == IEEE80211_M_STA) { + /* override 20/40 use based on config */ + if (vap->iv_flags_ht & IEEE80211_FHT_USEHT40) + caps |= IEEE80211_HTCAP_CHWIDTH40; + else + caps &= ~IEEE80211_HTCAP_CHWIDTH40; + /* use advertised setting (XXX locally constraint) */ + rxmax = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); + density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); + } else { + /* override 20/40 use based on current channel */ + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) + caps |= IEEE80211_HTCAP_CHWIDTH40; + else + caps &= ~IEEE80211_HTCAP_CHWIDTH40; + rxmax = vap->iv_ampdu_rxmax; + density = vap->iv_ampdu_density; + } + /* adjust short GI based on channel and config */ + if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) == 0) + caps &= ~IEEE80211_HTCAP_SHORTGI20; + if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) == 0 || + (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) + caps &= ~IEEE80211_HTCAP_SHORTGI40; + ADDSHORT(frm, caps); + + /* HT parameters */ + *frm = SM(rxmax, IEEE80211_HTCAP_MAXRXAMPDU) + | SM(density, IEEE80211_HTCAP_MPDUDENSITY) + ; + frm++; + + /* pre-zero remainder of ie */ + memset(frm, 0, sizeof(struct ieee80211_ie_htcap) - + __offsetof(struct ieee80211_ie_htcap, hc_mcsset)); + + /* supported MCS set */ + /* + * XXX it would better to get the rate set from ni_htrates + * so we can restrict it but for sta mode ni_htrates isn't + * setup when we're called to form an AssocReq frame so for + * now we're restricted to the default HT rate set. + */ + ieee80211_set_htrates(frm, &ieee80211_rateset_11n); + + frm += sizeof(struct ieee80211_ie_htcap) - + __offsetof(struct ieee80211_ie_htcap, hc_mcsset); + return frm; +#undef ADDSHORT +} + +/* + * Add 802.11n HT capabilities information element + */ +uint8_t * +ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni) +{ + frm[0] = IEEE80211_ELEMID_HTCAP; + frm[1] = sizeof(struct ieee80211_ie_htcap) - 2; + return ieee80211_add_htcap_body(frm + 2, ni); +} + +/* + * Add Broadcom OUI wrapped standard HTCAP ie; this is + * used for compatibility w/ pre-draft implementations. + */ +uint8_t * +ieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni) +{ + frm[0] = IEEE80211_ELEMID_VENDOR; + frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2; + frm[2] = (BCM_OUI >> 0) & 0xff; + frm[3] = (BCM_OUI >> 8) & 0xff; + frm[4] = (BCM_OUI >> 16) & 0xff; + frm[5] = BCM_OUI_HTCAP; + return ieee80211_add_htcap_body(frm + 6, ni); +} + +/* + * Construct the MCS bit mask of basic rates + * for inclusion in an HT information element. + */ +static void +ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) +{ + int i; + + for (i = 0; i < rs->rs_nrates; i++) { + int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; + if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) && + r < IEEE80211_HTRATE_MAXSIZE) { + /* NB: this assumes a particular implementation */ + setbit(frm, r); + } + } +} + +/* + * Update the HTINFO ie for a beacon frame. + */ +void +ieee80211_ht_update_beacon(struct ieee80211vap *vap, + struct ieee80211_beacon_offsets *bo) +{ +#define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT) + const struct ieee80211_channel *bsschan = vap->iv_bss->ni_chan; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_ie_htinfo *ht = + (struct ieee80211_ie_htinfo *) bo->bo_htinfo; + + /* XXX only update on channel change */ + ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan); + if (vap->iv_flags_ht & IEEE80211_FHT_RIFS) + ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PERM; + else + ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH; + if (IEEE80211_IS_CHAN_HT40U(bsschan)) + ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE; + else if (IEEE80211_IS_CHAN_HT40D(bsschan)) + ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW; + else + ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE; + if (IEEE80211_IS_CHAN_HT40(bsschan)) + ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040; + + /* protection mode */ + ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode; + + /* XXX propagate to vendor ie's */ +#undef PROTMODE +} + +/* + * Add body of an HTINFO information element. + * + * NB: We don't use struct ieee80211_ie_htinfo because we can + * be called to fillin both a standard ie and a compat ie that + * has a vendor OUI at the front. + */ +static uint8_t * +ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + + /* pre-zero remainder of ie */ + memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2); + + /* primary/control channel center */ + *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + + if (vap->iv_flags_ht & IEEE80211_FHT_RIFS) + frm[0] = IEEE80211_HTINFO_RIFSMODE_PERM; + else + frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH; + if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) + frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE; + else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) + frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW; + else + frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE; + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) + frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040; + + frm[1] = ic->ic_curhtprotmode; + + frm += 5; + + /* basic MCS set */ + ieee80211_set_basic_htrates(frm, &ni->ni_htrates); + frm += sizeof(struct ieee80211_ie_htinfo) - + __offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset); + return frm; +} + +/* + * Add 802.11n HT information information element. + */ +uint8_t * +ieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni) +{ + frm[0] = IEEE80211_ELEMID_HTINFO; + frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2; + return ieee80211_add_htinfo_body(frm + 2, ni); +} + +/* + * Add Broadcom OUI wrapped standard HTINFO ie; this is + * used for compatibility w/ pre-draft implementations. + */ +uint8_t * +ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni) +{ + frm[0] = IEEE80211_ELEMID_VENDOR; + frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2; + frm[2] = (BCM_OUI >> 0) & 0xff; + frm[3] = (BCM_OUI >> 8) & 0xff; + frm[4] = (BCM_OUI >> 16) & 0xff; + frm[5] = BCM_OUI_HTINFO; + return ieee80211_add_htinfo_body(frm + 6, ni); +} diff --git a/freebsd/sys/net80211/ieee80211_ht.h b/freebsd/sys/net80211/ieee80211_ht.h new file mode 100644 index 00000000..552a4264 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_ht.h @@ -0,0 +1,202 @@ +/*- + * 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 _NET80211_IEEE80211_HT_HH_ +#define _NET80211_IEEE80211_HT_HH_ + +/* + * 802.11n protocol implementation definitions. + */ + +#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 /* _NET80211_IEEE80211_HT_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_hwmp.c b/freebsd/sys/net80211/ieee80211_hwmp.c new file mode 100644 index 00000000..f0a2cde9 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_hwmp.c @@ -0,0 +1,1440 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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. + */ +#include <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP. + * + * Based on March 2009, D3.0 802.11s draft spec. + */ +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/errno.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net/bpf.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_action.h> +#include <freebsd/net80211/ieee80211_input.h> +#include <freebsd/net80211/ieee80211_mesh.h> + +static void hwmp_vattach(struct ieee80211vap *); +static void hwmp_vdetach(struct ieee80211vap *); +static int hwmp_newstate(struct ieee80211vap *, + enum ieee80211_state, int); +static int hwmp_send_action(struct ieee80211_node *, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN], + uint8_t *, size_t); +static uint8_t * hwmp_add_meshpreq(uint8_t *, + const struct ieee80211_meshpreq_ie *); +static uint8_t * hwmp_add_meshprep(uint8_t *, + const struct ieee80211_meshprep_ie *); +static uint8_t * hwmp_add_meshperr(uint8_t *, + const struct ieee80211_meshperr_ie *); +static uint8_t * hwmp_add_meshrann(uint8_t *, + const struct ieee80211_meshrann_ie *); +static void hwmp_rootmode_setup(struct ieee80211vap *); +static void hwmp_rootmode_cb(void *); +static void hwmp_rootmode_rann_cb(void *); +static void hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *, + const struct ieee80211_frame *, + const struct ieee80211_meshpreq_ie *); +static int hwmp_send_preq(struct ieee80211_node *, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN], + struct ieee80211_meshpreq_ie *); +static void hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *, + const struct ieee80211_frame *, + const struct ieee80211_meshprep_ie *); +static int hwmp_send_prep(struct ieee80211_node *, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN], + struct ieee80211_meshprep_ie *); +static void hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *, + const struct ieee80211_frame *, + const struct ieee80211_meshperr_ie *); +static int hwmp_send_perr(struct ieee80211_node *, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN], + struct ieee80211_meshperr_ie *); +static void hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *, + const struct ieee80211_frame *, + const struct ieee80211_meshrann_ie *); +static int hwmp_send_rann(struct ieee80211_node *, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN], + struct ieee80211_meshrann_ie *); +static struct ieee80211_node * + hwmp_discover(struct ieee80211vap *, + const uint8_t [IEEE80211_ADDR_LEN], struct mbuf *); +static void hwmp_peerdown(struct ieee80211_node *); + +static struct timeval ieee80211_hwmp_preqminint = { 0, 100000 }; +static struct timeval ieee80211_hwmp_perrminint = { 0, 100000 }; + +/* unalligned little endian access */ +#define LE_WRITE_2(p, v) do { \ + ((uint8_t *)(p))[0] = (v) & 0xff; \ + ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ +} while (0) +#define LE_WRITE_4(p, v) do { \ + ((uint8_t *)(p))[0] = (v) & 0xff; \ + ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ + ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff; \ + ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff; \ +} while (0) + + +/* NB: the Target Address set in a Proactive PREQ is the broadcast address. */ +static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +typedef uint32_t ieee80211_hwmp_seq; +#define HWMP_SEQ_LT(a, b) ((int32_t)((a)-(b)) < 0) +#define HWMP_SEQ_LEQ(a, b) ((int32_t)((a)-(b)) <= 0) +#define HWMP_SEQ_GT(a, b) ((int32_t)((a)-(b)) > 0) +#define HWMP_SEQ_GEQ(a, b) ((int32_t)((a)-(b)) >= 0) + +/* + * Private extension of ieee80211_mesh_route. + */ +struct ieee80211_hwmp_route { + ieee80211_hwmp_seq hr_seq; /* last HWMP seq seen from dst*/ + ieee80211_hwmp_seq hr_preqid; /* last PREQ ID seen from dst */ + ieee80211_hwmp_seq hr_origseq; /* seq. no. on our latest PREQ*/ + int hr_preqretries; +}; +struct ieee80211_hwmp_state { + ieee80211_hwmp_seq hs_seq; /* next seq to be used */ + ieee80211_hwmp_seq hs_preqid; /* next PREQ ID to be used */ + struct timeval hs_lastpreq; /* last time we sent a PREQ */ + struct timeval hs_lastperr; /* last time we sent a PERR */ + int hs_rootmode; /* proactive HWMP */ + struct callout hs_roottimer; + uint8_t hs_maxhops; /* max hop count */ +}; + +SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0, + "IEEE 802.11s HWMP parameters"); +static int ieee80211_hwmp_targetonly = 0; +SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs"); +static int ieee80211_hwmp_replyforward = 1; +SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs"); +static int ieee80211_hwmp_pathtimeout = -1; +SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, pathlifetime, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_hwmp_pathtimeout, 0, ieee80211_sysctl_msecs_ticks, "I", + "path entry lifetime (ms)"); +static int ieee80211_hwmp_roottimeout = -1; +SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, roottimeout, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_hwmp_roottimeout, 0, ieee80211_sysctl_msecs_ticks, "I", + "root PREQ timeout (ms)"); +static int ieee80211_hwmp_rootint = -1; +SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootint, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_hwmp_rootint, 0, ieee80211_sysctl_msecs_ticks, "I", + "root interval (ms)"); +static int ieee80211_hwmp_rannint = -1; +SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_hwmp_rannint, 0, ieee80211_sysctl_msecs_ticks, "I", + "root announcement interval (ms)"); + +#define IEEE80211_HWMP_DEFAULT_MAXHOPS 31 + +static ieee80211_recv_action_func hwmp_recv_action_meshpath; + +static struct ieee80211_mesh_proto_path mesh_proto_hwmp = { + .mpp_descr = "HWMP", + .mpp_ie = IEEE80211_MESHCONF_PATH_HWMP, + .mpp_discover = hwmp_discover, + .mpp_peerdown = hwmp_peerdown, + .mpp_vattach = hwmp_vattach, + .mpp_vdetach = hwmp_vdetach, + .mpp_newstate = hwmp_newstate, + .mpp_privlen = sizeof(struct ieee80211_hwmp_route), +}; +SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, inact, CTLTYPE_INT | CTLFLAG_RW, + &mesh_proto_hwmp.mpp_inact, 0, ieee80211_sysctl_msecs_ticks, "I", + "mesh route inactivity timeout (ms)"); + + +static void +ieee80211_hwmp_init(void) +{ + ieee80211_hwmp_pathtimeout = msecs_to_ticks(5*1000); + ieee80211_hwmp_roottimeout = msecs_to_ticks(5*1000); + ieee80211_hwmp_rootint = msecs_to_ticks(2*1000); + ieee80211_hwmp_rannint = msecs_to_ticks(1*1000); + + /* + * Register action frame handler. + */ + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH, + IEEE80211_ACTION_MESHPATH_SEL, hwmp_recv_action_meshpath); + + /* NB: default is 5 secs per spec */ + mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000); + + /* + * Register HWMP. + */ + ieee80211_mesh_register_proto_path(&mesh_proto_hwmp); +} +SYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL); + +void +hwmp_vattach(struct ieee80211vap *vap) +{ + struct ieee80211_hwmp_state *hs; + + KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, + ("not a mesh vap, opmode %d", vap->iv_opmode)); + + hs = malloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP, + M_NOWAIT | M_ZERO); + if (hs == NULL) { + printf("%s: couldn't alloc HWMP state\n", __func__); + return; + } + hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS; + callout_init(&hs->hs_roottimer, CALLOUT_MPSAFE); + vap->iv_hwmp = hs; +} + +void +hwmp_vdetach(struct ieee80211vap *vap) +{ + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + + callout_drain(&hs->hs_roottimer); + free(vap->iv_hwmp, M_80211_VAP); + vap->iv_hwmp = NULL; +} + +int +hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg) +{ + enum ieee80211_state nstate = vap->iv_state; + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", + __func__, ieee80211_state_name[ostate], + ieee80211_state_name[nstate], arg); + + if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) + callout_drain(&hs->hs_roottimer); + if (nstate == IEEE80211_S_RUN) + hwmp_rootmode_setup(vap); + return 0; +} + +static int +hwmp_recv_action_meshpath(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_meshpreq_ie preq; + struct ieee80211_meshprep_ie prep; + struct ieee80211_meshperr_ie perr; + struct ieee80211_meshrann_ie rann; + const uint8_t *iefrm = frm + 2; /* action + code */ + int found = 0; + + while (efrm - iefrm > 1) { + IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0); + switch (*iefrm) { + case IEEE80211_ELEMID_MESHPREQ: + { + const struct ieee80211_meshpreq_ie *mpreq = + (const struct ieee80211_meshpreq_ie *) iefrm; + /* XXX > 1 target */ + if (mpreq->preq_len != + sizeof(struct ieee80211_meshpreq_ie) - 2) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, + wh, NULL, "%s", "PREQ with wrong len"); + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + memcpy(&preq, mpreq, sizeof(preq)); + preq.preq_id = LE_READ_4(&mpreq->preq_id); + preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq); + preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime); + preq.preq_metric = LE_READ_4(&mpreq->preq_metric); + preq.preq_targets[0].target_seq = + LE_READ_4(&mpreq->preq_targets[0].target_seq); + hwmp_recv_preq(vap, ni, wh, &preq); + found++; + break; + } + case IEEE80211_ELEMID_MESHPREP: + { + const struct ieee80211_meshprep_ie *mprep = + (const struct ieee80211_meshprep_ie *) iefrm; + if (mprep->prep_len != + sizeof(struct ieee80211_meshprep_ie) - 2) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, + wh, NULL, "%s", "PREP with wrong len"); + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + memcpy(&prep, mprep, sizeof(prep)); + prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq); + prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime); + prep.prep_metric = LE_READ_4(&mprep->prep_metric); + prep.prep_origseq = LE_READ_4(&mprep->prep_origseq); + hwmp_recv_prep(vap, ni, wh, &prep); + found++; + break; + } + case IEEE80211_ELEMID_MESHPERR: + { + const struct ieee80211_meshperr_ie *mperr = + (const struct ieee80211_meshperr_ie *) iefrm; + /* XXX > 1 target */ + if (mperr->perr_len != + sizeof(struct ieee80211_meshperr_ie) - 2) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, + wh, NULL, "%s", "PERR with wrong len"); + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + memcpy(&perr, mperr, sizeof(perr)); + perr.perr_dests[0].dest_seq = + LE_READ_4(&mperr->perr_dests[0].dest_seq); + hwmp_recv_perr(vap, ni, wh, &perr); + found++; + break; + } + case IEEE80211_ELEMID_MESHRANN: + { + const struct ieee80211_meshrann_ie *mrann = + (const struct ieee80211_meshrann_ie *) iefrm; + if (mrann->rann_len != + sizeof(struct ieee80211_meshrann_ie) - 2) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, + wh, NULL, "%s", "RAN with wrong len"); + vap->iv_stats.is_rx_mgtdiscard++; + return 1; + } + memcpy(&rann, mrann, sizeof(rann)); + rann.rann_seq = LE_READ_4(&mrann->rann_seq); + rann.rann_metric = LE_READ_4(&mrann->rann_metric); + hwmp_recv_rann(vap, ni, wh, &rann); + found++; + break; + } + } + iefrm += iefrm[1] + 2; + } + if (!found) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP, + wh, NULL, "%s", "PATH SEL action without IE"); + vap->iv_stats.is_rx_mgtdiscard++; + } + return 0; +} + +static int +hwmp_send_action(struct ieee80211_node *ni, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + uint8_t *ie, size_t len) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_bpf_params params; + struct mbuf *m; + uint8_t *frm; + + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, + "block %s frame in CAC state", "HWMP action"); + vap->iv_stats.is_tx_badstate++; + return EIO; /* XXX */ + } + + KASSERT(ni != NULL, ("null node")); + /* + * Hold a reference on the node so it doesn't go away until after + * the xmit is complete all the way in the driver. On error we + * will remove our reference. + */ +#ifdef IEEE80211_DEBUG_REFCNT + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", + __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)+1); +#endif + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(struct ieee80211_action) + len + ); + if (m == NULL) { + ieee80211_free_node(ni); + vap->iv_stats.is_tx_nobuf++; + return ENOMEM; + } + *frm++ = IEEE80211_ACTION_CAT_MESHPATH; + *frm++ = IEEE80211_ACTION_MESHPATH_SEL; + switch (*ie) { + case IEEE80211_ELEMID_MESHPREQ: + frm = hwmp_add_meshpreq(frm, + (struct ieee80211_meshpreq_ie *)ie); + break; + case IEEE80211_ELEMID_MESHPREP: + frm = hwmp_add_meshprep(frm, + (struct ieee80211_meshprep_ie *)ie); + break; + case IEEE80211_ELEMID_MESHPERR: + frm = hwmp_add_meshperr(frm, + (struct ieee80211_meshperr_ie *)ie); + break; + case IEEE80211_ELEMID_MESHRANN: + frm = hwmp_add_meshrann(frm, + (struct ieee80211_meshrann_ie *)ie); + break; + } + + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + if (m == NULL) { + ieee80211_free_node(ni); + vap->iv_stats.is_tx_nobuf++; + return ENOMEM; + } + ieee80211_send_setup(ni, m, + IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, + IEEE80211_NONQOS_TID, sa, da, sa); + + m->m_flags |= M_ENCAP; /* mark encapsulated */ + IEEE80211_NODE_STAT(ni, tx_mgmt); + + memset(¶ms, 0, sizeof(params)); + params.ibp_pri = WME_AC_VO; + params.ibp_rate0 = ni->ni_txparms->mgmtrate; + if (IEEE80211_IS_MULTICAST(da)) + params.ibp_try0 = 1; + else + params.ibp_try0 = ni->ni_txparms->maxretry; + params.ibp_power = ni->ni_txpower; + return ic->ic_raw_xmit(ni, m, ¶ms); +} + +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) +#define ADDWORD(frm, v) do { \ + LE_WRITE_4(frm, v); \ + frm += 4; \ +} while (0) +/* + * Add a Mesh Path Request IE to a frame. + */ +static uint8_t * +hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq) +{ + int i; + + *frm++ = IEEE80211_ELEMID_MESHPREQ; + *frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 + + (preq->preq_tcount - 1) * sizeof(*preq->preq_targets); + *frm++ = preq->preq_flags; + *frm++ = preq->preq_hopcount; + *frm++ = preq->preq_ttl; + ADDWORD(frm, preq->preq_id); + IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6; + ADDWORD(frm, preq->preq_origseq); + ADDWORD(frm, preq->preq_lifetime); + ADDWORD(frm, preq->preq_metric); + *frm++ = preq->preq_tcount; + for (i = 0; i < preq->preq_tcount; i++) { + *frm++ = preq->preq_targets[i].target_flags; + IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr); + frm += 6; + ADDWORD(frm, preq->preq_targets[i].target_seq); + } + return frm; +} + +/* + * Add a Mesh Path Reply IE to a frame. + */ +static uint8_t * +hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep) +{ + *frm++ = IEEE80211_ELEMID_MESHPREP; + *frm++ = sizeof(struct ieee80211_meshprep_ie) - 2; + *frm++ = prep->prep_flags; + *frm++ = prep->prep_hopcount; + *frm++ = prep->prep_ttl; + IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6; + ADDWORD(frm, prep->prep_targetseq); + ADDWORD(frm, prep->prep_lifetime); + ADDWORD(frm, prep->prep_metric); + IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6; + ADDWORD(frm, prep->prep_origseq); + return frm; +} + +/* + * Add a Mesh Path Error IE to a frame. + */ +static uint8_t * +hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr) +{ + int i; + + *frm++ = IEEE80211_ELEMID_MESHPERR; + *frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 + + (perr->perr_ndests - 1) * sizeof(*perr->perr_dests); + *frm++ = perr->perr_ttl; + *frm++ = perr->perr_ndests; + for (i = 0; i < perr->perr_ndests; i++) { + *frm++ = perr->perr_dests[i].dest_flags; + IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr); + frm += 6; + ADDWORD(frm, perr->perr_dests[i].dest_seq); + ADDSHORT(frm, perr->perr_dests[i].dest_rcode); + } + return frm; +} + +/* + * Add a Root Annoucement IE to a frame. + */ +static uint8_t * +hwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann) +{ + *frm++ = IEEE80211_ELEMID_MESHRANN; + *frm++ = sizeof(struct ieee80211_meshrann_ie) - 2; + *frm++ = rann->rann_flags; + *frm++ = rann->rann_hopcount; + *frm++ = rann->rann_ttl; + IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6; + ADDWORD(frm, rann->rann_seq); + ADDWORD(frm, rann->rann_metric); + return frm; +} + +static void +hwmp_rootmode_setup(struct ieee80211vap *vap) +{ + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + + switch (hs->hs_rootmode) { + case IEEE80211_HWMP_ROOTMODE_DISABLED: + callout_drain(&hs->hs_roottimer); + break; + case IEEE80211_HWMP_ROOTMODE_NORMAL: + case IEEE80211_HWMP_ROOTMODE_PROACTIVE: + callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint, + hwmp_rootmode_cb, vap); + break; + case IEEE80211_HWMP_ROOTMODE_RANN: + callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint, + hwmp_rootmode_rann_cb, vap); + break; + } +} + +/* + * Send a broadcast Path Request to find all nodes on the mesh. We are + * called when the vap is configured as a HWMP root node. + */ +#define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags +#define PREQ_TADDR(n) preq.preq_targets[n].target_addr +#define PREQ_TSEQ(n) preq.preq_targets[n].target_seq +static void +hwmp_rootmode_cb(void *arg) +{ + struct ieee80211vap *vap = (struct ieee80211vap *)arg; + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_meshpreq_ie preq; + + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, + "%s", "send broadcast PREQ"); + + preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM; + if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) + preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR; + if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE) + preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP; + preq.preq_hopcount = 0; + preq.preq_ttl = ms->ms_ttl; + preq.preq_id = ++hs->hs_preqid; + IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); + preq.preq_origseq = ++hs->hs_seq; + preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout); + preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL; + preq.preq_tcount = 1; + IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr); + PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO | + IEEE80211_MESHPREQ_TFLAGS_RF; + PREQ_TSEQ(0) = 0; + vap->iv_stats.is_hwmp_rootreqs++; + hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq); + hwmp_rootmode_setup(vap); +} +#undef PREQ_TFLAGS +#undef PREQ_TADDR +#undef PREQ_TSEQ + +/* + * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are + * called when the vap is configured as a HWMP RANN root node. + */ +static void +hwmp_rootmode_rann_cb(void *arg) +{ + struct ieee80211vap *vap = (struct ieee80211vap *)arg; + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_meshrann_ie rann; + + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, + "%s", "send broadcast RANN"); + + rann.rann_flags = 0; + if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) + rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR; + rann.rann_hopcount = 0; + rann.rann_ttl = ms->ms_ttl; + IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr); + rann.rann_seq = ++hs->hs_seq; + rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL; + + vap->iv_stats.is_hwmp_rootrann++; + hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann); + hwmp_rootmode_setup(vap); +} + +#define PREQ_TFLAGS(n) preq->preq_targets[n].target_flags +#define PREQ_TADDR(n) preq->preq_targets[n].target_addr +#define PREQ_TSEQ(n) preq->preq_targets[n].target_seq +static void +hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, + const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt = NULL; + struct ieee80211_mesh_route *rtorig = NULL; + struct ieee80211_hwmp_route *hrorig; + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + struct ieee80211_meshprep_ie prep; + + if (ni == vap->iv_bss || + ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) + return; + /* + * Ignore PREQs from us. Could happen because someone forward it + * back to us. + */ + if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr)) + return; + + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "received PREQ, source %s", ether_sprintf(preq->preq_origaddr)); + + /* + * Acceptance criteria: if the PREQ is not for us and + * forwarding is disabled, discard this PREQ. + */ + if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) && + !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, + preq->preq_origaddr, NULL, "%s", "not accepting PREQ"); + return; + } + rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); + if (rtorig == NULL) + rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr); + if (rtorig == NULL) { + /* XXX stat */ + return; + } + hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route); + /* + * Sequence number validation. + */ + if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) && + HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "discard PREQ from %s, old seq no %u <= %u", + ether_sprintf(preq->preq_origaddr), + preq->preq_origseq, hrorig->hr_seq); + return; + } + hrorig->hr_preqid = preq->preq_id; + hrorig->hr_seq = preq->preq_origseq; + + /* + * Check if the PREQ is addressed to us. + */ + if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "reply to %s", ether_sprintf(preq->preq_origaddr)); + /* + * Build and send a PREP frame. + */ + prep.prep_flags = 0; + prep.prep_hopcount = 0; + prep.prep_ttl = ms->ms_ttl; + IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr); + prep.prep_targetseq = ++hs->hs_seq; + prep.prep_lifetime = preq->preq_lifetime; + prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; + IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr); + prep.prep_origseq = preq->preq_origseq; + hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep); + /* + * Build the reverse path, if we don't have it already. + */ + rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); + if (rt == NULL) + hwmp_discover(vap, preq->preq_origaddr, NULL); + else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) + hwmp_discover(vap, rt->rt_dest, NULL); + return; + } + /* + * Proactive PREQ: reply with a proactive PREP to the + * root STA if requested. + */ + if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) && + (PREQ_TFLAGS(0) & + ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) == + (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) { + uint8_t rootmac[IEEE80211_ADDR_LEN]; + + IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr); + rt = ieee80211_mesh_rt_find(vap, rootmac); + if (rt == NULL) { + rt = ieee80211_mesh_rt_add(vap, rootmac); + if (rt == NULL) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "unable to add root mesh path to %s", + ether_sprintf(rootmac)); + vap->iv_stats.is_mesh_rtaddfailed++; + return; + } + } + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "root mesh station @ %s", ether_sprintf(rootmac)); + + /* + * Reply with a PREP if we don't have a path to the root + * or if the root sent us a proactive PREQ. + */ + if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || + (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) { + prep.prep_flags = 0; + prep.prep_hopcount = 0; + prep.prep_ttl = ms->ms_ttl; + IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac); + prep.prep_origseq = preq->preq_origseq; + prep.prep_lifetime = preq->preq_lifetime; + prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; + IEEE80211_ADDR_COPY(prep.prep_targetaddr, + vap->iv_myaddr); + prep.prep_targetseq = ++hs->hs_seq; + hwmp_send_prep(vap->iv_bss, vap->iv_myaddr, + broadcastaddr, &prep); + } + hwmp_discover(vap, rootmac, NULL); + return; + } + rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0)); + + /* + * Forwarding and Intermediate reply for PREQs with 1 target. + */ + if (preq->preq_tcount == 1) { + struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */ + + memcpy(&ppreq, preq, sizeof(ppreq)); + /* + * We have a valid route to this node. + */ + if (rt != NULL && + (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) { + if (preq->preq_ttl > 1 && + preq->preq_hopcount < hs->hs_maxhops) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "forward PREQ from %s", + ether_sprintf(preq->preq_origaddr)); + /* + * Propagate the original PREQ. + */ + ppreq.preq_hopcount += 1; + ppreq.preq_ttl -= 1; + ppreq.preq_metric += + ms->ms_pmetric->mpm_metric(ni); + /* + * Set TO and unset RF bits because we are going + * to send a PREP next. + */ + ppreq.preq_targets[0].target_flags |= + IEEE80211_MESHPREQ_TFLAGS_TO; + ppreq.preq_targets[0].target_flags &= + ~IEEE80211_MESHPREQ_TFLAGS_RF; + hwmp_send_preq(ni, vap->iv_myaddr, + broadcastaddr, &ppreq); + } + /* + * Check if we can send an intermediate Path Reply, + * i.e., Target Only bit is not set. + */ + if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) { + struct ieee80211_meshprep_ie prep; + + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "intermediate reply for PREQ from %s", + ether_sprintf(preq->preq_origaddr)); + prep.prep_flags = 0; + prep.prep_hopcount = rt->rt_nhops + 1; + prep.prep_ttl = ms->ms_ttl; + IEEE80211_ADDR_COPY(&prep.prep_targetaddr, + PREQ_TADDR(0)); + prep.prep_targetseq = hrorig->hr_seq; + prep.prep_lifetime = preq->preq_lifetime; + prep.prep_metric = rt->rt_metric + + ms->ms_pmetric->mpm_metric(ni); + IEEE80211_ADDR_COPY(&prep.prep_origaddr, + preq->preq_origaddr); + prep.prep_origseq = hrorig->hr_seq; + hwmp_send_prep(ni, vap->iv_myaddr, + broadcastaddr, &prep); + } + /* + * We have no information about this path, + * propagate the PREQ. + */ + } else if (preq->preq_ttl > 1 && + preq->preq_hopcount < hs->hs_maxhops) { + if (rt == NULL) { + rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0)); + if (rt == NULL) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, + ni, "unable to add PREQ path to %s", + ether_sprintf(PREQ_TADDR(0))); + vap->iv_stats.is_mesh_rtaddfailed++; + return; + } + } + rt->rt_metric = preq->preq_metric; + rt->rt_lifetime = preq->preq_lifetime; + hrorig = IEEE80211_MESH_ROUTE_PRIV(rt, + struct ieee80211_hwmp_route); + hrorig->hr_seq = preq->preq_origseq; + hrorig->hr_preqid = preq->preq_id; + + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "forward PREQ from %s", + ether_sprintf(preq->preq_origaddr)); + ppreq.preq_hopcount += 1; + ppreq.preq_ttl -= 1; + ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni); + hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr, + &ppreq); + } + } + +} +#undef PREQ_TFLAGS +#undef PREQ_TADDR +#undef PREQ_TSEQ + +static int +hwmp_send_preq(struct ieee80211_node *ni, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + struct ieee80211_meshpreq_ie *preq) +{ + struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; + + /* + * Enforce PREQ interval. + */ + if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0) + return EALREADY; + getmicrouptime(&hs->hs_lastpreq); + + /* + * mesh preq action frame format + * [6] da + * [6] sa + * [6] addr3 = sa + * [1] action + * [1] category + * [tlv] mesh path request + */ + preq->preq_ie = IEEE80211_ELEMID_MESHPREQ; + return hwmp_send_action(ni, sa, da, (uint8_t *)preq, + sizeof(struct ieee80211_meshpreq_ie)); +} + +static void +hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, + const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + struct ieee80211_mesh_route *rt = NULL; + struct ieee80211_hwmp_route *hr; + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = vap->iv_ifp; + struct mbuf *m, *next; + + /* + * Acceptance criteria: if the corresponding PREQ was not generated + * by us and forwarding is disabled, discard this PREP. + */ + if (ni == vap->iv_bss || + ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) + return; + if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && + !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) + return; + + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "received PREP from %s", ether_sprintf(prep->prep_targetaddr)); + + rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr); + if (rt == NULL) { + /* + * If we have no entry this could be a reply to a root PREQ. + */ + if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) { + rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr); + if (rt == NULL) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, + ni, "unable to add PREP path to %s", + ether_sprintf(prep->prep_targetaddr)); + vap->iv_stats.is_mesh_rtaddfailed++; + return; + } + IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); + rt->rt_nhops = prep->prep_hopcount; + rt->rt_lifetime = prep->prep_lifetime; + rt->rt_metric = prep->prep_metric; + rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "add root path to %s nhops %d metric %d (PREP)", + ether_sprintf(prep->prep_targetaddr), + rt->rt_nhops, rt->rt_metric); + return; + } + return; + } + /* + * Sequence number validation. + */ + hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); + if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq)) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "discard PREP from %s, old seq no %u <= %u", + ether_sprintf(prep->prep_targetaddr), + prep->prep_targetseq, hr->hr_seq); + return; + } + hr->hr_seq = prep->prep_targetseq; + /* + * If it's NOT for us, propagate the PREP. + */ + if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) && + prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) { + struct ieee80211_meshprep_ie pprep; /* propagated PREP */ + + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "propagate PREP from %s", + ether_sprintf(prep->prep_targetaddr)); + + memcpy(&pprep, prep, sizeof(pprep)); + pprep.prep_hopcount += 1; + pprep.prep_ttl -= 1; + pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni); + IEEE80211_ADDR_COPY(pprep.prep_targetaddr, vap->iv_myaddr); + hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep); + } + hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); + if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) { + /* NB: never clobber a proxy entry */; + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "discard PREP for %s, route is marked PROXY", + ether_sprintf(prep->prep_targetaddr)); + vap->iv_stats.is_hwmp_proxy++; + } else if (prep->prep_origseq == hr->hr_origseq) { + /* + * Check if we already have a path to this node. + * If we do, check if this path reply contains a + * better route. + */ + if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || + (prep->prep_hopcount < rt->rt_nhops || + prep->prep_metric < rt->rt_metric)) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "%s path to %s, hopcount %d:%d metric %d:%d", + rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ? + "prefer" : "update", + ether_sprintf(prep->prep_origaddr), + rt->rt_nhops, prep->prep_hopcount, + rt->rt_metric, prep->prep_metric); + IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); + rt->rt_nhops = prep->prep_hopcount; + rt->rt_lifetime = prep->prep_lifetime; + rt->rt_metric = prep->prep_metric; + rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; + } else { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "ignore PREP for %s, hopcount %d:%d metric %d:%d", + ether_sprintf(prep->prep_targetaddr), + rt->rt_nhops, prep->prep_hopcount, + rt->rt_metric, prep->prep_metric); + } + } else { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "discard PREP for %s, wrong seqno %u != %u", + ether_sprintf(prep->prep_targetaddr), prep->prep_origseq, + hr->hr_seq); + vap->iv_stats.is_hwmp_wrongseq++; + } + /* + * Check for frames queued awaiting path discovery. + * XXX probably can tell exactly and avoid remove call + * NB: hash may have false matches, if so they will get + * stuck back on the stageq because there won't be + * a path. + */ + m = ieee80211_ageq_remove(&ic->ic_stageq, + (struct ieee80211_node *)(uintptr_t) + ieee80211_mac_hash(ic, rt->rt_dest)); + for (; m != NULL; m = next) { + next = m->m_nextpkt; + m->m_nextpkt = NULL; + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "flush queued frame %p len %d", m, m->m_pkthdr.len); + ifp->if_transmit(ifp, m); + } +} + +static int +hwmp_send_prep(struct ieee80211_node *ni, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + struct ieee80211_meshprep_ie *prep) +{ + /* NB: there's no PREP minimum interval. */ + + /* + * mesh prep action frame format + * [6] da + * [6] sa + * [6] addr3 = sa + * [1] action + * [1] category + * [tlv] mesh path reply + */ + prep->prep_ie = IEEE80211_ELEMID_MESHPREP; + return hwmp_send_action(ni, sa, da, (uint8_t *)prep, + sizeof(struct ieee80211_meshprep_ie)); +} + +#define PERR_DFLAGS(n) perr.perr_dests[n].dest_flags +#define PERR_DADDR(n) perr.perr_dests[n].dest_addr +#define PERR_DSEQ(n) perr.perr_dests[n].dest_seq +#define PERR_DRCODE(n) perr.perr_dests[n].dest_rcode +static void +hwmp_peerdown(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_meshperr_ie perr; + struct ieee80211_mesh_route *rt; + struct ieee80211_hwmp_route *hr; + + rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr); + if (rt == NULL) + return; + hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "%s", "delete route entry"); + perr.perr_ttl = ms->ms_ttl; + perr.perr_ndests = 1; + PERR_DFLAGS(0) = 0; + if (hr->hr_seq == 0) + PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN; + PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC; + IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest); + PERR_DSEQ(0) = hr->hr_seq; + PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH; + /* NB: flush everything passing through peer */ + ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr); + hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr); +} +#undef PERR_DFLAGS +#undef PERR_DADDR +#undef PERR_DSEQ +#undef PERR_DRCODE + +#define PERR_DFLAGS(n) perr->perr_dests[n].dest_flags +#define PERR_DADDR(n) perr->perr_dests[n].dest_addr +#define PERR_DSEQ(n) perr->perr_dests[n].dest_seq +#define PERR_DRCODE(n) perr->perr_dests[n].dest_rcode +static void +hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni, + const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt = NULL; + struct ieee80211_hwmp_route *hr; + struct ieee80211_meshperr_ie pperr; + int i, forward = 0; + + /* + * Acceptance criteria: check if we received a PERR from a + * neighbor and forwarding is enabled. + */ + if (ni == vap->iv_bss || + ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || + !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) + return; + /* + * Find all routing entries that match and delete them. + */ + for (i = 0; i < perr->perr_ndests; i++) { + rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i)); + if (rt == NULL) + continue; + hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); + if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) && + HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) { + ieee80211_mesh_rt_del(vap, rt->rt_dest); + ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest); + rt = NULL; + forward = 1; + } + } + /* + * Propagate the PERR if we previously found it on our routing table. + * XXX handle ndest > 1 + */ + if (forward && perr->perr_ttl > 1) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "propagate PERR from %s", ether_sprintf(wh->i_addr2)); + memcpy(&pperr, perr, sizeof(*perr)); + pperr.perr_ttl--; + hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, + &pperr); + } +} +#undef PEER_DADDR +#undef PERR_DSEQ + +static int +hwmp_send_perr(struct ieee80211_node *ni, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + struct ieee80211_meshperr_ie *perr) +{ + struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; + + /* + * Enforce PERR interval. + */ + if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0) + return EALREADY; + getmicrouptime(&hs->hs_lastperr); + + /* + * mesh perr action frame format + * [6] da + * [6] sa + * [6] addr3 = sa + * [1] action + * [1] category + * [tlv] mesh path error + */ + perr->perr_ie = IEEE80211_ELEMID_MESHPERR; + return hwmp_send_action(ni, sa, da, (uint8_t *)perr, + sizeof(struct ieee80211_meshperr_ie)); +} + +static void +hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni, + const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + struct ieee80211_mesh_route *rt = NULL; + struct ieee80211_hwmp_route *hr; + struct ieee80211_meshrann_ie prann; + + if (ni == vap->iv_bss || + ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || + IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr)) + return; + + rt = ieee80211_mesh_rt_find(vap, rann->rann_addr); + /* + * Discover the path to the root mesh STA. + * If we already know it, propagate the RANN element. + */ + if (rt == NULL) { + hwmp_discover(vap, rann->rann_addr, NULL); + return; + } + hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); + if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) { + hr->hr_seq = rann->rann_seq; + if (rann->rann_ttl > 1 && + rann->rann_hopcount < hs->hs_maxhops && + (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { + memcpy(&prann, rann, sizeof(prann)); + prann.rann_hopcount += 1; + prann.rann_ttl -= 1; + prann.rann_metric += ms->ms_pmetric->mpm_metric(ni); + hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, + broadcastaddr, &prann); + } + } +} + +static int +hwmp_send_rann(struct ieee80211_node *ni, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + struct ieee80211_meshrann_ie *rann) +{ + /* + * mesh rann action frame format + * [6] da + * [6] sa + * [6] addr3 = sa + * [1] action + * [1] category + * [tlv] root annoucement + */ + rann->rann_ie = IEEE80211_ELEMID_MESHRANN; + return hwmp_send_action(ni, sa, da, (uint8_t *)rann, + sizeof(struct ieee80211_meshrann_ie)); +} + +#define PREQ_TFLAGS(n) preq.preq_targets[n].target_flags +#define PREQ_TADDR(n) preq.preq_targets[n].target_addr +#define PREQ_TSEQ(n) preq.preq_targets[n].target_seq +static struct ieee80211_node * +hwmp_discover(struct ieee80211vap *vap, + const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m) +{ + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt = NULL; + struct ieee80211_hwmp_route *hr; + struct ieee80211_meshpreq_ie preq; + struct ieee80211_node *ni; + int sendpreq = 0; + + KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, + ("not a mesh vap, opmode %d", vap->iv_opmode)); + + KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest), + ("%s: discovering self!", __func__)); + + ni = NULL; + if (!IEEE80211_IS_MULTICAST(dest)) { + rt = ieee80211_mesh_rt_find(vap, dest); + if (rt == NULL) { + rt = ieee80211_mesh_rt_add(vap, dest); + if (rt == NULL) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, + ni, "unable to add discovery path to %s", + ether_sprintf(dest)); + vap->iv_stats.is_mesh_rtaddfailed++; + goto done; + } + } + hr = IEEE80211_MESH_ROUTE_PRIV(rt, + struct ieee80211_hwmp_route); + if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { + if (hr->hr_origseq == 0) + hr->hr_origseq = ++hs->hs_seq; + rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL; + rt->rt_lifetime = + ticks_to_msecs(ieee80211_hwmp_pathtimeout); + /* XXX check preq retries */ + sendpreq = 1; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, + "start path discovery (src %s)", + m == NULL ? "<none>" : ether_sprintf( + mtod(m, struct ether_header *)->ether_shost)); + /* + * Try to discover the path for this node. + */ + preq.preq_flags = 0; + preq.preq_hopcount = 0; + preq.preq_ttl = ms->ms_ttl; + preq.preq_id = ++hs->hs_preqid; + IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); + preq.preq_origseq = hr->hr_origseq; + preq.preq_lifetime = rt->rt_lifetime; + preq.preq_metric = rt->rt_metric; + preq.preq_tcount = 1; + IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest); + PREQ_TFLAGS(0) = 0; + if (ieee80211_hwmp_targetonly) + PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO; + if (ieee80211_hwmp_replyforward) + PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF; + PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN; + PREQ_TSEQ(0) = 0; + /* XXX check return value */ + hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, + broadcastaddr, &preq); + } + if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) + ni = ieee80211_find_txnode(vap, rt->rt_nexthop); + } else { + ni = ieee80211_find_txnode(vap, dest); + /* NB: if null then we leak mbuf */ + KASSERT(ni != NULL, ("leak mcast frame")); + return ni; + } +done: + if (ni == NULL && m != NULL) { + if (sendpreq) { + struct ieee80211com *ic = vap->iv_ic; + /* + * Queue packet for transmit when path discovery + * completes. If discovery never completes the + * frame will be flushed by way of the aging timer. + */ + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, + "%s", "queue frame until path found"); + m->m_pkthdr.rcvif = (void *)(uintptr_t) + ieee80211_mac_hash(ic, dest); + /* XXX age chosen randomly */ + ieee80211_ageq_append(&ic->ic_stageq, m, + IEEE80211_INACT_WAIT); + } else { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, + dest, NULL, "%s", "no valid path to this node"); + m_freem(m); + } + } + return ni; +} +#undef PREQ_TFLAGS +#undef PREQ_TADDR +#undef PREQ_TSEQ + +static int +hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + int error; + + if (vap->iv_opmode != IEEE80211_M_MBSS) + return ENOSYS; + error = 0; + switch (ireq->i_type) { + case IEEE80211_IOC_HWMP_ROOTMODE: + ireq->i_val = hs->hs_rootmode; + break; + case IEEE80211_IOC_HWMP_MAXHOPS: + ireq->i_val = hs->hs_maxhops; + break; + default: + return ENOSYS; + } + return error; +} +IEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211); + +static int +hwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_hwmp_state *hs = vap->iv_hwmp; + int error; + + if (vap->iv_opmode != IEEE80211_M_MBSS) + return ENOSYS; + error = 0; + switch (ireq->i_type) { + case IEEE80211_IOC_HWMP_ROOTMODE: + if (ireq->i_val < 0 || ireq->i_val > 3) + return EINVAL; + hs->hs_rootmode = ireq->i_val; + hwmp_rootmode_setup(vap); + break; + case IEEE80211_IOC_HWMP_MAXHOPS: + if (ireq->i_val <= 0 || ireq->i_val > 255) + return EINVAL; + hs->hs_maxhops = ireq->i_val; + break; + default: + return ENOSYS; + } + return error; +} +IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211); diff --git a/freebsd/sys/net80211/ieee80211_input.c b/freebsd/sys/net80211/ieee80211_input.c new file mode 100644 index 00000000..60803d2f --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_input.c @@ -0,0 +1,852 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/ethernet.h> +#include <freebsd/net/if.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_vlan_var.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_input.h> +#ifdef IEEE80211_SUPPORT_MESH +#include <freebsd/net80211/ieee80211_mesh.h> +#endif + +#include <freebsd/net/bpf.h> + +#ifdef INET +#include <freebsd/netinet/in.h> +#include <freebsd/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(const 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 <freebsd/machine/stdarg.h> + +void +ieee80211_note(const 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(const 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(const 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(const 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(const 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(const 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 */ diff --git a/freebsd/sys/net80211/ieee80211_input.h b/freebsd/sys/net80211/ieee80211_input.h new file mode 100644 index 00000000..778badb6 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_input.h @@ -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 _NET80211_IEEE80211_INPUT_HH_ +#define _NET80211_IEEE80211_INPUT_HH_ + +/* 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 /* _NET80211_IEEE80211_INPUT_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_ioctl.c b/freebsd/sys/net80211/ieee80211_ioctl.c new file mode 100644 index 00000000..87d1acc5 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_ioctl.c @@ -0,0 +1,3349 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 ioctl support (FreeBSD-specific) + */ + +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_ipx.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/endian.h> +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/priv.h> +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> +#include <freebsd/sys/systm.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_dl.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#ifdef INET +#include <freebsd/netinet/in.h> +#include <freebsd/netinet/if_ether.h> +#endif + +#ifdef IPX +#include <freebsd/netipx/ipx.h> +#include <freebsd/netipx/ipx_if.h> +#endif + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_ioctl.h> +#include <freebsd/net80211/ieee80211_regdomain.h> +#include <freebsd/net80211/ieee80211_input.h> + +#define IS_UP_AUTO(_vap) \ + (IFNET_IS_UP_RUNNING((_vap)->iv_ifp) && \ + (_vap)->iv_roaming == IEEE80211_ROAMING_AUTO) + +static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; +static struct ieee80211_channel *findchannel(struct ieee80211com *, + int ieee, int mode); + +static __noinline int +ieee80211_ioctl_getkey(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + struct ieee80211req_key ik; + struct ieee80211_key *wk; + const struct ieee80211_cipher *cip; + u_int kid; + int error; + + if (ireq->i_len != sizeof(ik)) + return EINVAL; + error = copyin(ireq->i_data, &ik, sizeof(ik)); + if (error) + return error; + kid = ik.ik_keyix; + if (kid == IEEE80211_KEYIX_NONE) { + ni = ieee80211_find_vap_node(&ic->ic_sta, vap, ik.ik_macaddr); + if (ni == NULL) + return ENOENT; + wk = &ni->ni_ucastkey; + } else { + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + wk = &vap->iv_nw_keys[kid]; + IEEE80211_ADDR_COPY(&ik.ik_macaddr, vap->iv_bss->ni_macaddr); + ni = NULL; + } + cip = wk->wk_cipher; + ik.ik_type = cip->ic_cipher; + ik.ik_keylen = wk->wk_keylen; + ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV); + if (wk->wk_keyix == vap->iv_def_txkey) + ik.ik_flags |= IEEE80211_KEY_DEFAULT; + if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) { + /* NB: only root can read key data */ + ik.ik_keyrsc = wk->wk_keyrsc[IEEE80211_NONQOS_TID]; + ik.ik_keytsc = wk->wk_keytsc; + memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); + if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { + memcpy(ik.ik_keydata+wk->wk_keylen, + wk->wk_key + IEEE80211_KEYBUF_SIZE, + IEEE80211_MICBUF_SIZE); + ik.ik_keylen += IEEE80211_MICBUF_SIZE; + } + } else { + ik.ik_keyrsc = 0; + ik.ik_keytsc = 0; + memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata)); + } + if (ni != NULL) + ieee80211_free_node(ni); + return copyout(&ik, ireq->i_data, sizeof(ik)); +} + +static __noinline int +ieee80211_ioctl_getchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + + if (sizeof(ic->ic_chan_active) < ireq->i_len) + ireq->i_len = sizeof(ic->ic_chan_active); + return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len); +} + +static __noinline int +ieee80211_ioctl_getchaninfo(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + int space; + + space = __offsetof(struct ieee80211req_chaninfo, + ic_chans[ic->ic_nchans]); + if (space > ireq->i_len) + space = ireq->i_len; + /* XXX assumes compatible layout */ + return copyout(&ic->ic_nchans, ireq->i_data, space); +} + +static __noinline int +ieee80211_ioctl_getwpaie(struct ieee80211vap *vap, + struct ieee80211req *ireq, int req) +{ + struct ieee80211_node *ni; + struct ieee80211req_wpaie2 wpaie; + int error; + + if (ireq->i_len < IEEE80211_ADDR_LEN) + return EINVAL; + error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN); + if (error != 0) + return error; + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, wpaie.wpa_macaddr); + if (ni == NULL) + return ENOENT; + memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); + if (ni->ni_ies.wpa_ie != NULL) { + int ielen = ni->ni_ies.wpa_ie[1] + 2; + if (ielen > sizeof(wpaie.wpa_ie)) + ielen = sizeof(wpaie.wpa_ie); + memcpy(wpaie.wpa_ie, ni->ni_ies.wpa_ie, ielen); + } + if (req == IEEE80211_IOC_WPAIE2) { + memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie)); + if (ni->ni_ies.rsn_ie != NULL) { + int ielen = ni->ni_ies.rsn_ie[1] + 2; + if (ielen > sizeof(wpaie.rsn_ie)) + ielen = sizeof(wpaie.rsn_ie); + memcpy(wpaie.rsn_ie, ni->ni_ies.rsn_ie, ielen); + } + if (ireq->i_len > sizeof(struct ieee80211req_wpaie2)) + ireq->i_len = sizeof(struct ieee80211req_wpaie2); + } else { + /* compatibility op, may overwrite wpa ie */ + /* XXX check ic_flags? */ + if (ni->ni_ies.rsn_ie != NULL) { + int ielen = ni->ni_ies.rsn_ie[1] + 2; + if (ielen > sizeof(wpaie.wpa_ie)) + ielen = sizeof(wpaie.wpa_ie); + memcpy(wpaie.wpa_ie, ni->ni_ies.rsn_ie, ielen); + } + if (ireq->i_len > sizeof(struct ieee80211req_wpaie)) + ireq->i_len = sizeof(struct ieee80211req_wpaie); + } + ieee80211_free_node(ni); + return copyout(&wpaie, ireq->i_data, ireq->i_len); +} + +static __noinline int +ieee80211_ioctl_getstastats(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); + int error; + + if (ireq->i_len < off) + return EINVAL; + error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); + if (error != 0) + return error; + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr); + if (ni == NULL) + return ENOENT; + if (ireq->i_len > sizeof(struct ieee80211req_sta_stats)) + ireq->i_len = sizeof(struct ieee80211req_sta_stats); + /* NB: copy out only the statistics */ + error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off, + ireq->i_len - off); + ieee80211_free_node(ni); + return error; +} + +struct scanreq { + struct ieee80211req_scan_result *sr; + size_t space; +}; + +static size_t +scan_space(const struct ieee80211_scan_entry *se, int *ielen) +{ + size_t len; + + *ielen = se->se_ies.len; + /* + * NB: ie's can be no more than 255 bytes and the max 802.11 + * packet is <3Kbytes so we are sure this doesn't overflow + * 16-bits; if this is a concern we can drop the ie's. + */ + len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] + + se->se_meshid[1] + *ielen; + return roundup(len, sizeof(uint32_t)); +} + +static void +get_scan_space(void *arg, const struct ieee80211_scan_entry *se) +{ + struct scanreq *req = arg; + int ielen; + + req->space += scan_space(se, &ielen); +} + +static __noinline void +get_scan_result(void *arg, const struct ieee80211_scan_entry *se) +{ + struct scanreq *req = arg; + struct ieee80211req_scan_result *sr; + int ielen, len, nr, nxr; + uint8_t *cp; + + len = scan_space(se, &ielen); + if (len > req->space) + return; + + sr = req->sr; + KASSERT(len <= 65535 && ielen <= 65535, + ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen)); + sr->isr_len = len; + sr->isr_ie_off = sizeof(struct ieee80211req_scan_result); + sr->isr_ie_len = ielen; + sr->isr_freq = se->se_chan->ic_freq; + sr->isr_flags = se->se_chan->ic_flags; + sr->isr_rssi = se->se_rssi; + sr->isr_noise = se->se_noise; + sr->isr_intval = se->se_intval; + sr->isr_capinfo = se->se_capinfo; + sr->isr_erp = se->se_erp; + IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid); + nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE); + memcpy(sr->isr_rates, se->se_rates+2, nr); + nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr); + memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr); + sr->isr_nrates = nr + nxr; + + /* copy SSID */ + sr->isr_ssid_len = se->se_ssid[1]; + cp = ((uint8_t *)sr) + sr->isr_ie_off; + memcpy(cp, se->se_ssid+2, sr->isr_ssid_len); + + /* copy mesh id */ + cp += sr->isr_ssid_len; + sr->isr_meshid_len = se->se_meshid[1]; + memcpy(cp, se->se_meshid+2, sr->isr_meshid_len); + cp += sr->isr_meshid_len; + + if (ielen) + memcpy(cp, se->se_ies.data, ielen); + + req->space -= len; + req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len); +} + +static __noinline int +ieee80211_ioctl_getscanresults(struct ieee80211vap *vap, + struct ieee80211req *ireq) +{ + struct scanreq req; + int error; + + if (ireq->i_len < sizeof(struct scanreq)) + return EFAULT; + + error = 0; + req.space = 0; + ieee80211_scan_iterate(vap, get_scan_space, &req); + if (req.space > ireq->i_len) + req.space = ireq->i_len; + if (req.space > 0) { + size_t space; + void *p; + + space = req.space; + /* XXX M_WAITOK after driver lock released */ + p = malloc(space, M_TEMP, M_NOWAIT | M_ZERO); + if (p == NULL) + return ENOMEM; + req.sr = p; + ieee80211_scan_iterate(vap, get_scan_result, &req); + ireq->i_len = space - req.space; + error = copyout(p, ireq->i_data, ireq->i_len); + free(p, M_TEMP); + } else + ireq->i_len = 0; + + return error; +} + +struct stainforeq { + struct ieee80211vap *vap; + struct ieee80211req_sta_info *si; + size_t space; +}; + +static size_t +sta_space(const struct ieee80211_node *ni, size_t *ielen) +{ + *ielen = ni->ni_ies.len; + return roundup(sizeof(struct ieee80211req_sta_info) + *ielen, + sizeof(uint32_t)); +} + +static void +get_sta_space(void *arg, struct ieee80211_node *ni) +{ + struct stainforeq *req = arg; + size_t ielen; + + if (req->vap != ni->ni_vap) + return; + if (ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP && + ni->ni_associd == 0) /* only associated stations */ + return; + req->space += sta_space(ni, &ielen); +} + +static __noinline void +get_sta_info(void *arg, struct ieee80211_node *ni) +{ + struct stainforeq *req = arg; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211req_sta_info *si; + size_t ielen, len; + uint8_t *cp; + + if (req->vap != ni->ni_vap) + return; + if (vap->iv_opmode == IEEE80211_M_HOSTAP && + ni->ni_associd == 0) /* only associated stations */ + return; + if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */ + return; + len = sta_space(ni, &ielen); + if (len > req->space) + return; + si = req->si; + si->isi_len = len; + si->isi_ie_off = sizeof(struct ieee80211req_sta_info); + si->isi_ie_len = ielen; + si->isi_freq = ni->ni_chan->ic_freq; + si->isi_flags = ni->ni_chan->ic_flags; + si->isi_state = ni->ni_flags; + si->isi_authmode = ni->ni_authmode; + vap->iv_ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise); + vap->iv_ic->ic_node_getmimoinfo(ni, &si->isi_mimo); + si->isi_capinfo = ni->ni_capinfo; + si->isi_erp = ni->ni_erp; + IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); + si->isi_nrates = ni->ni_rates.rs_nrates; + if (si->isi_nrates > 15) + si->isi_nrates = 15; + memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); + si->isi_txrate = ni->ni_txrate; + if (si->isi_txrate & IEEE80211_RATE_MCS) { + const struct ieee80211_mcs_rates *mcs = + &ieee80211_htrates[ni->ni_txrate &~ IEEE80211_RATE_MCS]; + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { + if (ni->ni_flags & IEEE80211_NODE_SGI40) + si->isi_txmbps = mcs->ht40_rate_800ns; + else + si->isi_txmbps = mcs->ht40_rate_400ns; + } else { + if (ni->ni_flags & IEEE80211_NODE_SGI20) + si->isi_txmbps = mcs->ht20_rate_800ns; + else + si->isi_txmbps = mcs->ht20_rate_400ns; + } + } else + si->isi_txmbps = si->isi_txrate; + si->isi_associd = ni->ni_associd; + si->isi_txpower = ni->ni_txpower; + si->isi_vlan = ni->ni_vlan; + if (ni->ni_flags & IEEE80211_NODE_QOS) { + memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); + memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); + } else { + si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID]; + si->isi_rxseqs[0] = ni->ni_rxseqs[IEEE80211_NONQOS_TID]; + } + /* NB: leave all cases in case we relax ni_associd == 0 check */ + if (ieee80211_node_is_authorized(ni)) + si->isi_inact = vap->iv_inact_run; + else if (ni->ni_associd != 0 || + (vap->iv_opmode == IEEE80211_M_WDS && + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))) + si->isi_inact = vap->iv_inact_auth; + else + si->isi_inact = vap->iv_inact_init; + si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; + si->isi_localid = ni->ni_mllid; + si->isi_peerid = ni->ni_mlpid; + si->isi_peerstate = ni->ni_mlstate; + + if (ielen) { + cp = ((uint8_t *)si) + si->isi_ie_off; + memcpy(cp, ni->ni_ies.data, ielen); + } + + req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len); + req->space -= len; +} + +static __noinline int +getstainfo_common(struct ieee80211vap *vap, struct ieee80211req *ireq, + struct ieee80211_node *ni, int off) +{ + struct ieee80211com *ic = vap->iv_ic; + struct stainforeq req; + size_t space; + void *p; + int error; + + error = 0; + req.space = 0; + req.vap = vap; + if (ni == NULL) + ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req); + else + get_sta_space(&req, ni); + if (req.space > ireq->i_len) + req.space = ireq->i_len; + if (req.space > 0) { + space = req.space; + /* XXX M_WAITOK after driver lock released */ + p = malloc(space, M_TEMP, M_NOWAIT | M_ZERO); + if (p == NULL) { + error = ENOMEM; + goto bad; + } + req.si = p; + if (ni == NULL) + ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req); + else + get_sta_info(&req, ni); + ireq->i_len = space - req.space; + error = copyout(p, (uint8_t *) ireq->i_data+off, ireq->i_len); + free(p, M_TEMP); + } else + ireq->i_len = 0; +bad: + if (ni != NULL) + ieee80211_free_node(ni); + return error; +} + +static __noinline int +ieee80211_ioctl_getstainfo(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + uint8_t macaddr[IEEE80211_ADDR_LEN]; + const int off = __offsetof(struct ieee80211req_sta_req, info); + struct ieee80211_node *ni; + int error; + + if (ireq->i_len < sizeof(struct ieee80211req_sta_req)) + return EFAULT; + error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); + if (error != 0) + return error; + if (IEEE80211_ADDR_EQ(macaddr, vap->iv_ifp->if_broadcastaddr)) { + ni = NULL; + } else { + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr); + if (ni == NULL) + return ENOENT; + } + return getstainfo_common(vap, ireq, ni, off); +} + +static __noinline int +ieee80211_ioctl_getstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_sta_txpow txpow; + int error; + + if (ireq->i_len != sizeof(txpow)) + return EINVAL; + error = copyin(ireq->i_data, &txpow, sizeof(txpow)); + if (error != 0) + return error; + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr); + if (ni == NULL) + return ENOENT; + txpow.it_txpow = ni->ni_txpower; + error = copyout(&txpow, ireq->i_data, sizeof(txpow)); + ieee80211_free_node(ni); + return error; +} + +static __noinline int +ieee80211_ioctl_getwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_wme_state *wme = &ic->ic_wme; + struct wmeParams *wmep; + int ac; + + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return EINVAL; + + ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); + if (ac >= WME_NUM_AC) + ac = WME_AC_BE; + if (ireq->i_len & IEEE80211_WMEPARAM_BSS) + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; + else + wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; + switch (ireq->i_type) { + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + ireq->i_val = wmep->wmep_logcwmin; + break; + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + ireq->i_val = wmep->wmep_logcwmax; + break; + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + ireq->i_val = wmep->wmep_aifsn; + break; + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + ireq->i_val = wmep->wmep_txopLimit; + break; + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; + ireq->i_val = wmep->wmep_acm; + break; + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ + wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; + ireq->i_val = !wmep->wmep_noackPolicy; + break; + } + return 0; +} + +static __noinline int +ieee80211_ioctl_getmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + const struct ieee80211_aclator *acl = vap->iv_acl; + + return (acl == NULL ? EINVAL : acl->iac_getioctl(vap, ireq)); +} + +static __noinline int +ieee80211_ioctl_getcurchan(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel *c; + + if (ireq->i_len != sizeof(struct ieee80211_channel)) + return EINVAL; + /* + * vap's may have different operating channels when HT is + * in use. When in RUN state report the vap-specific channel. + * Otherwise return curchan. + */ + if (vap->iv_state == IEEE80211_S_RUN) + c = vap->iv_bss->ni_chan; + else + c = ic->ic_curchan; + return copyout(c, ireq->i_data, sizeof(*c)); +} + +static int +getappie(const struct ieee80211_appie *aie, struct ieee80211req *ireq) +{ + if (aie == NULL) + return EINVAL; + /* NB: truncate, caller can check length */ + if (ireq->i_len > aie->ie_len) + ireq->i_len = aie->ie_len; + return copyout(aie->ie_data, ireq->i_data, ireq->i_len); +} + +static int +ieee80211_ioctl_getappie(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + uint8_t fc0; + + fc0 = ireq->i_val & 0xff; + if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) + return EINVAL; + /* NB: could check iv_opmode and reject but hardly worth the effort */ + switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) { + case IEEE80211_FC0_SUBTYPE_BEACON: + return getappie(vap->iv_appie_beacon, ireq); + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + return getappie(vap->iv_appie_proberesp, ireq); + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + return getappie(vap->iv_appie_assocresp, ireq); + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + return getappie(vap->iv_appie_probereq, ireq); + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + return getappie(vap->iv_appie_assocreq, ireq); + case IEEE80211_FC0_SUBTYPE_BEACON|IEEE80211_FC0_SUBTYPE_PROBE_RESP: + return getappie(vap->iv_appie_wpa, ireq); + } + return EINVAL; +} + +static __noinline int +ieee80211_ioctl_getregdomain(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + + if (ireq->i_len != sizeof(ic->ic_regdomain)) + return EINVAL; + return copyout(&ic->ic_regdomain, ireq->i_data, + sizeof(ic->ic_regdomain)); +} + +static __noinline int +ieee80211_ioctl_getroam(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + size_t len = ireq->i_len; + /* NB: accept short requests for backwards compat */ + if (len > sizeof(vap->iv_roamparms)) + len = sizeof(vap->iv_roamparms); + return copyout(vap->iv_roamparms, ireq->i_data, len); +} + +static __noinline int +ieee80211_ioctl_gettxparams(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + size_t len = ireq->i_len; + /* NB: accept short requests for backwards compat */ + if (len > sizeof(vap->iv_txparms)) + len = sizeof(vap->iv_txparms); + return copyout(vap->iv_txparms, ireq->i_data, len); +} + +static __noinline int +ieee80211_ioctl_getdevcaps(struct ieee80211com *ic, + const struct ieee80211req *ireq) +{ + struct ieee80211_devcaps_req *dc; + struct ieee80211req_chaninfo *ci; + int maxchans, error; + + maxchans = 1 + ((ireq->i_len - sizeof(struct ieee80211_devcaps_req)) / + sizeof(struct ieee80211_channel)); + /* NB: require 1 so we know ic_nchans is accessible */ + if (maxchans < 1) + return EINVAL; + /* constrain max request size, 2K channels is ~24Kbytes */ + if (maxchans > 2048) + maxchans = 2048; + dc = (struct ieee80211_devcaps_req *) + malloc(IEEE80211_DEVCAPS_SIZE(maxchans), M_TEMP, M_NOWAIT | M_ZERO); + if (dc == NULL) + return ENOMEM; + dc->dc_drivercaps = ic->ic_caps; + dc->dc_cryptocaps = ic->ic_cryptocaps; + dc->dc_htcaps = ic->ic_htcaps; + ci = &dc->dc_chaninfo; + ic->ic_getradiocaps(ic, maxchans, &ci->ic_nchans, ci->ic_chans); + KASSERT(ci->ic_nchans <= maxchans, + ("nchans %d maxchans %d", ci->ic_nchans, maxchans)); + ieee80211_sort_channels(ci->ic_chans, ci->ic_nchans); + error = copyout(dc, ireq->i_data, IEEE80211_DEVCAPS_SPACE(dc)); + free(dc, M_TEMP); + return error; +} + +static __noinline int +ieee80211_ioctl_getstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_sta_vlan vlan; + int error; + + if (ireq->i_len != sizeof(vlan)) + return EINVAL; + error = copyin(ireq->i_data, &vlan, sizeof(vlan)); + if (error != 0) + return error; + if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) { + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, + vlan.sv_macaddr); + if (ni == NULL) + return ENOENT; + } else + ni = ieee80211_ref_node(vap->iv_bss); + vlan.sv_vlan = ni->ni_vlan; + error = copyout(&vlan, ireq->i_data, sizeof(vlan)); + ieee80211_free_node(ni); + return error; +} + +/* + * Dummy ioctl get handler so the linker set is defined. + */ +static int +dummy_ioctl_get(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + return ENOSYS; +} +IEEE80211_IOCTL_GET(dummy, dummy_ioctl_get); + +static int +ieee80211_ioctl_getdefault(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + ieee80211_ioctl_getfunc * const *get; + int error; + + SET_FOREACH(get, ieee80211_ioctl_getset) { + error = (*get)(vap, ireq); + if (error != ENOSYS) + return error; + } + return EINVAL; +} + +/* + * When building the kernel with -O2 on the i386 architecture, gcc + * seems to want to inline this function into ieee80211_ioctl() + * (which is the only routine that calls it). When this happens, + * ieee80211_ioctl() ends up consuming an additional 2K of stack + * space. (Exactly why it needs so much is unclear.) The problem + * is that it's possible for ieee80211_ioctl() to invoke other + * routines (including driver init functions) which could then find + * themselves perilously close to exhausting the stack. + * + * To avoid this, we deliberately prevent gcc from inlining this + * routine. Another way to avoid this is to use less agressive + * optimization when compiling this file (i.e. -O instead of -O2) + * but special-casing the compilation of this one module in the + * build system would be awkward. + */ +static __noinline int +ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, + struct ieee80211req *ireq) +{ +#define MS(_v, _f) (((_v) & _f) >> _f##_S) + struct ieee80211com *ic = vap->iv_ic; + u_int kid, len; + uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; + char tmpssid[IEEE80211_NWID_LEN]; + int error = 0; + + switch (ireq->i_type) { + case IEEE80211_IOC_SSID: + switch (vap->iv_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + ireq->i_len = vap->iv_des_ssid[0].len; + memcpy(tmpssid, vap->iv_des_ssid[0].ssid, ireq->i_len); + break; + default: + ireq->i_len = vap->iv_bss->ni_esslen; + memcpy(tmpssid, vap->iv_bss->ni_essid, ireq->i_len); + break; + } + error = copyout(tmpssid, ireq->i_data, ireq->i_len); + break; + case IEEE80211_IOC_NUMSSIDS: + ireq->i_val = 1; + break; + case IEEE80211_IOC_WEP: + if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) + ireq->i_val = IEEE80211_WEP_OFF; + else if (vap->iv_flags & IEEE80211_F_DROPUNENC) + ireq->i_val = IEEE80211_WEP_ON; + else + ireq->i_val = IEEE80211_WEP_MIXED; + break; + case IEEE80211_IOC_WEPKEY: + kid = (u_int) ireq->i_val; + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + len = (u_int) vap->iv_nw_keys[kid].wk_keylen; + /* NB: only root can read WEP keys */ + if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) { + bcopy(vap->iv_nw_keys[kid].wk_key, tmpkey, len); + } else { + bzero(tmpkey, len); + } + ireq->i_len = len; + error = copyout(tmpkey, ireq->i_data, len); + break; + case IEEE80211_IOC_NUMWEPKEYS: + ireq->i_val = IEEE80211_WEP_NKID; + break; + case IEEE80211_IOC_WEPTXKEY: + ireq->i_val = vap->iv_def_txkey; + break; + case IEEE80211_IOC_AUTHMODE: + if (vap->iv_flags & IEEE80211_F_WPA) + ireq->i_val = IEEE80211_AUTH_WPA; + else + ireq->i_val = vap->iv_bss->ni_authmode; + break; + case IEEE80211_IOC_CHANNEL: + ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan); + break; + case IEEE80211_IOC_POWERSAVE: + if (vap->iv_flags & IEEE80211_F_PMGTON) + ireq->i_val = IEEE80211_POWERSAVE_ON; + else + ireq->i_val = IEEE80211_POWERSAVE_OFF; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + ireq->i_val = ic->ic_lintval; + break; + case IEEE80211_IOC_RTSTHRESHOLD: + ireq->i_val = vap->iv_rtsthreshold; + break; + case IEEE80211_IOC_PROTMODE: + ireq->i_val = ic->ic_protmode; + break; + case IEEE80211_IOC_TXPOWER: + /* + * Tx power limit is the min of max regulatory + * power, any user-set limit, and the max the + * radio can do. + */ + ireq->i_val = 2*ic->ic_curchan->ic_maxregpower; + if (ireq->i_val > ic->ic_txpowlimit) + ireq->i_val = ic->ic_txpowlimit; + if (ireq->i_val > ic->ic_curchan->ic_maxpower) + ireq->i_val = ic->ic_curchan->ic_maxpower; + break; + case IEEE80211_IOC_WPA: + switch (vap->iv_flags & IEEE80211_F_WPA) { + case IEEE80211_F_WPA1: + ireq->i_val = 1; + break; + case IEEE80211_F_WPA2: + ireq->i_val = 2; + break; + case IEEE80211_F_WPA1 | IEEE80211_F_WPA2: + ireq->i_val = 3; + break; + default: + ireq->i_val = 0; + break; + } + break; + case IEEE80211_IOC_CHANLIST: + error = ieee80211_ioctl_getchanlist(vap, ireq); + break; + case IEEE80211_IOC_ROAMING: + ireq->i_val = vap->iv_roaming; + break; + case IEEE80211_IOC_PRIVACY: + ireq->i_val = (vap->iv_flags & IEEE80211_F_PRIVACY) != 0; + break; + case IEEE80211_IOC_DROPUNENCRYPTED: + ireq->i_val = (vap->iv_flags & IEEE80211_F_DROPUNENC) != 0; + break; + case IEEE80211_IOC_COUNTERMEASURES: + ireq->i_val = (vap->iv_flags & IEEE80211_F_COUNTERM) != 0; + break; + case IEEE80211_IOC_WME: + ireq->i_val = (vap->iv_flags & IEEE80211_F_WME) != 0; + break; + case IEEE80211_IOC_HIDESSID: + ireq->i_val = (vap->iv_flags & IEEE80211_F_HIDESSID) != 0; + break; + case IEEE80211_IOC_APBRIDGE: + ireq->i_val = (vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0; + break; + case IEEE80211_IOC_WPAKEY: + error = ieee80211_ioctl_getkey(vap, ireq); + break; + case IEEE80211_IOC_CHANINFO: + error = ieee80211_ioctl_getchaninfo(vap, ireq); + break; + case IEEE80211_IOC_BSSID: + if (ireq->i_len != IEEE80211_ADDR_LEN) + return EINVAL; + if (vap->iv_state == IEEE80211_S_RUN) { + error = copyout(vap->iv_opmode == IEEE80211_M_WDS ? + vap->iv_bss->ni_macaddr : vap->iv_bss->ni_bssid, + ireq->i_data, ireq->i_len); + } else + error = copyout(vap->iv_des_bssid, ireq->i_data, + ireq->i_len); + break; + case IEEE80211_IOC_WPAIE: + error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type); + break; + case IEEE80211_IOC_WPAIE2: + error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type); + break; + case IEEE80211_IOC_SCAN_RESULTS: + error = ieee80211_ioctl_getscanresults(vap, ireq); + break; + case IEEE80211_IOC_STA_STATS: + error = ieee80211_ioctl_getstastats(vap, ireq); + break; + case IEEE80211_IOC_TXPOWMAX: + ireq->i_val = vap->iv_bss->ni_txpower; + break; + case IEEE80211_IOC_STA_TXPOW: + error = ieee80211_ioctl_getstatxpow(vap, ireq); + break; + case IEEE80211_IOC_STA_INFO: + error = ieee80211_ioctl_getstainfo(vap, ireq); + break; + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ + error = ieee80211_ioctl_getwmeparam(vap, ireq); + break; + case IEEE80211_IOC_DTIM_PERIOD: + ireq->i_val = vap->iv_dtim_period; + break; + case IEEE80211_IOC_BEACON_INTERVAL: + /* NB: get from ic_bss for station mode */ + ireq->i_val = vap->iv_bss->ni_intval; + break; + case IEEE80211_IOC_PUREG: + ireq->i_val = (vap->iv_flags & IEEE80211_F_PUREG) != 0; + break; + case IEEE80211_IOC_BGSCAN: + ireq->i_val = (vap->iv_flags & IEEE80211_F_BGSCAN) != 0; + break; + case IEEE80211_IOC_BGSCAN_IDLE: + ireq->i_val = vap->iv_bgscanidle*hz/1000; /* ms */ + break; + case IEEE80211_IOC_BGSCAN_INTERVAL: + ireq->i_val = vap->iv_bgscanintvl/hz; /* seconds */ + break; + case IEEE80211_IOC_SCANVALID: + ireq->i_val = vap->iv_scanvalid/hz; /* seconds */ + break; + case IEEE80211_IOC_FRAGTHRESHOLD: + ireq->i_val = vap->iv_fragthreshold; + break; + case IEEE80211_IOC_MACCMD: + error = ieee80211_ioctl_getmaccmd(vap, ireq); + break; + case IEEE80211_IOC_BURST: + ireq->i_val = (vap->iv_flags & IEEE80211_F_BURST) != 0; + break; + case IEEE80211_IOC_BMISSTHRESHOLD: + ireq->i_val = vap->iv_bmissthreshold; + break; + case IEEE80211_IOC_CURCHAN: + error = ieee80211_ioctl_getcurchan(vap, ireq); + break; + case IEEE80211_IOC_SHORTGI: + ireq->i_val = 0; + if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) + ireq->i_val |= IEEE80211_HTCAP_SHORTGI20; + if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) + ireq->i_val |= IEEE80211_HTCAP_SHORTGI40; + break; + case IEEE80211_IOC_AMPDU: + ireq->i_val = 0; + if (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) + ireq->i_val |= 1; + if (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_RX) + ireq->i_val |= 2; + break; + case IEEE80211_IOC_AMPDU_LIMIT: + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + ireq->i_val = vap->iv_ampdu_rxmax; + else if (vap->iv_state == IEEE80211_S_RUN) + ireq->i_val = MS(vap->iv_bss->ni_htparam, + IEEE80211_HTCAP_MAXRXAMPDU); + else + ireq->i_val = vap->iv_ampdu_limit; + break; + case IEEE80211_IOC_AMPDU_DENSITY: + if (vap->iv_opmode == IEEE80211_M_STA && + vap->iv_state == IEEE80211_S_RUN) + ireq->i_val = MS(vap->iv_bss->ni_htparam, + IEEE80211_HTCAP_MPDUDENSITY); + else + ireq->i_val = vap->iv_ampdu_density; + break; + case IEEE80211_IOC_AMSDU: + ireq->i_val = 0; + if (vap->iv_flags_ht & IEEE80211_FHT_AMSDU_TX) + ireq->i_val |= 1; + if (vap->iv_flags_ht & IEEE80211_FHT_AMSDU_RX) + ireq->i_val |= 2; + break; + case IEEE80211_IOC_AMSDU_LIMIT: + ireq->i_val = vap->iv_amsdu_limit; /* XXX truncation? */ + break; + case IEEE80211_IOC_PUREN: + ireq->i_val = (vap->iv_flags_ht & IEEE80211_FHT_PUREN) != 0; + break; + case IEEE80211_IOC_DOTH: + ireq->i_val = (vap->iv_flags & IEEE80211_F_DOTH) != 0; + break; + case IEEE80211_IOC_REGDOMAIN: + error = ieee80211_ioctl_getregdomain(vap, ireq); + break; + case IEEE80211_IOC_ROAM: + error = ieee80211_ioctl_getroam(vap, ireq); + break; + case IEEE80211_IOC_TXPARAMS: + error = ieee80211_ioctl_gettxparams(vap, ireq); + break; + case IEEE80211_IOC_HTCOMPAT: + ireq->i_val = (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) != 0; + break; + case IEEE80211_IOC_DWDS: + ireq->i_val = (vap->iv_flags & IEEE80211_F_DWDS) != 0; + break; + case IEEE80211_IOC_INACTIVITY: + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_INACT) != 0; + break; + case IEEE80211_IOC_APPIE: + error = ieee80211_ioctl_getappie(vap, ireq); + break; + case IEEE80211_IOC_WPS: + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_WPS) != 0; + break; + case IEEE80211_IOC_TSN: + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_TSN) != 0; + break; + case IEEE80211_IOC_DFS: + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DFS) != 0; + break; + case IEEE80211_IOC_DOTD: + ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DOTD) != 0; + break; + case IEEE80211_IOC_DEVCAPS: + error = ieee80211_ioctl_getdevcaps(ic, ireq); + break; + case IEEE80211_IOC_HTPROTMODE: + ireq->i_val = ic->ic_htprotmode; + break; + case IEEE80211_IOC_HTCONF: + if (vap->iv_flags_ht & IEEE80211_FHT_HT) { + ireq->i_val = 1; + if (vap->iv_flags_ht & IEEE80211_FHT_USEHT40) + ireq->i_val |= 2; + } else + ireq->i_val = 0; + break; + case IEEE80211_IOC_STA_VLAN: + error = ieee80211_ioctl_getstavlan(vap, ireq); + break; + case IEEE80211_IOC_SMPS: + if (vap->iv_opmode == IEEE80211_M_STA && + vap->iv_state == IEEE80211_S_RUN) { + if (vap->iv_bss->ni_flags & IEEE80211_NODE_MIMO_RTS) + ireq->i_val = IEEE80211_HTCAP_SMPS_DYNAMIC; + else if (vap->iv_bss->ni_flags & IEEE80211_NODE_MIMO_PS) + ireq->i_val = IEEE80211_HTCAP_SMPS_ENA; + else + ireq->i_val = IEEE80211_HTCAP_SMPS_OFF; + } else + ireq->i_val = vap->iv_htcaps & IEEE80211_HTCAP_SMPS; + break; + case IEEE80211_IOC_RIFS: + if (vap->iv_opmode == IEEE80211_M_STA && + vap->iv_state == IEEE80211_S_RUN) + ireq->i_val = + (vap->iv_bss->ni_flags & IEEE80211_NODE_RIFS) != 0; + else + ireq->i_val = + (vap->iv_flags_ht & IEEE80211_FHT_RIFS) != 0; + break; + default: + error = ieee80211_ioctl_getdefault(vap, ireq); + break; + } + return error; +#undef MS +} + +static __noinline int +ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211req_key ik; + struct ieee80211_node *ni; + struct ieee80211_key *wk; + uint16_t kid; + int error, i; + + if (ireq->i_len != sizeof(ik)) + return EINVAL; + error = copyin(ireq->i_data, &ik, sizeof(ik)); + if (error) + return error; + /* NB: cipher support is verified by ieee80211_crypt_newkey */ + /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */ + if (ik.ik_keylen > sizeof(ik.ik_keydata)) + return E2BIG; + kid = ik.ik_keyix; + if (kid == IEEE80211_KEYIX_NONE) { + /* XXX unicast keys currently must be tx/rx */ + if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) + return EINVAL; + if (vap->iv_opmode == IEEE80211_M_STA) { + ni = ieee80211_ref_node(vap->iv_bss); + if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) { + ieee80211_free_node(ni); + return EADDRNOTAVAIL; + } + } else { + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, + ik.ik_macaddr); + if (ni == NULL) + return ENOENT; + } + wk = &ni->ni_ucastkey; + } else { + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + wk = &vap->iv_nw_keys[kid]; + /* + * Global slots start off w/o any assigned key index. + * Force one here for consistency with IEEE80211_IOC_WEPKEY. + */ + if (wk->wk_keyix == IEEE80211_KEYIX_NONE) + wk->wk_keyix = kid; + ni = NULL; + } + error = 0; + ieee80211_key_update_begin(vap); + if (ieee80211_crypto_newkey(vap, ik.ik_type, ik.ik_flags, wk)) { + wk->wk_keylen = ik.ik_keylen; + /* NB: MIC presence is implied by cipher type */ + if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE) + wk->wk_keylen = IEEE80211_KEYBUF_SIZE; + for (i = 0; i < IEEE80211_TID_SIZE; i++) + wk->wk_keyrsc[i] = ik.ik_keyrsc; + wk->wk_keytsc = 0; /* new key, reset */ + memset(wk->wk_key, 0, sizeof(wk->wk_key)); + memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen); + IEEE80211_ADDR_COPY(wk->wk_macaddr, + ni != NULL ? ni->ni_macaddr : ik.ik_macaddr); + if (!ieee80211_crypto_setkey(vap, wk)) + error = EIO; + else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT)) + vap->iv_def_txkey = kid; + } else + error = ENXIO; + ieee80211_key_update_end(vap); + if (ni != NULL) + ieee80211_free_node(ni); + return error; +} + +static __noinline int +ieee80211_ioctl_delkey(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211req_del_key dk; + int kid, error; + + if (ireq->i_len != sizeof(dk)) + return EINVAL; + error = copyin(ireq->i_data, &dk, sizeof(dk)); + if (error) + return error; + kid = dk.idk_keyix; + /* XXX uint8_t -> uint16_t */ + if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) { + struct ieee80211_node *ni; + + if (vap->iv_opmode == IEEE80211_M_STA) { + ni = ieee80211_ref_node(vap->iv_bss); + if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) { + ieee80211_free_node(ni); + return EADDRNOTAVAIL; + } + } else { + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, + dk.idk_macaddr); + if (ni == NULL) + return ENOENT; + } + /* XXX error return */ + ieee80211_node_delucastkey(ni); + ieee80211_free_node(ni); + } else { + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + /* XXX error return */ + ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[kid]); + } + return 0; +} + +struct mlmeop { + struct ieee80211vap *vap; + int op; + int reason; +}; + +static void +mlmedebug(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], + int op, int reason) +{ +#ifdef IEEE80211_DEBUG + static const struct { + int mask; + const char *opstr; + } ops[] = { + { 0, "op#0" }, + { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE | + IEEE80211_MSG_ASSOC, "assoc" }, + { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE | + IEEE80211_MSG_ASSOC, "disassoc" }, + { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE | + IEEE80211_MSG_AUTH, "deauth" }, + { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE | + IEEE80211_MSG_AUTH, "authorize" }, + { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE | + IEEE80211_MSG_AUTH, "unauthorize" }, + }; + + if (op == IEEE80211_MLME_AUTH) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_IOCTL | + IEEE80211_MSG_STATE | IEEE80211_MSG_AUTH, mac, + "station authenticate %s via MLME (reason %d)", + reason == IEEE80211_STATUS_SUCCESS ? "ACCEPT" : "REJECT", + reason); + } else if (!(IEEE80211_MLME_ASSOC <= op && op <= IEEE80211_MLME_AUTH)) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, mac, + "unknown MLME request %d (reason %d)", op, reason); + } else if (reason == IEEE80211_STATUS_SUCCESS) { + IEEE80211_NOTE_MAC(vap, ops[op].mask, mac, + "station %s via MLME", ops[op].opstr); + } else { + IEEE80211_NOTE_MAC(vap, ops[op].mask, mac, + "station %s via MLME (reason %d)", ops[op].opstr, reason); + } +#endif /* IEEE80211_DEBUG */ +} + +static void +domlme(void *arg, struct ieee80211_node *ni) +{ + struct mlmeop *mop = arg; + struct ieee80211vap *vap = ni->ni_vap; + + if (vap != mop->vap) + return; + /* + * NB: if ni_associd is zero then the node is already cleaned + * up and we don't need to do this (we're safely holding a + * reference but should otherwise not modify it's state). + */ + if (ni->ni_associd == 0) + return; + mlmedebug(vap, ni->ni_macaddr, mop->op, mop->reason); + if (mop->op == IEEE80211_MLME_DEAUTH) { + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + mop->reason); + } else { + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC, + mop->reason); + } + ieee80211_node_leave(ni); +} + +static int +setmlme_dropsta(struct ieee80211vap *vap, + const uint8_t mac[IEEE80211_ADDR_LEN], struct mlmeop *mlmeop) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211_node *ni; + int error = 0; + + /* NB: the broadcast address means do 'em all */ + if (!IEEE80211_ADDR_EQ(mac, ic->ic_ifp->if_broadcastaddr)) { + IEEE80211_NODE_LOCK(nt); + ni = ieee80211_find_node_locked(nt, mac); + if (ni != NULL) { + domlme(mlmeop, ni); + ieee80211_free_node(ni); + } else + error = ENOENT; + IEEE80211_NODE_UNLOCK(nt); + } else { + ieee80211_iterate_nodes(nt, domlme, mlmeop); + } + return error; +} + +static __noinline int +setmlme_common(struct ieee80211vap *vap, int op, + const uint8_t mac[IEEE80211_ADDR_LEN], int reason) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211_node *ni; + struct mlmeop mlmeop; + int error; + + error = 0; + switch (op) { + case IEEE80211_MLME_DISASSOC: + case IEEE80211_MLME_DEAUTH: + switch (vap->iv_opmode) { + case IEEE80211_M_STA: + mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason); + /* XXX not quite right */ + ieee80211_new_state(vap, IEEE80211_S_INIT, reason); + break; + case IEEE80211_M_HOSTAP: + mlmeop.vap = vap; + mlmeop.op = op; + mlmeop.reason = reason; + error = setmlme_dropsta(vap, mac, &mlmeop); + break; + case IEEE80211_M_WDS: + /* XXX user app should send raw frame? */ + if (op != IEEE80211_MLME_DEAUTH) { + error = EINVAL; + break; + } +#if 0 + /* XXX accept any address, simplifies user code */ + if (!IEEE80211_ADDR_EQ(mac, vap->iv_bss->ni_macaddr)) { + error = EINVAL; + break; + } +#endif + mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason); + ni = ieee80211_ref_node(vap->iv_bss); + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, reason); + ieee80211_free_node(ni); + break; + default: + error = EINVAL; + break; + } + break; + case IEEE80211_MLME_AUTHORIZE: + case IEEE80211_MLME_UNAUTHORIZE: + if (vap->iv_opmode != IEEE80211_M_HOSTAP && + vap->iv_opmode != IEEE80211_M_WDS) { + error = EINVAL; + break; + } + IEEE80211_NODE_LOCK(nt); + ni = ieee80211_find_vap_node_locked(nt, vap, mac); + if (ni != NULL) { + mlmedebug(vap, mac, op, reason); + if (op == IEEE80211_MLME_AUTHORIZE) + ieee80211_node_authorize(ni); + else + ieee80211_node_unauthorize(ni); + ieee80211_free_node(ni); + } else + error = ENOENT; + IEEE80211_NODE_UNLOCK(nt); + break; + case IEEE80211_MLME_AUTH: + if (vap->iv_opmode != IEEE80211_M_HOSTAP) { + error = EINVAL; + break; + } + IEEE80211_NODE_LOCK(nt); + ni = ieee80211_find_vap_node_locked(nt, vap, mac); + if (ni != NULL) { + mlmedebug(vap, mac, op, reason); + if (reason == IEEE80211_STATUS_SUCCESS) { + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + /* + * For shared key auth, just continue the + * exchange. Otherwise 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 && + ni->ni_challenge == NULL) + ieee80211_node_authorize(ni); + } else { + vap->iv_stats.is_rx_acl++; + ieee80211_send_error(ni, ni->ni_macaddr, + IEEE80211_FC0_SUBTYPE_AUTH, 2|(reason<<16)); + ieee80211_node_leave(ni); + } + ieee80211_free_node(ni); + } else + error = ENOENT; + IEEE80211_NODE_UNLOCK(nt); + break; + default: + error = EINVAL; + break; + } + return error; +} + +struct scanlookup { + const uint8_t *mac; + int esslen; + const uint8_t *essid; + const struct ieee80211_scan_entry *se; +}; + +/* + * Match mac address and any ssid. + */ +static void +mlmelookup(void *arg, const struct ieee80211_scan_entry *se) +{ + struct scanlookup *look = arg; + + if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr)) + return; + if (look->esslen != 0) { + if (se->se_ssid[1] != look->esslen) + return; + if (memcmp(look->essid, se->se_ssid+2, look->esslen)) + return; + } + look->se = se; +} + +static __noinline int +setmlme_assoc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], + int ssid_len, const uint8_t ssid[IEEE80211_NWID_LEN]) +{ + struct scanlookup lookup; + + /* XXX ibss/ahdemo */ + if (vap->iv_opmode != IEEE80211_M_STA) + return EINVAL; + + /* NB: this is racey if roaming is !manual */ + lookup.se = NULL; + lookup.mac = mac; + lookup.esslen = ssid_len; + lookup.essid = ssid; + ieee80211_scan_iterate(vap, mlmelookup, &lookup); + if (lookup.se == NULL) + return ENOENT; + mlmedebug(vap, mac, IEEE80211_MLME_ASSOC, 0); + if (!ieee80211_sta_join(vap, lookup.se->se_chan, lookup.se)) + return EIO; /* XXX unique but could be better */ + return 0; +} + +static __noinline int +ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211req_mlme mlme; + int error; + + if (ireq->i_len != sizeof(mlme)) + return EINVAL; + error = copyin(ireq->i_data, &mlme, sizeof(mlme)); + if (error) + return error; + if (mlme.im_op == IEEE80211_MLME_ASSOC) + return setmlme_assoc(vap, mlme.im_macaddr, + vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid); + else + return setmlme_common(vap, mlme.im_op, + mlme.im_macaddr, mlme.im_reason); +} + +static __noinline int +ieee80211_ioctl_macmac(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + uint8_t mac[IEEE80211_ADDR_LEN]; + const struct ieee80211_aclator *acl = vap->iv_acl; + int error; + + if (ireq->i_len != sizeof(mac)) + return EINVAL; + error = copyin(ireq->i_data, mac, ireq->i_len); + if (error) + return error; + if (acl == NULL) { + acl = ieee80211_aclator_get("mac"); + if (acl == NULL || !acl->iac_attach(vap)) + return EINVAL; + vap->iv_acl = acl; + } + if (ireq->i_type == IEEE80211_IOC_ADDMAC) + acl->iac_add(vap, mac); + else + acl->iac_remove(vap, mac); + return 0; +} + +static __noinline int +ieee80211_ioctl_setmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + const struct ieee80211_aclator *acl = vap->iv_acl; + + switch (ireq->i_val) { + case IEEE80211_MACCMD_POLICY_OPEN: + case IEEE80211_MACCMD_POLICY_ALLOW: + case IEEE80211_MACCMD_POLICY_DENY: + case IEEE80211_MACCMD_POLICY_RADIUS: + if (acl == NULL) { + acl = ieee80211_aclator_get("mac"); + if (acl == NULL || !acl->iac_attach(vap)) + return EINVAL; + vap->iv_acl = acl; + } + acl->iac_setpolicy(vap, ireq->i_val); + break; + case IEEE80211_MACCMD_FLUSH: + if (acl != NULL) + acl->iac_flush(vap); + /* NB: silently ignore when not in use */ + break; + case IEEE80211_MACCMD_DETACH: + if (acl != NULL) { + vap->iv_acl = NULL; + acl->iac_detach(vap); + } + break; + default: + if (acl == NULL) + return EINVAL; + else + return acl->iac_setioctl(vap, ireq); + } + return 0; +} + +static __noinline int +ieee80211_ioctl_setchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + uint8_t *chanlist, *list; + int i, nchan, maxchan, error; + + if (ireq->i_len > sizeof(ic->ic_chan_active)) + ireq->i_len = sizeof(ic->ic_chan_active); + list = malloc(ireq->i_len + IEEE80211_CHAN_BYTES, M_TEMP, + M_NOWAIT | M_ZERO); + if (list == NULL) + return ENOMEM; + error = copyin(ireq->i_data, list, ireq->i_len); + if (error) { + free(list, M_TEMP); + return error; + } + nchan = 0; + chanlist = list + ireq->i_len; /* NB: zero'd already */ + maxchan = ireq->i_len * NBBY; + for (i = 0; i < ic->ic_nchans; i++) { + const struct ieee80211_channel *c = &ic->ic_channels[i]; + /* + * Calculate the intersection of the user list and the + * available channels so users can do things like specify + * 1-255 to get all available channels. + */ + if (c->ic_ieee < maxchan && isset(list, c->ic_ieee)) { + setbit(chanlist, c->ic_ieee); + nchan++; + } + } + if (nchan == 0) { + free(list, M_TEMP); + return EINVAL; + } + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */ + isclr(chanlist, ic->ic_bsschan->ic_ieee)) + ic->ic_bsschan = IEEE80211_CHAN_ANYC; + memcpy(ic->ic_chan_active, chanlist, IEEE80211_CHAN_BYTES); + ieee80211_scan_flush(vap); + free(list, M_TEMP); + return ENETRESET; +} + +static __noinline int +ieee80211_ioctl_setstastats(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + int error; + + /* + * NB: we could copyin ieee80211req_sta_stats so apps + * could make selective changes but that's overkill; + * just clear all stats for now. + */ + if (ireq->i_len < IEEE80211_ADDR_LEN) + return EINVAL; + error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN); + if (error != 0) + return error; + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr); + if (ni == NULL) + return ENOENT; + /* XXX require ni_vap == vap? */ + memset(&ni->ni_stats, 0, sizeof(ni->ni_stats)); + ieee80211_free_node(ni); + return 0; +} + +static __noinline int +ieee80211_ioctl_setstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_sta_txpow txpow; + int error; + + if (ireq->i_len != sizeof(txpow)) + return EINVAL; + error = copyin(ireq->i_data, &txpow, sizeof(txpow)); + if (error != 0) + return error; + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr); + if (ni == NULL) + return ENOENT; + ni->ni_txpower = txpow.it_txpow; + ieee80211_free_node(ni); + return error; +} + +static __noinline int +ieee80211_ioctl_setwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_wme_state *wme = &ic->ic_wme; + struct wmeParams *wmep, *chanp; + int isbss, ac; + + if ((ic->ic_caps & IEEE80211_C_WME) == 0) + return EOPNOTSUPP; + + isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS); + ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL); + if (ac >= WME_NUM_AC) + ac = WME_AC_BE; + if (isbss) { + chanp = &wme->wme_bssChanParams.cap_wmeParams[ac]; + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac]; + } else { + chanp = &wme->wme_chanParams.cap_wmeParams[ac]; + wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac]; + } + switch (ireq->i_type) { + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + if (isbss) { + wmep->wmep_logcwmin = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_logcwmin = ireq->i_val; + } else { + wmep->wmep_logcwmin = chanp->wmep_logcwmin = + ireq->i_val; + } + break; + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + if (isbss) { + wmep->wmep_logcwmax = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_logcwmax = ireq->i_val; + } else { + wmep->wmep_logcwmax = chanp->wmep_logcwmax = + ireq->i_val; + } + break; + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + if (isbss) { + wmep->wmep_aifsn = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_aifsn = ireq->i_val; + } else { + wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val; + } + break; + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + if (isbss) { + wmep->wmep_txopLimit = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_txopLimit = ireq->i_val; + } else { + wmep->wmep_txopLimit = chanp->wmep_txopLimit = + ireq->i_val; + } + break; + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + wmep->wmep_acm = ireq->i_val; + if ((wme->wme_flags & WME_F_AGGRMODE) == 0) + chanp->wmep_acm = ireq->i_val; + break; + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/ + wmep->wmep_noackPolicy = chanp->wmep_noackPolicy = + (ireq->i_val) == 0; + break; + } + ieee80211_wme_updateparams(vap); + return 0; +} + +static int +find11gchannel(struct ieee80211com *ic, int start, int freq) +{ + const struct ieee80211_channel *c; + int i; + + for (i = start+1; i < ic->ic_nchans; i++) { + c = &ic->ic_channels[i]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) + return 1; + } + /* NB: should not be needed but in case things are mis-sorted */ + for (i = 0; i < start; i++) { + c = &ic->ic_channels[i]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) + return 1; + } + return 0; +} + +static struct ieee80211_channel * +findchannel(struct ieee80211com *ic, int ieee, int mode) +{ + static const u_int chanflags[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = 0, + [IEEE80211_MODE_11A] = IEEE80211_CHAN_A, + [IEEE80211_MODE_11B] = IEEE80211_CHAN_B, + [IEEE80211_MODE_11G] = IEEE80211_CHAN_G, + [IEEE80211_MODE_FH] = IEEE80211_CHAN_FHSS, + [IEEE80211_MODE_TURBO_A] = IEEE80211_CHAN_108A, + [IEEE80211_MODE_TURBO_G] = IEEE80211_CHAN_108G, + [IEEE80211_MODE_STURBO_A] = IEEE80211_CHAN_STURBO, + [IEEE80211_MODE_HALF] = IEEE80211_CHAN_HALF, + [IEEE80211_MODE_QUARTER] = IEEE80211_CHAN_QUARTER, + /* NB: handled specially below */ + [IEEE80211_MODE_11NA] = IEEE80211_CHAN_A, + [IEEE80211_MODE_11NG] = IEEE80211_CHAN_G, + }; + u_int modeflags; + int i; + + modeflags = chanflags[mode]; + for (i = 0; i < ic->ic_nchans; i++) { + struct ieee80211_channel *c = &ic->ic_channels[i]; + + if (c->ic_ieee != ieee) + continue; + if (mode == IEEE80211_MODE_AUTO) { + /* ignore turbo channels for autoselect */ + if (IEEE80211_IS_CHAN_TURBO(c)) + continue; + /* + * XXX special-case 11b/g channels so we + * always select the g channel if both + * are present. + * XXX prefer HT to non-HT? + */ + if (!IEEE80211_IS_CHAN_B(c) || + !find11gchannel(ic, i, c->ic_freq)) + return c; + } else { + /* must check HT specially */ + if ((mode == IEEE80211_MODE_11NA || + mode == IEEE80211_MODE_11NG) && + !IEEE80211_IS_CHAN_HT(c)) + continue; + if ((c->ic_flags & modeflags) == modeflags) + return c; + } + } + return NULL; +} + +/* + * Check the specified against any desired mode (aka netband). + * This is only used (presently) when operating in hostap mode + * to enforce consistency. + */ +static int +check_mode_consistency(const struct ieee80211_channel *c, int mode) +{ + KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel")); + + switch (mode) { + case IEEE80211_MODE_11B: + return (IEEE80211_IS_CHAN_B(c)); + case IEEE80211_MODE_11G: + return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c)); + case IEEE80211_MODE_11A: + return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c)); + case IEEE80211_MODE_STURBO_A: + return (IEEE80211_IS_CHAN_STURBO(c)); + case IEEE80211_MODE_11NA: + return (IEEE80211_IS_CHAN_HTA(c)); + case IEEE80211_MODE_11NG: + return (IEEE80211_IS_CHAN_HTG(c)); + } + return 1; + +} + +/* + * Common code to set the current channel. If the device + * is up and running this may result in an immediate channel + * change or a kick of the state machine. + */ +static int +setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c) +{ + struct ieee80211com *ic = vap->iv_ic; + int error; + + if (c != IEEE80211_CHAN_ANYC) { + if (IEEE80211_IS_CHAN_RADAR(c)) + return EBUSY; /* XXX better code? */ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + if (IEEE80211_IS_CHAN_NOHOSTAP(c)) + return EINVAL; + if (!check_mode_consistency(c, vap->iv_des_mode)) + return EINVAL; + } else if (vap->iv_opmode == IEEE80211_M_IBSS) { + if (IEEE80211_IS_CHAN_NOADHOC(c)) + return EINVAL; + } + if (vap->iv_state == IEEE80211_S_RUN && + vap->iv_bss->ni_chan == c) + return 0; /* NB: nothing to do */ + } + vap->iv_des_chan = c; + + error = 0; + if (vap->iv_opmode == IEEE80211_M_MONITOR && + vap->iv_des_chan != IEEE80211_CHAN_ANYC) { + /* + * Monitor mode can switch directly. + */ + if (IFNET_IS_UP_RUNNING(vap->iv_ifp)) { + /* XXX need state machine for other vap's to follow */ + ieee80211_setcurchan(ic, vap->iv_des_chan); + vap->iv_bss->ni_chan = ic->ic_curchan; + } else + ic->ic_curchan = vap->iv_des_chan; + ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); + } else { + /* + * Need to go through the state machine in case we + * need to reassociate or the like. The state machine + * will pickup the desired channel and avoid scanning. + */ + if (IS_UP_AUTO(vap)) + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + else if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { + /* + * When not up+running and a real channel has + * been specified fix the current channel so + * there is immediate feedback; e.g. via ifconfig. + */ + ic->ic_curchan = vap->iv_des_chan; + ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); + } + } + return error; +} + +/* + * Old api for setting the current channel; this is + * deprecated because channel numbers are ambiguous. + */ +static __noinline int +ieee80211_ioctl_setchannel(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel *c; + + /* XXX 0xffff overflows 16-bit signed */ + if (ireq->i_val == 0 || + ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) { + c = IEEE80211_CHAN_ANYC; + } else { + struct ieee80211_channel *c2; + + c = findchannel(ic, ireq->i_val, vap->iv_des_mode); + if (c == NULL) { + c = findchannel(ic, ireq->i_val, + IEEE80211_MODE_AUTO); + if (c == NULL) + return EINVAL; + } + /* + * Fine tune channel selection based on desired mode: + * if 11b is requested, find the 11b version of any + * 11g channel returned, + * if static turbo, find the turbo version of any + * 11a channel return, + * if 11na is requested, find the ht version of any + * 11a channel returned, + * if 11ng is requested, find the ht version of any + * 11g channel returned, + * otherwise we should be ok with what we've got. + */ + switch (vap->iv_des_mode) { + case IEEE80211_MODE_11B: + if (IEEE80211_IS_CHAN_ANYG(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_11B); + /* NB: should not happen, =>'s 11g w/o 11b */ + if (c2 != NULL) + c = c2; + } + break; + case IEEE80211_MODE_TURBO_A: + if (IEEE80211_IS_CHAN_A(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_TURBO_A); + if (c2 != NULL) + c = c2; + } + break; + case IEEE80211_MODE_11NA: + if (IEEE80211_IS_CHAN_A(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_11NA); + if (c2 != NULL) + c = c2; + } + break; + case IEEE80211_MODE_11NG: + if (IEEE80211_IS_CHAN_ANYG(c)) { + c2 = findchannel(ic, ireq->i_val, + IEEE80211_MODE_11NG); + if (c2 != NULL) + c = c2; + } + break; + default: /* NB: no static turboG */ + break; + } + } + return setcurchan(vap, c); +} + +/* + * New/current api for setting the current channel; a complete + * channel description is provide so there is no ambiguity in + * identifying the channel. + */ +static __noinline int +ieee80211_ioctl_setcurchan(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel chan, *c; + int error; + + if (ireq->i_len != sizeof(chan)) + return EINVAL; + error = copyin(ireq->i_data, &chan, sizeof(chan)); + if (error != 0) + return error; + /* XXX 0xffff overflows 16-bit signed */ + if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) { + c = IEEE80211_CHAN_ANYC; + } else { + c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags); + if (c == NULL) + return EINVAL; + } + return setcurchan(vap, c); +} + +static __noinline int +ieee80211_ioctl_setregdomain(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + struct ieee80211_regdomain_req *reg; + int nchans, error; + + nchans = 1 + ((ireq->i_len - sizeof(struct ieee80211_regdomain_req)) / + sizeof(struct ieee80211_channel)); + if (!(1 <= nchans && nchans <= IEEE80211_CHAN_MAX)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL, + "%s: bad # chans, i_len %d nchans %d\n", __func__, + ireq->i_len, nchans); + return EINVAL; + } + reg = (struct ieee80211_regdomain_req *) + malloc(IEEE80211_REGDOMAIN_SIZE(nchans), M_TEMP, M_NOWAIT); + if (reg == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL, + "%s: no memory, nchans %d\n", __func__, nchans); + return ENOMEM; + } + error = copyin(ireq->i_data, reg, IEEE80211_REGDOMAIN_SIZE(nchans)); + if (error == 0) { + /* NB: validate inline channel count against storage size */ + if (reg->chaninfo.ic_nchans != nchans) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL, + "%s: chan cnt mismatch, %d != %d\n", __func__, + reg->chaninfo.ic_nchans, nchans); + error = EINVAL; + } else + error = ieee80211_setregdomain(vap, reg); + } + free(reg, M_TEMP); + + return (error == 0 ? ENETRESET : error); +} + +static int +ieee80211_ioctl_setroam(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + if (ireq->i_len != sizeof(vap->iv_roamparms)) + return EINVAL; + /* XXX validate params */ + /* XXX? ENETRESET to push to device? */ + return copyin(ireq->i_data, vap->iv_roamparms, + sizeof(vap->iv_roamparms)); +} + +static int +checkrate(const struct ieee80211_rateset *rs, int rate) +{ + int i; + + if (rate == IEEE80211_FIXED_RATE_NONE) + return 1; + for (i = 0; i < rs->rs_nrates; i++) + if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rate) + return 1; + return 0; +} + +static int +checkmcs(int mcs) +{ + if (mcs == IEEE80211_FIXED_RATE_NONE) + return 1; + if ((mcs & IEEE80211_RATE_MCS) == 0) /* MCS always have 0x80 set */ + return 0; + return (mcs & 0x7f) <= 15; /* XXX could search ht rate set */ +} + +static __noinline int +ieee80211_ioctl_settxparams(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_txparams_req parms; /* XXX stack use? */ + struct ieee80211_txparam *src, *dst; + const struct ieee80211_rateset *rs; + int error, mode, changed, is11n, nmodes; + + /* NB: accept short requests for backwards compat */ + if (ireq->i_len > sizeof(parms)) + return EINVAL; + error = copyin(ireq->i_data, &parms, ireq->i_len); + if (error != 0) + return error; + nmodes = ireq->i_len / sizeof(struct ieee80211_txparam); + changed = 0; + /* validate parameters and check if anything changed */ + for (mode = IEEE80211_MODE_11A; mode < nmodes; mode++) { + if (isclr(ic->ic_modecaps, mode)) + continue; + src = &parms.params[mode]; + dst = &vap->iv_txparms[mode]; + rs = &ic->ic_sup_rates[mode]; /* NB: 11n maps to legacy */ + is11n = (mode == IEEE80211_MODE_11NA || + mode == IEEE80211_MODE_11NG); + if (src->ucastrate != dst->ucastrate) { + if (!checkrate(rs, src->ucastrate) && + (!is11n || !checkmcs(src->ucastrate))) + return EINVAL; + changed++; + } + if (src->mcastrate != dst->mcastrate) { + if (!checkrate(rs, src->mcastrate) && + (!is11n || !checkmcs(src->mcastrate))) + return EINVAL; + changed++; + } + if (src->mgmtrate != dst->mgmtrate) { + if (!checkrate(rs, src->mgmtrate) && + (!is11n || !checkmcs(src->mgmtrate))) + return EINVAL; + changed++; + } + if (src->maxretry != dst->maxretry) /* NB: no bounds */ + changed++; + } + if (changed) { + /* + * Copy new parameters in place and notify the + * driver so it can push state to the device. + */ + for (mode = IEEE80211_MODE_11A; mode < nmodes; mode++) { + if (isset(ic->ic_modecaps, mode)) + vap->iv_txparms[mode] = parms.params[mode]; + } + /* XXX could be more intelligent, + e.g. don't reset if setting not being used */ + return ENETRESET; + } + return 0; +} + +/* + * Application Information Element support. + */ +static int +setappie(struct ieee80211_appie **aie, const struct ieee80211req *ireq) +{ + struct ieee80211_appie *app = *aie; + struct ieee80211_appie *napp; + int error; + + if (ireq->i_len == 0) { /* delete any existing ie */ + if (app != NULL) { + *aie = NULL; /* XXX racey */ + free(app, M_80211_NODE_IE); + } + return 0; + } + if (!(2 <= ireq->i_len && ireq->i_len <= IEEE80211_MAX_APPIE)) + return EINVAL; + /* + * Allocate a new appie structure and copy in the user data. + * When done swap in the new structure. Note that we do not + * guard against users holding a ref to the old structure; + * this must be handled outside this code. + * + * XXX bad bad bad + */ + napp = (struct ieee80211_appie *) malloc( + sizeof(struct ieee80211_appie) + ireq->i_len, M_80211_NODE_IE, M_NOWAIT); + if (napp == NULL) + return ENOMEM; + /* XXX holding ic lock */ + error = copyin(ireq->i_data, napp->ie_data, ireq->i_len); + if (error) { + free(napp, M_80211_NODE_IE); + return error; + } + napp->ie_len = ireq->i_len; + *aie = napp; + if (app != NULL) + free(app, M_80211_NODE_IE); + return 0; +} + +static void +setwparsnie(struct ieee80211vap *vap, uint8_t *ie, int space) +{ + /* validate data is present as best we can */ + if (space == 0 || 2+ie[1] > space) + return; + if (ie[0] == IEEE80211_ELEMID_VENDOR) + vap->iv_wpa_ie = ie; + else if (ie[0] == IEEE80211_ELEMID_RSN) + vap->iv_rsn_ie = ie; +} + +static __noinline int +ieee80211_ioctl_setappie_locked(struct ieee80211vap *vap, + const struct ieee80211req *ireq, int fc0) +{ + int error; + + IEEE80211_LOCK_ASSERT(vap->iv_ic); + + switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) { + case IEEE80211_FC0_SUBTYPE_BEACON: + if (vap->iv_opmode != IEEE80211_M_HOSTAP && + vap->iv_opmode != IEEE80211_M_IBSS) { + error = EINVAL; + break; + } + error = setappie(&vap->iv_appie_beacon, ireq); + if (error == 0) + ieee80211_beacon_notify(vap, IEEE80211_BEACON_APPIE); + break; + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + error = setappie(&vap->iv_appie_proberesp, ireq); + break; + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + error = setappie(&vap->iv_appie_assocresp, ireq); + else + error = EINVAL; + break; + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + error = setappie(&vap->iv_appie_probereq, ireq); + break; + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + if (vap->iv_opmode == IEEE80211_M_STA) + error = setappie(&vap->iv_appie_assocreq, ireq); + else + error = EINVAL; + break; + case (IEEE80211_APPIE_WPA & IEEE80211_FC0_SUBTYPE_MASK): + error = setappie(&vap->iv_appie_wpa, ireq); + if (error == 0) { + /* + * Must split single blob of data into separate + * WPA and RSN ie's because they go in different + * locations in the mgt frames. + * XXX use IEEE80211_IOC_WPA2 so user code does split + */ + vap->iv_wpa_ie = NULL; + vap->iv_rsn_ie = NULL; + if (vap->iv_appie_wpa != NULL) { + struct ieee80211_appie *appie = + vap->iv_appie_wpa; + uint8_t *data = appie->ie_data; + + /* XXX ie length validate is painful, cheat */ + setwparsnie(vap, data, appie->ie_len); + setwparsnie(vap, data + 2 + data[1], + appie->ie_len - (2 + data[1])); + } + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) { + /* + * Must rebuild beacon frame as the update + * mechanism doesn't handle WPA/RSN ie's. + * Could extend it but it doesn't normally + * change; this is just to deal with hostapd + * plumbing the ie after the interface is up. + */ + error = ENETRESET; + } + } + break; + default: + error = EINVAL; + break; + } + return error; +} + +static __noinline int +ieee80211_ioctl_setappie(struct ieee80211vap *vap, + const struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + int error; + uint8_t fc0; + + fc0 = ireq->i_val & 0xff; + if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) + return EINVAL; + /* NB: could check iv_opmode and reject but hardly worth the effort */ + IEEE80211_LOCK(ic); + error = ieee80211_ioctl_setappie_locked(vap, ireq, fc0); + IEEE80211_UNLOCK(ic); + return error; +} + +static __noinline int +ieee80211_ioctl_chanswitch(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_chanswitch_req csr; + struct ieee80211_channel *c; + int error; + + if (ireq->i_len != sizeof(csr)) + return EINVAL; + error = copyin(ireq->i_data, &csr, sizeof(csr)); + if (error != 0) + return error; + /* XXX adhoc mode not supported */ + if (vap->iv_opmode != IEEE80211_M_HOSTAP || + (vap->iv_flags & IEEE80211_F_DOTH) == 0) + return EOPNOTSUPP; + c = ieee80211_find_channel(ic, + csr.csa_chan.ic_freq, csr.csa_chan.ic_flags); + if (c == NULL) + return ENOENT; + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) + ieee80211_csa_startswitch(ic, c, csr.csa_mode, csr.csa_count); + else if (csr.csa_count == 0) + ieee80211_csa_cancelswitch(ic); + else + error = EBUSY; + IEEE80211_UNLOCK(ic); + return error; +} + +static __noinline int +ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ +#define IEEE80211_IOC_SCAN_FLAGS \ + (IEEE80211_IOC_SCAN_NOPICK | IEEE80211_IOC_SCAN_ACTIVE | \ + IEEE80211_IOC_SCAN_PICK1ST | IEEE80211_IOC_SCAN_BGSCAN | \ + IEEE80211_IOC_SCAN_ONCE | IEEE80211_IOC_SCAN_NOBCAST | \ + IEEE80211_IOC_SCAN_NOJOIN | IEEE80211_IOC_SCAN_FLUSH | \ + IEEE80211_IOC_SCAN_CHECK) + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_req sr; /* XXX off stack? */ + int error, i; + + /* NB: parent must be running */ + if ((ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return ENXIO; + + if (ireq->i_len != sizeof(sr)) + return EINVAL; + error = copyin(ireq->i_data, &sr, sizeof(sr)); + if (error != 0) + return error; + /* convert duration */ + if (sr.sr_duration == IEEE80211_IOC_SCAN_FOREVER) + sr.sr_duration = IEEE80211_SCAN_FOREVER; + else { + if (sr.sr_duration < IEEE80211_IOC_SCAN_DURATION_MIN || + sr.sr_duration > IEEE80211_IOC_SCAN_DURATION_MAX) + return EINVAL; + sr.sr_duration = msecs_to_ticks(sr.sr_duration); + if (sr.sr_duration < 1) + sr.sr_duration = 1; + } + /* convert min/max channel dwell */ + if (sr.sr_mindwell != 0) { + sr.sr_mindwell = msecs_to_ticks(sr.sr_mindwell); + if (sr.sr_mindwell < 1) + sr.sr_mindwell = 1; + } + if (sr.sr_maxdwell != 0) { + sr.sr_maxdwell = msecs_to_ticks(sr.sr_maxdwell); + if (sr.sr_maxdwell < 1) + sr.sr_maxdwell = 1; + } + /* NB: silently reduce ssid count to what is supported */ + if (sr.sr_nssid > IEEE80211_SCAN_MAX_SSID) + sr.sr_nssid = IEEE80211_SCAN_MAX_SSID; + for (i = 0; i < sr.sr_nssid; i++) + if (sr.sr_ssid[i].len > IEEE80211_NWID_LEN) + return EINVAL; + /* cleanse flags just in case, could reject if invalid flags */ + sr.sr_flags &= IEEE80211_IOC_SCAN_FLAGS; + /* + * Add an implicit NOPICK if the vap is not marked UP. This + * allows applications to scan without joining a bss (or picking + * a channel and setting up a bss) and without forcing manual + * roaming mode--you just need to mark the parent device UP. + */ + if ((vap->iv_ifp->if_flags & IFF_UP) == 0) + sr.sr_flags |= IEEE80211_IOC_SCAN_NOPICK; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: flags 0x%x%s duration 0x%x mindwell %u maxdwell %u nssid %d\n", + __func__, sr.sr_flags, + (vap->iv_ifp->if_flags & IFF_UP) == 0 ? " (!IFF_UP)" : "", + sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell, sr.sr_nssid); + /* + * If we are in INIT state then the driver has never had a chance + * to setup hardware state to do a scan; we must use the state + * machine to get us up to the SCAN state but once we reach SCAN + * state we then want to use the supplied params. Stash the + * parameters in the vap and mark IEEE80211_FEXT_SCANREQ; the + * state machines will recognize this and use the stashed params + * to issue the scan request. + * + * Otherwise just invoke the scan machinery directly. + */ + IEEE80211_LOCK(ic); + if (vap->iv_state == IEEE80211_S_INIT) { + /* NB: clobbers previous settings */ + vap->iv_scanreq_flags = sr.sr_flags; + vap->iv_scanreq_duration = sr.sr_duration; + vap->iv_scanreq_nssid = sr.sr_nssid; + for (i = 0; i < sr.sr_nssid; i++) { + vap->iv_scanreq_ssid[i].len = sr.sr_ssid[i].len; + memcpy(vap->iv_scanreq_ssid[i].ssid, sr.sr_ssid[i].ssid, + sr.sr_ssid[i].len); + } + vap->iv_flags_ext |= IEEE80211_FEXT_SCANREQ; + IEEE80211_UNLOCK(ic); + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + } else { + vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; + IEEE80211_UNLOCK(ic); + /* XXX neeed error return codes */ + if (sr.sr_flags & IEEE80211_IOC_SCAN_CHECK) { + (void) ieee80211_check_scan(vap, sr.sr_flags, + sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell, + sr.sr_nssid, + /* NB: cheat, we assume structures are compatible */ + (const struct ieee80211_scan_ssid *) &sr.sr_ssid[0]); + } else { + (void) ieee80211_start_scan(vap, sr.sr_flags, + sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell, + sr.sr_nssid, + /* NB: cheat, we assume structures are compatible */ + (const struct ieee80211_scan_ssid *) &sr.sr_ssid[0]); + } + } + return error; +#undef IEEE80211_IOC_SCAN_FLAGS +} + +static __noinline int +ieee80211_ioctl_setstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_node *ni; + struct ieee80211req_sta_vlan vlan; + int error; + + if (ireq->i_len != sizeof(vlan)) + return EINVAL; + error = copyin(ireq->i_data, &vlan, sizeof(vlan)); + if (error != 0) + return error; + if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) { + ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, + vlan.sv_macaddr); + if (ni == NULL) + return ENOENT; + } else + ni = ieee80211_ref_node(vap->iv_bss); + ni->ni_vlan = vlan.sv_vlan; + ieee80211_free_node(ni); + return error; +} + +static int +isvap11g(const struct ieee80211vap *vap) +{ + const struct ieee80211_node *bss = vap->iv_bss; + return bss->ni_chan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_ANYG(bss->ni_chan); +} + +static int +isvapht(const struct ieee80211vap *vap) +{ + const struct ieee80211_node *bss = vap->iv_bss; + return bss->ni_chan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_HT(bss->ni_chan); +} + +/* + * Dummy ioctl set handler so the linker set is defined. + */ +static int +dummy_ioctl_set(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + return ENOSYS; +} +IEEE80211_IOCTL_SET(dummy, dummy_ioctl_set); + +static int +ieee80211_ioctl_setdefault(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + ieee80211_ioctl_setfunc * const *set; + int error; + + SET_FOREACH(set, ieee80211_ioctl_setset) { + error = (*set)(vap, ireq); + if (error != ENOSYS) + return error; + } + return EINVAL; +} + +static __noinline int +ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + int error; + const struct ieee80211_authenticator *auth; + uint8_t tmpkey[IEEE80211_KEYBUF_SIZE]; + char tmpssid[IEEE80211_NWID_LEN]; + uint8_t tmpbssid[IEEE80211_ADDR_LEN]; + struct ieee80211_key *k; + u_int kid; + uint32_t flags; + + error = 0; + switch (ireq->i_type) { + case IEEE80211_IOC_SSID: + if (ireq->i_val != 0 || + ireq->i_len > IEEE80211_NWID_LEN) + return EINVAL; + error = copyin(ireq->i_data, tmpssid, ireq->i_len); + if (error) + break; + memset(vap->iv_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN); + vap->iv_des_ssid[0].len = ireq->i_len; + memcpy(vap->iv_des_ssid[0].ssid, tmpssid, ireq->i_len); + vap->iv_des_nssid = (ireq->i_len > 0); + error = ENETRESET; + break; + case IEEE80211_IOC_WEP: + switch (ireq->i_val) { + case IEEE80211_WEP_OFF: + vap->iv_flags &= ~IEEE80211_F_PRIVACY; + vap->iv_flags &= ~IEEE80211_F_DROPUNENC; + break; + case IEEE80211_WEP_ON: + vap->iv_flags |= IEEE80211_F_PRIVACY; + vap->iv_flags |= IEEE80211_F_DROPUNENC; + break; + case IEEE80211_WEP_MIXED: + vap->iv_flags |= IEEE80211_F_PRIVACY; + vap->iv_flags &= ~IEEE80211_F_DROPUNENC; + break; + } + error = ENETRESET; + break; + case IEEE80211_IOC_WEPKEY: + kid = (u_int) ireq->i_val; + if (kid >= IEEE80211_WEP_NKID) + return EINVAL; + k = &vap->iv_nw_keys[kid]; + if (ireq->i_len == 0) { + /* zero-len =>'s delete any existing key */ + (void) ieee80211_crypto_delkey(vap, k); + break; + } + if (ireq->i_len > sizeof(tmpkey)) + return EINVAL; + memset(tmpkey, 0, sizeof(tmpkey)); + error = copyin(ireq->i_data, tmpkey, ireq->i_len); + if (error) + break; + ieee80211_key_update_begin(vap); + k->wk_keyix = kid; /* NB: force fixed key id */ + if (ieee80211_crypto_newkey(vap, IEEE80211_CIPHER_WEP, + IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) { + k->wk_keylen = ireq->i_len; + memcpy(k->wk_key, tmpkey, sizeof(tmpkey)); + IEEE80211_ADDR_COPY(k->wk_macaddr, vap->iv_myaddr); + if (!ieee80211_crypto_setkey(vap, k)) + error = EINVAL; + } else + error = EINVAL; + ieee80211_key_update_end(vap); + break; + case IEEE80211_IOC_WEPTXKEY: + kid = (u_int) ireq->i_val; + if (kid >= IEEE80211_WEP_NKID && + (uint16_t) kid != IEEE80211_KEYIX_NONE) + return EINVAL; + vap->iv_def_txkey = kid; + break; + case IEEE80211_IOC_AUTHMODE: + switch (ireq->i_val) { + case IEEE80211_AUTH_WPA: + case IEEE80211_AUTH_8021X: /* 802.1x */ + case IEEE80211_AUTH_OPEN: /* open */ + case IEEE80211_AUTH_SHARED: /* shared-key */ + case IEEE80211_AUTH_AUTO: /* auto */ + auth = ieee80211_authenticator_get(ireq->i_val); + if (auth == NULL) + return EINVAL; + break; + default: + return EINVAL; + } + switch (ireq->i_val) { + case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */ + vap->iv_flags |= IEEE80211_F_PRIVACY; + ireq->i_val = IEEE80211_AUTH_8021X; + break; + case IEEE80211_AUTH_OPEN: /* open */ + vap->iv_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY); + break; + case IEEE80211_AUTH_SHARED: /* shared-key */ + case IEEE80211_AUTH_8021X: /* 802.1x */ + vap->iv_flags &= ~IEEE80211_F_WPA; + /* both require a key so mark the PRIVACY capability */ + vap->iv_flags |= IEEE80211_F_PRIVACY; + break; + case IEEE80211_AUTH_AUTO: /* auto */ + vap->iv_flags &= ~IEEE80211_F_WPA; + /* XXX PRIVACY handling? */ + /* XXX what's the right way to do this? */ + break; + } + /* NB: authenticator attach/detach happens on state change */ + vap->iv_bss->ni_authmode = ireq->i_val; + /* XXX mixed/mode/usage? */ + vap->iv_auth = auth; + error = ENETRESET; + break; + case IEEE80211_IOC_CHANNEL: + error = ieee80211_ioctl_setchannel(vap, ireq); + break; + case IEEE80211_IOC_POWERSAVE: + switch (ireq->i_val) { + case IEEE80211_POWERSAVE_OFF: + if (vap->iv_flags & IEEE80211_F_PMGTON) { + ieee80211_syncflag(vap, -IEEE80211_F_PMGTON); + error = ERESTART; + } + break; + case IEEE80211_POWERSAVE_ON: + if ((vap->iv_caps & IEEE80211_C_PMGT) == 0) + error = EOPNOTSUPP; + else if ((vap->iv_flags & IEEE80211_F_PMGTON) == 0) { + ieee80211_syncflag(vap, IEEE80211_F_PMGTON); + error = ERESTART; + } + break; + default: + error = EINVAL; + break; + } + break; + case IEEE80211_IOC_POWERSAVESLEEP: + if (ireq->i_val < 0) + return EINVAL; + ic->ic_lintval = ireq->i_val; + error = ERESTART; + break; + case IEEE80211_IOC_RTSTHRESHOLD: + if (!(IEEE80211_RTS_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_RTS_MAX)) + return EINVAL; + vap->iv_rtsthreshold = ireq->i_val; + error = ERESTART; + break; + case IEEE80211_IOC_PROTMODE: + if (ireq->i_val > IEEE80211_PROT_RTSCTS) + return EINVAL; + ic->ic_protmode = ireq->i_val; + /* NB: if not operating in 11g this can wait */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) + error = ERESTART; + break; + case IEEE80211_IOC_TXPOWER: + if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) + return EOPNOTSUPP; + if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_TXPOWER_MAX)) + return EINVAL; + ic->ic_txpowlimit = ireq->i_val; + error = ERESTART; + break; + case IEEE80211_IOC_ROAMING: + if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val && + ireq->i_val <= IEEE80211_ROAMING_MANUAL)) + return EINVAL; + vap->iv_roaming = ireq->i_val; + /* XXXX reset? */ + break; + case IEEE80211_IOC_PRIVACY: + if (ireq->i_val) { + /* XXX check for key state? */ + vap->iv_flags |= IEEE80211_F_PRIVACY; + } else + vap->iv_flags &= ~IEEE80211_F_PRIVACY; + /* XXX ERESTART? */ + break; + case IEEE80211_IOC_DROPUNENCRYPTED: + if (ireq->i_val) + vap->iv_flags |= IEEE80211_F_DROPUNENC; + else + vap->iv_flags &= ~IEEE80211_F_DROPUNENC; + /* XXX ERESTART? */ + break; + case IEEE80211_IOC_WPAKEY: + error = ieee80211_ioctl_setkey(vap, ireq); + break; + case IEEE80211_IOC_DELKEY: + error = ieee80211_ioctl_delkey(vap, ireq); + break; + case IEEE80211_IOC_MLME: + error = ieee80211_ioctl_setmlme(vap, ireq); + break; + case IEEE80211_IOC_COUNTERMEASURES: + if (ireq->i_val) { + if ((vap->iv_flags & IEEE80211_F_WPA) == 0) + return EOPNOTSUPP; + vap->iv_flags |= IEEE80211_F_COUNTERM; + } else + vap->iv_flags &= ~IEEE80211_F_COUNTERM; + /* XXX ERESTART? */ + break; + case IEEE80211_IOC_WPA: + if (ireq->i_val > 3) + return EINVAL; + /* XXX verify ciphers available */ + flags = vap->iv_flags & ~IEEE80211_F_WPA; + switch (ireq->i_val) { + case 1: + if (!(vap->iv_caps & IEEE80211_C_WPA1)) + return EOPNOTSUPP; + flags |= IEEE80211_F_WPA1; + break; + case 2: + if (!(vap->iv_caps & IEEE80211_C_WPA2)) + return EOPNOTSUPP; + flags |= IEEE80211_F_WPA2; + break; + case 3: + if ((vap->iv_caps & IEEE80211_C_WPA) != IEEE80211_C_WPA) + return EOPNOTSUPP; + flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; + break; + default: /* Can't set any -> error */ + return EOPNOTSUPP; + } + vap->iv_flags = flags; + error = ERESTART; /* NB: can change beacon frame */ + break; + case IEEE80211_IOC_WME: + if (ireq->i_val) { + if ((vap->iv_caps & IEEE80211_C_WME) == 0) + return EOPNOTSUPP; + ieee80211_syncflag(vap, IEEE80211_F_WME); + } else + ieee80211_syncflag(vap, -IEEE80211_F_WME); + error = ERESTART; /* NB: can change beacon frame */ + break; + case IEEE80211_IOC_HIDESSID: + if (ireq->i_val) + vap->iv_flags |= IEEE80211_F_HIDESSID; + else + vap->iv_flags &= ~IEEE80211_F_HIDESSID; + error = ERESTART; /* XXX ENETRESET? */ + break; + case IEEE80211_IOC_APBRIDGE: + if (ireq->i_val == 0) + vap->iv_flags |= IEEE80211_F_NOBRIDGE; + else + vap->iv_flags &= ~IEEE80211_F_NOBRIDGE; + break; + case IEEE80211_IOC_BSSID: + if (ireq->i_len != sizeof(tmpbssid)) + return EINVAL; + error = copyin(ireq->i_data, tmpbssid, ireq->i_len); + if (error) + break; + IEEE80211_ADDR_COPY(vap->iv_des_bssid, tmpbssid); + if (IEEE80211_ADDR_EQ(vap->iv_des_bssid, zerobssid)) + vap->iv_flags &= ~IEEE80211_F_DESBSSID; + else + vap->iv_flags |= IEEE80211_F_DESBSSID; + error = ENETRESET; + break; + case IEEE80211_IOC_CHANLIST: + error = ieee80211_ioctl_setchanlist(vap, ireq); + break; +#define OLD_IEEE80211_IOC_SCAN_REQ 23 +#ifdef OLD_IEEE80211_IOC_SCAN_REQ + case OLD_IEEE80211_IOC_SCAN_REQ: + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: active scan request\n", __func__); + /* + * If we are in INIT state then the driver has never + * had a chance to setup hardware state to do a scan; + * use the state machine to get us up the SCAN state. + * Otherwise just invoke the scan machinery to start + * a one-time scan. + */ + if (vap->iv_state == IEEE80211_S_INIT) + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + else + (void) ieee80211_start_scan(vap, + IEEE80211_SCAN_ACTIVE | + IEEE80211_SCAN_NOPICK | + IEEE80211_SCAN_ONCE, + IEEE80211_SCAN_FOREVER, 0, 0, + /* XXX use ioctl params */ + vap->iv_des_nssid, vap->iv_des_ssid); + break; +#endif /* OLD_IEEE80211_IOC_SCAN_REQ */ + case IEEE80211_IOC_SCAN_REQ: + error = ieee80211_ioctl_scanreq(vap, ireq); + break; + case IEEE80211_IOC_SCAN_CANCEL: + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: cancel scan\n", __func__); + ieee80211_cancel_scan(vap); + break; + case IEEE80211_IOC_HTCONF: + if (ireq->i_val & 1) + ieee80211_syncflag_ht(vap, IEEE80211_FHT_HT); + else + ieee80211_syncflag_ht(vap, -IEEE80211_FHT_HT); + if (ireq->i_val & 2) + ieee80211_syncflag_ht(vap, IEEE80211_FHT_USEHT40); + else + ieee80211_syncflag_ht(vap, -IEEE80211_FHT_USEHT40); + error = ENETRESET; + break; + case IEEE80211_IOC_ADDMAC: + case IEEE80211_IOC_DELMAC: + error = ieee80211_ioctl_macmac(vap, ireq); + break; + case IEEE80211_IOC_MACCMD: + error = ieee80211_ioctl_setmaccmd(vap, ireq); + break; + case IEEE80211_IOC_STA_STATS: + error = ieee80211_ioctl_setstastats(vap, ireq); + break; + case IEEE80211_IOC_STA_TXPOW: + error = ieee80211_ioctl_setstatxpow(vap, ireq); + break; + case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */ + case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */ + case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */ + case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */ + case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */ + case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */ + error = ieee80211_ioctl_setwmeparam(vap, ireq); + break; + case IEEE80211_IOC_DTIM_PERIOD: + if (vap->iv_opmode != IEEE80211_M_HOSTAP && + vap->iv_opmode != IEEE80211_M_MBSS && + vap->iv_opmode != IEEE80211_M_IBSS) + return EINVAL; + if (IEEE80211_DTIM_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_DTIM_MAX) { + vap->iv_dtim_period = ireq->i_val; + error = ENETRESET; /* requires restart */ + } else + error = EINVAL; + break; + case IEEE80211_IOC_BEACON_INTERVAL: + if (vap->iv_opmode != IEEE80211_M_HOSTAP && + vap->iv_opmode != IEEE80211_M_MBSS && + vap->iv_opmode != IEEE80211_M_IBSS) + return EINVAL; + if (IEEE80211_BINTVAL_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_BINTVAL_MAX) { + ic->ic_bintval = ireq->i_val; + error = ENETRESET; /* requires restart */ + } else + error = EINVAL; + break; + case IEEE80211_IOC_PUREG: + if (ireq->i_val) + vap->iv_flags |= IEEE80211_F_PUREG; + else + vap->iv_flags &= ~IEEE80211_F_PUREG; + /* NB: reset only if we're operating on an 11g channel */ + if (isvap11g(vap)) + error = ENETRESET; + break; + case IEEE80211_IOC_BGSCAN: + if (ireq->i_val) { + if ((vap->iv_caps & IEEE80211_C_BGSCAN) == 0) + return EOPNOTSUPP; + vap->iv_flags |= IEEE80211_F_BGSCAN; + } else + vap->iv_flags &= ~IEEE80211_F_BGSCAN; + break; + case IEEE80211_IOC_BGSCAN_IDLE: + if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN) + vap->iv_bgscanidle = ireq->i_val*hz/1000; + else + error = EINVAL; + break; + case IEEE80211_IOC_BGSCAN_INTERVAL: + if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN) + vap->iv_bgscanintvl = ireq->i_val*hz; + else + error = EINVAL; + break; + case IEEE80211_IOC_SCANVALID: + if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN) + vap->iv_scanvalid = ireq->i_val*hz; + else + error = EINVAL; + break; + case IEEE80211_IOC_FRAGTHRESHOLD: + if ((vap->iv_caps & IEEE80211_C_TXFRAG) == 0 && + ireq->i_val != IEEE80211_FRAG_MAX) + return EOPNOTSUPP; + if (!(IEEE80211_FRAG_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_FRAG_MAX)) + return EINVAL; + vap->iv_fragthreshold = ireq->i_val; + error = ERESTART; + break; + case IEEE80211_IOC_BURST: + if (ireq->i_val) { + if ((vap->iv_caps & IEEE80211_C_BURST) == 0) + return EOPNOTSUPP; + ieee80211_syncflag(vap, IEEE80211_F_BURST); + } else + ieee80211_syncflag(vap, -IEEE80211_F_BURST); + error = ERESTART; + break; + case IEEE80211_IOC_BMISSTHRESHOLD: + if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val && + ireq->i_val <= IEEE80211_HWBMISS_MAX)) + return EINVAL; + vap->iv_bmissthreshold = ireq->i_val; + error = ERESTART; + break; + case IEEE80211_IOC_CURCHAN: + error = ieee80211_ioctl_setcurchan(vap, ireq); + break; + case IEEE80211_IOC_SHORTGI: + if (ireq->i_val) { +#define IEEE80211_HTCAP_SHORTGI \ + (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) + if (((ireq->i_val ^ vap->iv_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0) + return EINVAL; + if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20) + vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI20; + if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40) + vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI40; +#undef IEEE80211_HTCAP_SHORTGI + } else + vap->iv_flags_ht &= + ~(IEEE80211_FHT_SHORTGI20 | IEEE80211_FHT_SHORTGI40); + error = ERESTART; + break; + case IEEE80211_IOC_AMPDU: + if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMPDU) == 0) + return EINVAL; + if (ireq->i_val & 1) + vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_TX; + else + vap->iv_flags_ht &= ~IEEE80211_FHT_AMPDU_TX; + if (ireq->i_val & 2) + vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_RX; + else + vap->iv_flags_ht &= ~IEEE80211_FHT_AMPDU_RX; + /* NB: reset only if we're operating on an 11n channel */ + if (isvapht(vap)) + error = ERESTART; + break; + case IEEE80211_IOC_AMPDU_LIMIT: + if (!(IEEE80211_HTCAP_MAXRXAMPDU_8K <= ireq->i_val && + ireq->i_val <= IEEE80211_HTCAP_MAXRXAMPDU_64K)) + return EINVAL; + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + vap->iv_ampdu_rxmax = ireq->i_val; + else + vap->iv_ampdu_limit = ireq->i_val; + error = ERESTART; + break; + case IEEE80211_IOC_AMPDU_DENSITY: + if (!(IEEE80211_HTCAP_MPDUDENSITY_NA <= ireq->i_val && + ireq->i_val <= IEEE80211_HTCAP_MPDUDENSITY_16)) + return EINVAL; + vap->iv_ampdu_density = ireq->i_val; + error = ERESTART; + break; + case IEEE80211_IOC_AMSDU: + if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMSDU) == 0) + return EINVAL; + if (ireq->i_val & 1) + vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_TX; + else + vap->iv_flags_ht &= ~IEEE80211_FHT_AMSDU_TX; + if (ireq->i_val & 2) + vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_RX; + else + vap->iv_flags_ht &= ~IEEE80211_FHT_AMSDU_RX; + /* NB: reset only if we're operating on an 11n channel */ + if (isvapht(vap)) + error = ERESTART; + break; + case IEEE80211_IOC_AMSDU_LIMIT: + /* XXX validate */ + vap->iv_amsdu_limit = ireq->i_val; /* XXX truncation? */ + break; + case IEEE80211_IOC_PUREN: + if (ireq->i_val) { + if ((vap->iv_flags_ht & IEEE80211_FHT_HT) == 0) + return EINVAL; + vap->iv_flags_ht |= IEEE80211_FHT_PUREN; + } else + vap->iv_flags_ht &= ~IEEE80211_FHT_PUREN; + /* NB: reset only if we're operating on an 11n channel */ + if (isvapht(vap)) + error = ERESTART; + break; + case IEEE80211_IOC_DOTH: + if (ireq->i_val) { +#if 0 + /* XXX no capability */ + if ((vap->iv_caps & IEEE80211_C_DOTH) == 0) + return EOPNOTSUPP; +#endif + vap->iv_flags |= IEEE80211_F_DOTH; + } else + vap->iv_flags &= ~IEEE80211_F_DOTH; + error = ENETRESET; + break; + case IEEE80211_IOC_REGDOMAIN: + error = ieee80211_ioctl_setregdomain(vap, ireq); + break; + case IEEE80211_IOC_ROAM: + error = ieee80211_ioctl_setroam(vap, ireq); + break; + case IEEE80211_IOC_TXPARAMS: + error = ieee80211_ioctl_settxparams(vap, ireq); + break; + case IEEE80211_IOC_HTCOMPAT: + if (ireq->i_val) { + if ((vap->iv_flags_ht & IEEE80211_FHT_HT) == 0) + return EOPNOTSUPP; + vap->iv_flags_ht |= IEEE80211_FHT_HTCOMPAT; + } else + vap->iv_flags_ht &= ~IEEE80211_FHT_HTCOMPAT; + /* NB: reset only if we're operating on an 11n channel */ + if (isvapht(vap)) + error = ERESTART; + break; + case IEEE80211_IOC_DWDS: + if (ireq->i_val) { + /* NB: DWDS only makes sense for WDS-capable devices */ + if ((ic->ic_caps & IEEE80211_C_WDS) == 0) + return EOPNOTSUPP; + /* NB: DWDS is used only with ap+sta vaps */ + if (vap->iv_opmode != IEEE80211_M_HOSTAP && + vap->iv_opmode != IEEE80211_M_STA) + return EINVAL; + vap->iv_flags |= IEEE80211_F_DWDS; + if (vap->iv_opmode == IEEE80211_M_STA) + vap->iv_flags_ext |= IEEE80211_FEXT_4ADDR; + } else { + vap->iv_flags &= ~IEEE80211_F_DWDS; + if (vap->iv_opmode == IEEE80211_M_STA) + vap->iv_flags_ext &= ~IEEE80211_FEXT_4ADDR; + } + break; + case IEEE80211_IOC_INACTIVITY: + if (ireq->i_val) + vap->iv_flags_ext |= IEEE80211_FEXT_INACT; + else + vap->iv_flags_ext &= ~IEEE80211_FEXT_INACT; + break; + case IEEE80211_IOC_APPIE: + error = ieee80211_ioctl_setappie(vap, ireq); + break; + case IEEE80211_IOC_WPS: + if (ireq->i_val) { + if ((vap->iv_caps & IEEE80211_C_WPA) == 0) + return EOPNOTSUPP; + vap->iv_flags_ext |= IEEE80211_FEXT_WPS; + } else + vap->iv_flags_ext &= ~IEEE80211_FEXT_WPS; + break; + case IEEE80211_IOC_TSN: + if (ireq->i_val) { + if ((vap->iv_caps & IEEE80211_C_WPA) == 0) + return EOPNOTSUPP; + vap->iv_flags_ext |= IEEE80211_FEXT_TSN; + } else + vap->iv_flags_ext &= ~IEEE80211_FEXT_TSN; + break; + case IEEE80211_IOC_CHANSWITCH: + error = ieee80211_ioctl_chanswitch(vap, ireq); + break; + case IEEE80211_IOC_DFS: + if (ireq->i_val) { + if ((vap->iv_caps & IEEE80211_C_DFS) == 0) + return EOPNOTSUPP; + /* NB: DFS requires 11h support */ + if ((vap->iv_flags & IEEE80211_F_DOTH) == 0) + return EINVAL; + vap->iv_flags_ext |= IEEE80211_FEXT_DFS; + } else + vap->iv_flags_ext &= ~IEEE80211_FEXT_DFS; + break; + case IEEE80211_IOC_DOTD: + if (ireq->i_val) + vap->iv_flags_ext |= IEEE80211_FEXT_DOTD; + else + vap->iv_flags_ext &= ~IEEE80211_FEXT_DOTD; + if (vap->iv_opmode == IEEE80211_M_STA) + error = ENETRESET; + break; + case IEEE80211_IOC_HTPROTMODE: + if (ireq->i_val > IEEE80211_PROT_RTSCTS) + return EINVAL; + ic->ic_htprotmode = ireq->i_val ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE; + /* NB: if not operating in 11n this can wait */ + if (isvapht(vap)) + error = ERESTART; + break; + case IEEE80211_IOC_STA_VLAN: + error = ieee80211_ioctl_setstavlan(vap, ireq); + break; + case IEEE80211_IOC_SMPS: + if ((ireq->i_val &~ IEEE80211_HTCAP_SMPS) != 0 || + ireq->i_val == 0x0008) /* value of 2 is reserved */ + return EINVAL; + if (ireq->i_val != IEEE80211_HTCAP_SMPS_OFF && + (vap->iv_htcaps & IEEE80211_HTC_SMPS) == 0) + return EOPNOTSUPP; + vap->iv_htcaps = (vap->iv_htcaps &~ IEEE80211_HTCAP_SMPS) | + ireq->i_val; + /* NB: if not operating in 11n this can wait */ + if (isvapht(vap)) + error = ERESTART; + break; + case IEEE80211_IOC_RIFS: + if (ireq->i_val != 0) { + if ((vap->iv_htcaps & IEEE80211_HTC_RIFS) == 0) + return EOPNOTSUPP; + vap->iv_flags_ht |= IEEE80211_FHT_RIFS; + } else + vap->iv_flags_ht &= ~IEEE80211_FHT_RIFS; + /* NB: if not operating in 11n this can wait */ + if (isvapht(vap)) + error = ERESTART; + break; + default: + error = ieee80211_ioctl_setdefault(vap, ireq); + break; + } + /* + * The convention is that ENETRESET means an operation + * requires a complete re-initialization of the device (e.g. + * changing something that affects the association state). + * ERESTART means the request may be handled with only a + * reload of the hardware state. We hand ERESTART requests + * to the iv_reset callback so the driver can decide. If + * a device does not fillin iv_reset then it defaults to one + * that returns ENETRESET. Otherwise a driver may return + * ENETRESET (in which case a full reset will be done) or + * 0 to mean there's no need to do anything (e.g. when the + * change has no effect on the driver/device). + */ + if (error == ERESTART) + error = IFNET_IS_UP_RUNNING(vap->iv_ifp) ? + vap->iv_reset(vap, ireq->i_type) : 0; + if (error == ENETRESET) { + /* XXX need to re-think AUTO handling */ + if (IS_UP_AUTO(vap)) + ieee80211_init(vap); + error = 0; + } + return error; +} + +/* + * Rebuild the parent's multicast address list after an add/del + * of a multicast address for a vap. We have no way to tell + * what happened above to optimize the work so we purge the entire + * list and rebuild from scratch. This is way expensive. + * Note also the half-baked workaround for if_addmulti calling + * back to the parent device; there's no way to insert mcast + * entries quietly and/or cheaply. + */ +static void +ieee80211_ioctl_updatemulti(struct ieee80211com *ic) +{ + struct ifnet *parent = ic->ic_ifp; + struct ieee80211vap *vap; + void *ioctl; + + IEEE80211_LOCK(ic); + if_delallmulti(parent); + ioctl = parent->if_ioctl; /* XXX WAR if_allmulti */ + parent->if_ioctl = NULL; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ifnet *ifp = vap->iv_ifp; + struct ifmultiaddr *ifma; + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + (void) if_addmulti(parent, ifma->ifma_addr, NULL); + } + } + parent->if_ioctl = ioctl; + ieee80211_runtask(ic, &ic->ic_mcast_task); + IEEE80211_UNLOCK(ic); +} + +int +ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + int error = 0; + struct ifreq *ifr; + struct ifaddr *ifa; /* XXX */ + + switch (cmd) { + case SIOCSIFFLAGS: + IEEE80211_LOCK(ic); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + ieee80211_syncifflag_locked(ic, IFF_ALLMULTI); + if (ifp->if_flags & IFF_UP) { + /* + * Bring ourself up unless we're already operational. + * If we're the first vap and the parent is not up + * then it will automatically be brought up as a + * side-effect of bringing ourself up. + */ + if (vap->iv_state == IEEE80211_S_INIT) + ieee80211_start_locked(vap); + } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + /* + * Stop ourself. If we are the last vap to be + * marked down the parent will also be taken down. + */ + ieee80211_stop_locked(vap); + } + IEEE80211_UNLOCK(ic); + /* Wait for parent ioctl handler if it was queued */ + ieee80211_waitfor_parent(ic); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + ieee80211_ioctl_updatemulti(ic); + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + ifr = (struct ifreq *)data; + error = ifmedia_ioctl(ifp, ifr, &vap->iv_media, cmd); + break; + case SIOCG80211: + error = ieee80211_ioctl_get80211(vap, cmd, + (struct ieee80211req *) data); + break; + case SIOCS80211: + error = priv_check(curthread, PRIV_NET80211_MANAGE); + if (error == 0) + error = ieee80211_ioctl_set80211(vap, cmd, + (struct ieee80211req *) data); + break; + case SIOCG80211STATS: + ifr = (struct ifreq *)data; + copyout(&vap->iv_stats, ifr->ifr_data, sizeof (vap->iv_stats)); + break; + case SIOCSIFMTU: + ifr = (struct ifreq *)data; + if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu && + ifr->ifr_mtu <= IEEE80211_MTU_MAX)) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + case SIOCSIFADDR: + /* + * XXX Handle this directly so we can supress if_init calls. + * XXX This should be done in ether_ioctl but for the moment + * XXX there are too many other parts of the system that + * XXX set IFF_UP and so supress if_init being called when + * XXX it should be. + */ + ifa = (struct ifaddr *) data; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + if ((ifp->if_flags & IFF_UP) == 0) { + ifp->if_flags |= IFF_UP; + ifp->if_init(ifp->if_softc); + } + arp_ifinit(ifp, ifa); + break; +#endif +#ifdef IPX + /* + * XXX - This code is probably wrong, + * but has been copied many times. + */ + case AF_IPX: { + struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); + + if (ipx_nullhost(*ina)) + ina->x_host = *(union ipx_host *) + IF_LLADDR(ifp); + else + bcopy((caddr_t) ina->x_host.c_host, + (caddr_t) IF_LLADDR(ifp), + ETHER_ADDR_LEN); + /* fall thru... */ + } +#endif + default: + if ((ifp->if_flags & IFF_UP) == 0) { + ifp->if_flags |= IFF_UP; + ifp->if_init(ifp->if_softc); + } + break; + } + break; + /* Pass NDIS ioctls up to the driver */ + case SIOCGDRVSPEC: + case SIOCSDRVSPEC: + case SIOCGPRIVATE_0: { + struct ifnet *parent = vap->iv_ic->ic_ifp; + error = parent->if_ioctl(parent, cmd, data); + break; + } + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + return error; +} diff --git a/freebsd/sys/net80211/ieee80211_ioctl.h b/freebsd/sys/net80211/ieee80211_ioctl.h new file mode 100644 index 00000000..109a27fb --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_ioctl.h @@ -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. + * + * $FreeBSD$ + */ +#ifndef _NET80211_IEEE80211_IOCTL_HH_ +#define _NET80211_IEEE80211_IOCTL_HH_ + +/* + * IEEE 802.11 ioctls. + */ +#include <freebsd/net80211/_ieee80211.h> +#include <freebsd/net80211/ieee80211.h> +#include <freebsd/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 */ + uint16_t isi_freq; /* MHz */ + uint32_t isi_flags; /* channel flags */ + 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; +}; + +#ifdef __FreeBSD__ +/* + * 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 */ + uint16_t isr_freq; /* MHz */ + uint16_t isr_flags; /* channel flags */ + 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 /* _NET80211_IEEE80211_IOCTL_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_mesh.c b/freebsd/sys/net80211/ieee80211_mesh.c new file mode 100644 index 00000000..06bac89a --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_mesh.c @@ -0,0 +1,2755 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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. + */ +#include <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11s Mesh Point (MBSS) support. + * + * Based on March 2009, D3.0 802.11s draft spec. + */ +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/errno.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_action.h> +#include <freebsd/net80211/ieee80211_input.h> +#include <freebsd/net80211/ieee80211_mesh.h> + +static void mesh_rt_flush_invalid(struct ieee80211vap *); +static int mesh_select_proto_path(struct ieee80211vap *, const char *); +static int mesh_select_proto_metric(struct ieee80211vap *, const char *); +static void mesh_vattach(struct ieee80211vap *); +static int mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static void mesh_rt_cleanup_cb(void *); +static void mesh_linkchange(struct ieee80211_node *, + enum ieee80211_mesh_mlstate); +static void mesh_checkid(void *, struct ieee80211_node *); +static uint32_t mesh_generateid(struct ieee80211vap *); +static int mesh_checkpseq(struct ieee80211vap *, + const uint8_t [IEEE80211_ADDR_LEN], uint32_t); +static struct ieee80211_node * + mesh_find_txnode(struct ieee80211vap *, + const uint8_t [IEEE80211_ADDR_LEN]); +static void mesh_forward(struct ieee80211vap *, struct mbuf *, + const struct ieee80211_meshcntl *); +static int mesh_input(struct ieee80211_node *, struct mbuf *, int, int); +static void mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, + int, int); +static void mesh_peer_timeout_setup(struct ieee80211_node *); +static void mesh_peer_timeout_backoff(struct ieee80211_node *); +static void mesh_peer_timeout_cb(void *); +static __inline void + mesh_peer_timeout_stop(struct ieee80211_node *); +static int mesh_verify_meshid(struct ieee80211vap *, const uint8_t *); +static int mesh_verify_meshconf(struct ieee80211vap *, const uint8_t *); +static int mesh_verify_meshpeer(struct ieee80211vap *, uint8_t, + const uint8_t *); +uint32_t mesh_airtime_calc(struct ieee80211_node *); + +/* + * Timeout values come from the specification and are in milliseconds. + */ +SYSCTL_NODE(_net_wlan, OID_AUTO, mesh, CTLFLAG_RD, 0, + "IEEE 802.11s parameters"); +static int ieee80211_mesh_retrytimeout = -1; +SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, retrytimeout, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_mesh_retrytimeout, 0, ieee80211_sysctl_msecs_ticks, "I", + "Retry timeout (msec)"); +static int ieee80211_mesh_holdingtimeout = -1; +SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, holdingtimeout, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_mesh_holdingtimeout, 0, ieee80211_sysctl_msecs_ticks, "I", + "Holding state timeout (msec)"); +static int ieee80211_mesh_confirmtimeout = -1; +SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, confirmtimeout, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_mesh_confirmtimeout, 0, ieee80211_sysctl_msecs_ticks, "I", + "Confirm state timeout (msec)"); +static int ieee80211_mesh_maxretries = 2; +SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxretries, CTLTYPE_INT | CTLFLAG_RW, + &ieee80211_mesh_maxretries, 0, + "Maximum retries during peer link establishment"); + +static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static ieee80211_recv_action_func mesh_recv_action_meshpeering_open; +static ieee80211_recv_action_func mesh_recv_action_meshpeering_confirm; +static ieee80211_recv_action_func mesh_recv_action_meshpeering_close; +static ieee80211_recv_action_func mesh_recv_action_meshlmetric_req; +static ieee80211_recv_action_func mesh_recv_action_meshlmetric_rep; + +static ieee80211_send_action_func mesh_send_action_meshpeering_open; +static ieee80211_send_action_func mesh_send_action_meshpeering_confirm; +static ieee80211_send_action_func mesh_send_action_meshpeering_close; +static ieee80211_send_action_func mesh_send_action_meshlink_request; +static ieee80211_send_action_func mesh_send_action_meshlink_reply; + +static const struct ieee80211_mesh_proto_metric mesh_metric_airtime = { + .mpm_descr = "AIRTIME", + .mpm_ie = IEEE80211_MESHCONF_METRIC_AIRTIME, + .mpm_metric = mesh_airtime_calc, +}; + +static struct ieee80211_mesh_proto_path mesh_proto_paths[4]; +static struct ieee80211_mesh_proto_metric mesh_proto_metrics[4]; + +#define MESH_RT_LOCK(ms) mtx_lock(&(ms)->ms_rt_lock) +#define MESH_RT_LOCK_ASSERT(ms) mtx_assert(&(ms)->ms_rt_lock, MA_OWNED) +#define MESH_RT_UNLOCK(ms) mtx_unlock(&(ms)->ms_rt_lock) + +MALLOC_DEFINE(M_80211_MESH_RT, "80211mesh", "802.11s routing table"); + +/* + * Helper functions to manipulate the Mesh routing table. + */ + +static struct ieee80211_mesh_route * +mesh_rt_find_locked(struct ieee80211_mesh_state *ms, + const uint8_t dest[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_route *rt; + + MESH_RT_LOCK_ASSERT(ms); + + TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) { + if (IEEE80211_ADDR_EQ(dest, rt->rt_dest)) + return rt; + } + return NULL; +} + +static struct ieee80211_mesh_route * +mesh_rt_add_locked(struct ieee80211_mesh_state *ms, + const uint8_t dest[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_route *rt; + + KASSERT(!IEEE80211_ADDR_EQ(broadcastaddr, dest), + ("%s: adding broadcast to the routing table", __func__)); + + MESH_RT_LOCK_ASSERT(ms); + + rt = malloc(ALIGN(sizeof(struct ieee80211_mesh_route)) + + ms->ms_ppath->mpp_privlen, M_80211_MESH_RT, M_NOWAIT | M_ZERO); + if (rt != NULL) { + IEEE80211_ADDR_COPY(rt->rt_dest, dest); + rt->rt_priv = (void *)ALIGN(&rt[1]); + rt->rt_crtime = ticks; + TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next); + } + return rt; +} + +struct ieee80211_mesh_route * +ieee80211_mesh_rt_find(struct ieee80211vap *vap, + const uint8_t dest[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt; + + MESH_RT_LOCK(ms); + rt = mesh_rt_find_locked(ms, dest); + MESH_RT_UNLOCK(ms); + return rt; +} + +struct ieee80211_mesh_route * +ieee80211_mesh_rt_add(struct ieee80211vap *vap, + const uint8_t dest[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt; + + KASSERT(ieee80211_mesh_rt_find(vap, dest) == NULL, + ("%s: duplicate entry in the routing table", __func__)); + KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest), + ("%s: adding self to the routing table", __func__)); + + MESH_RT_LOCK(ms); + rt = mesh_rt_add_locked(ms, dest); + MESH_RT_UNLOCK(ms); + return rt; +} + +/* + * Add a proxy route (as needed) for the specified destination. + */ +void +ieee80211_mesh_proxy_check(struct ieee80211vap *vap, + const uint8_t dest[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt; + + MESH_RT_LOCK(ms); + rt = mesh_rt_find_locked(ms, dest); + if (rt == NULL) { + rt = mesh_rt_add_locked(ms, dest); + if (rt == NULL) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, + "%s", "unable to add proxy entry"); + vap->iv_stats.is_mesh_rtaddfailed++; + } else { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, + "%s", "add proxy entry"); + IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr); + rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID + | IEEE80211_MESHRT_FLAGS_PROXY; + } + /* XXX assert PROXY? */ + } else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { + struct ieee80211com *ic = vap->iv_ic; + /* + * Fix existing entry created by received frames from + * stations that have some memory of dest. We also + * flush any frames held on the staging queue; delivering + * them is too much trouble right now. + */ + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, + "%s", "fix proxy entry"); + IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr); + rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID + | IEEE80211_MESHRT_FLAGS_PROXY; + /* XXX belongs in hwmp */ + ieee80211_ageq_drain_node(&ic->ic_stageq, + (void *)(uintptr_t) ieee80211_mac_hash(ic, dest)); + /* XXX stat? */ + } + MESH_RT_UNLOCK(ms); +} + +static __inline void +mesh_rt_del(struct ieee80211_mesh_state *ms, struct ieee80211_mesh_route *rt) +{ + TAILQ_REMOVE(&ms->ms_routes, rt, rt_next); + free(rt, M_80211_MESH_RT); +} + +void +ieee80211_mesh_rt_del(struct ieee80211vap *vap, + const uint8_t dest[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt, *next; + + MESH_RT_LOCK(ms); + TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) { + if (IEEE80211_ADDR_EQ(rt->rt_dest, dest)) { + mesh_rt_del(ms, rt); + MESH_RT_UNLOCK(ms); + return; + } + } + MESH_RT_UNLOCK(ms); +} + +void +ieee80211_mesh_rt_flush(struct ieee80211vap *vap) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt, *next; + + if (ms == NULL) + return; + MESH_RT_LOCK(ms); + TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) + mesh_rt_del(ms, rt); + MESH_RT_UNLOCK(ms); +} + +void +ieee80211_mesh_rt_flush_peer(struct ieee80211vap *vap, + const uint8_t peer[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt, *next; + + MESH_RT_LOCK(ms); + TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) { + if (IEEE80211_ADDR_EQ(rt->rt_nexthop, peer)) + mesh_rt_del(ms, rt); + } + MESH_RT_UNLOCK(ms); +} + +/* + * Flush expired routing entries, i.e. those in invalid state for + * some time. + */ +static void +mesh_rt_flush_invalid(struct ieee80211vap *vap) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt, *next; + + if (ms == NULL) + return; + MESH_RT_LOCK(ms); + TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) { + if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 && + ticks - rt->rt_crtime >= ms->ms_ppath->mpp_inact) + mesh_rt_del(ms, rt); + } + MESH_RT_UNLOCK(ms); +} + +#define N(a) (sizeof(a) / sizeof(a[0])) +int +ieee80211_mesh_register_proto_path(const struct ieee80211_mesh_proto_path *mpp) +{ + int i, firstempty = -1; + + for (i = 0; i < N(mesh_proto_paths); i++) { + if (strncmp(mpp->mpp_descr, mesh_proto_paths[i].mpp_descr, + IEEE80211_MESH_PROTO_DSZ) == 0) + return EEXIST; + if (!mesh_proto_paths[i].mpp_active && firstempty == -1) + firstempty = i; + } + if (firstempty < 0) + return ENOSPC; + memcpy(&mesh_proto_paths[firstempty], mpp, sizeof(*mpp)); + mesh_proto_paths[firstempty].mpp_active = 1; + return 0; +} + +int +ieee80211_mesh_register_proto_metric(const struct + ieee80211_mesh_proto_metric *mpm) +{ + int i, firstempty = -1; + + for (i = 0; i < N(mesh_proto_metrics); i++) { + if (strncmp(mpm->mpm_descr, mesh_proto_metrics[i].mpm_descr, + IEEE80211_MESH_PROTO_DSZ) == 0) + return EEXIST; + if (!mesh_proto_metrics[i].mpm_active && firstempty == -1) + firstempty = i; + } + if (firstempty < 0) + return ENOSPC; + memcpy(&mesh_proto_metrics[firstempty], mpm, sizeof(*mpm)); + mesh_proto_metrics[firstempty].mpm_active = 1; + return 0; +} + +static int +mesh_select_proto_path(struct ieee80211vap *vap, const char *name) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + int i; + + for (i = 0; i < N(mesh_proto_paths); i++) { + if (strcasecmp(mesh_proto_paths[i].mpp_descr, name) == 0) { + ms->ms_ppath = &mesh_proto_paths[i]; + return 0; + } + } + return ENOENT; +} + +static int +mesh_select_proto_metric(struct ieee80211vap *vap, const char *name) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + int i; + + for (i = 0; i < N(mesh_proto_metrics); i++) { + if (strcasecmp(mesh_proto_metrics[i].mpm_descr, name) == 0) { + ms->ms_pmetric = &mesh_proto_metrics[i]; + return 0; + } + } + return ENOENT; +} +#undef N + +static void +ieee80211_mesh_init(void) +{ + + memset(mesh_proto_paths, 0, sizeof(mesh_proto_paths)); + memset(mesh_proto_metrics, 0, sizeof(mesh_proto_metrics)); + + /* + * Setup mesh parameters that depends on the clock frequency. + */ + ieee80211_mesh_retrytimeout = msecs_to_ticks(40); + ieee80211_mesh_holdingtimeout = msecs_to_ticks(40); + ieee80211_mesh_confirmtimeout = msecs_to_ticks(40); + + /* + * Register action frame handlers. + */ + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_OPEN, + mesh_recv_action_meshpeering_open); + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CONFIRM, + mesh_recv_action_meshpeering_confirm); + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + mesh_recv_action_meshpeering_close); + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC, + IEEE80211_ACTION_MESHLMETRIC_REQ, mesh_recv_action_meshlmetric_req); + ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC, + IEEE80211_ACTION_MESHLMETRIC_REP, mesh_recv_action_meshlmetric_rep); + + ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_OPEN, + mesh_send_action_meshpeering_open); + ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CONFIRM, + mesh_send_action_meshpeering_confirm); + ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + mesh_send_action_meshpeering_close); + ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC, + IEEE80211_ACTION_MESHLMETRIC_REQ, + mesh_send_action_meshlink_request); + ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHLMETRIC, + IEEE80211_ACTION_MESHLMETRIC_REP, + mesh_send_action_meshlink_reply); + + /* + * Register Airtime Link Metric. + */ + ieee80211_mesh_register_proto_metric(&mesh_metric_airtime); + +} +SYSINIT(wlan_mesh, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_mesh_init, NULL); + +void +ieee80211_mesh_attach(struct ieee80211com *ic) +{ + ic->ic_vattach[IEEE80211_M_MBSS] = mesh_vattach; +} + +void +ieee80211_mesh_detach(struct ieee80211com *ic) +{ +} + +static void +mesh_vdetach_peers(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + uint16_t args[3]; + + if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED) { + args[0] = ni->ni_mlpid; + args[1] = ni->ni_mllid; + args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + args); + } + callout_drain(&ni->ni_mltimer); + /* XXX belongs in hwmp */ + ieee80211_ageq_drain_node(&ic->ic_stageq, + (void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr)); +} + +static void +mesh_vdetach(struct ieee80211vap *vap) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + + callout_drain(&ms->ms_cleantimer); + ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_vdetach_peers, + NULL); + ieee80211_mesh_rt_flush(vap); + mtx_destroy(&ms->ms_rt_lock); + ms->ms_ppath->mpp_vdetach(vap); + free(vap->iv_mesh, M_80211_VAP); + vap->iv_mesh = NULL; +} + +static void +mesh_vattach(struct ieee80211vap *vap) +{ + struct ieee80211_mesh_state *ms; + vap->iv_newstate = mesh_newstate; + vap->iv_input = mesh_input; + vap->iv_opdetach = mesh_vdetach; + vap->iv_recv_mgmt = mesh_recv_mgmt; + ms = malloc(sizeof(struct ieee80211_mesh_state), M_80211_VAP, + M_NOWAIT | M_ZERO); + if (ms == NULL) { + printf("%s: couldn't alloc MBSS state\n", __func__); + return; + } + vap->iv_mesh = ms; + ms->ms_seq = 0; + ms->ms_flags = (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_FWD); + ms->ms_ttl = IEEE80211_MESH_DEFAULT_TTL; + TAILQ_INIT(&ms->ms_routes); + mtx_init(&ms->ms_rt_lock, "MBSS", "802.11s routing table", MTX_DEF); + callout_init(&ms->ms_cleantimer, CALLOUT_MPSAFE); + mesh_select_proto_metric(vap, "AIRTIME"); + KASSERT(ms->ms_pmetric, ("ms_pmetric == NULL")); + mesh_select_proto_path(vap, "HWMP"); + KASSERT(ms->ms_ppath, ("ms_ppath == NULL")); + ms->ms_ppath->mpp_vattach(vap); +} + +/* + * IEEE80211_M_MBSS vap state machine handler. + */ +static int +mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + 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 (ostate != IEEE80211_S_SCAN) + ieee80211_cancel_scan(vap); /* background scan */ + ni = vap->iv_bss; /* NB: no reference held */ + if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) + callout_drain(&ms->ms_cleantimer); + switch (nstate) { + case IEEE80211_S_INIT: + switch (ostate) { + case IEEE80211_S_SCAN: + ieee80211_cancel_scan(vap); + break; + case IEEE80211_S_CAC: + ieee80211_dfs_cac_stop(vap); + break; + case IEEE80211_S_RUN: + ieee80211_iterate_nodes(&ic->ic_sta, + mesh_vdetach_peers, NULL); + break; + default: + break; + } + if (ostate != IEEE80211_S_INIT) { + /* NB: optimize INIT -> INIT case */ + ieee80211_reset_bss(vap); + ieee80211_mesh_rt_flush(vap); + } + break; + case IEEE80211_S_SCAN: + switch (ostate) { + case IEEE80211_S_INIT: + if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && + !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan) && + ms->ms_idlen != 0) { + /* + * Already have a channel and a mesh ID; 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; + default: + break; + } + break; + case IEEE80211_S_CAC: + /* + * Start CAC on a DFS channel. We come here when starting + * a bss on a DFS channel (see ieee80211_create_ibss). + */ + ieee80211_dfs_cac_start(vap); + break; + case IEEE80211_S_RUN: + switch (ostate) { + case IEEE80211_S_INIT: + /* + * Already have a channel; bypass the + * scan and startup immediately. + * Note that ieee80211_create_ibss will call + * back to do a RUN->RUN state change. + */ + ieee80211_create_ibss(vap, + ieee80211_ht_adjust_channel(ic, + ic->ic_curchan, vap->iv_flags_ht)); + /* NB: iv_bss is changed on return */ + break; + case IEEE80211_S_CAC: + /* + * NB: This is the normal state change when CAC + * expires and no radar was detected; no need to + * clear the CAC timer as it's already expired. + */ + /* fall thru... */ + case IEEE80211_S_CSA: +#if 0 + /* + * Shorten inactivity timer of associated stations + * to weed out sta's that don't follow a CSA. + */ + ieee80211_iterate_nodes(&ic->ic_sta, sta_csa, vap); +#endif + /* + * Update bss node channel to reflect where + * we landed after CSA. + */ + ieee80211_node_set_chan(vap->iv_bss, + ieee80211_ht_adjust_channel(ic, ic->ic_curchan, + ieee80211_htchanflags(vap->iv_bss->ni_chan))); + /* XXX bypass debug msgs */ + break; + case IEEE80211_S_SCAN: + case IEEE80211_S_RUN: +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_debug(vap)) { + struct ieee80211_node *ni = vap->iv_bss; + ieee80211_note(vap, + "synchronized with %s meshid ", + ether_sprintf(ni->ni_meshid)); + ieee80211_print_essid(ni->ni_meshid, + ni->ni_meshidlen); + /* XXX MCS/HT */ + printf(" channel %d\n", + ieee80211_chan2ieee(ic, ic->ic_curchan)); + } +#endif + break; + default: + break; + } + ieee80211_node_authorize(vap->iv_bss); + callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact, + mesh_rt_cleanup_cb, vap); + break; + default: + break; + } + /* NB: ostate not nstate */ + ms->ms_ppath->mpp_newstate(vap, ostate, arg); + return 0; +} + +static void +mesh_rt_cleanup_cb(void *arg) +{ + struct ieee80211vap *vap = arg; + struct ieee80211_mesh_state *ms = vap->iv_mesh; + + mesh_rt_flush_invalid(vap); + callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact, + mesh_rt_cleanup_cb, vap); +} + + +/* + * Helper function to note the Mesh Peer Link FSM change. + */ +static void +mesh_linkchange(struct ieee80211_node *ni, enum ieee80211_mesh_mlstate state) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_mesh_state *ms = vap->iv_mesh; +#ifdef IEEE80211_DEBUG + static const char *meshlinkstates[] = { + [IEEE80211_NODE_MESH_IDLE] = "IDLE", + [IEEE80211_NODE_MESH_OPENSNT] = "OPEN SENT", + [IEEE80211_NODE_MESH_OPENRCV] = "OPEN RECEIVED", + [IEEE80211_NODE_MESH_CONFIRMRCV] = "CONFIRM RECEIVED", + [IEEE80211_NODE_MESH_ESTABLISHED] = "ESTABLISHED", + [IEEE80211_NODE_MESH_HOLDING] = "HOLDING" + }; +#endif + IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, + ni, "peer link: %s -> %s", + meshlinkstates[ni->ni_mlstate], meshlinkstates[state]); + + /* track neighbor count */ + if (state == IEEE80211_NODE_MESH_ESTABLISHED && + ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) { + KASSERT(ms->ms_neighbors < 65535, ("neighbor count overflow")); + ms->ms_neighbors++; + ieee80211_beacon_notify(vap, IEEE80211_BEACON_MESHCONF); + } else if (ni->ni_mlstate == IEEE80211_NODE_MESH_ESTABLISHED && + state != IEEE80211_NODE_MESH_ESTABLISHED) { + KASSERT(ms->ms_neighbors > 0, ("neighbor count 0")); + ms->ms_neighbors--; + ieee80211_beacon_notify(vap, IEEE80211_BEACON_MESHCONF); + } + ni->ni_mlstate = state; + switch (state) { + case IEEE80211_NODE_MESH_HOLDING: + ms->ms_ppath->mpp_peerdown(ni); + break; + case IEEE80211_NODE_MESH_ESTABLISHED: + ieee80211_mesh_discover(vap, ni->ni_macaddr, NULL); + break; + default: + break; + } +} + +/* + * Helper function to generate a unique local ID required for mesh + * peer establishment. + */ +static void +mesh_checkid(void *arg, struct ieee80211_node *ni) +{ + uint16_t *r = arg; + + if (*r == ni->ni_mllid) + *(uint16_t *)arg = 0; +} + +static uint32_t +mesh_generateid(struct ieee80211vap *vap) +{ + int maxiter = 4; + uint16_t r; + + do { + get_random_bytes(&r, 2); + ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_checkid, &r); + maxiter--; + } while (r == 0 && maxiter > 0); + return r; +} + +/* + * Verifies if we already received this packet by checking its + * sequence number. + * Returns 0 if the frame is to be accepted, 1 otherwise. + */ +static int +mesh_checkpseq(struct ieee80211vap *vap, + const uint8_t source[IEEE80211_ADDR_LEN], uint32_t seq) +{ + struct ieee80211_mesh_route *rt; + + rt = ieee80211_mesh_rt_find(vap, source); + if (rt == NULL) { + rt = ieee80211_mesh_rt_add(vap, source); + if (rt == NULL) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source, + "%s", "add mcast route failed"); + vap->iv_stats.is_mesh_rtaddfailed++; + return 1; + } + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source, + "add mcast route, mesh seqno %d", seq); + rt->rt_lastmseq = seq; + return 0; + } + if (IEEE80211_MESH_SEQ_GEQ(rt->rt_lastmseq, seq)) { + return 1; + } else { + rt->rt_lastmseq = seq; + return 0; + } +} + +/* + * Iterate the routing table and locate the next hop. + */ +static struct ieee80211_node * +mesh_find_txnode(struct ieee80211vap *vap, + const uint8_t dest[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_route *rt; + + rt = ieee80211_mesh_rt_find(vap, dest); + if (rt == NULL) + return NULL; + if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || + (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, + "%s: !valid or proxy, flags 0x%x", __func__, rt->rt_flags); + /* XXX stat */ + return NULL; + } + return ieee80211_find_txnode(vap, rt->rt_nexthop); +} + +/* + * Forward the specified frame. + * Decrement the TTL and set TA to our MAC address. + */ +static void +mesh_forward(struct ieee80211vap *vap, struct mbuf *m, + const struct ieee80211_meshcntl *mc) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ifnet *ifp = vap->iv_ifp; + struct ifnet *parent = ic->ic_ifp; + const struct ieee80211_frame *wh = + mtod(m, const struct ieee80211_frame *); + struct mbuf *mcopy; + struct ieee80211_meshcntl *mccopy; + struct ieee80211_frame *whcopy; + struct ieee80211_node *ni; + int err; + + if (mc->mc_ttl == 0) { + IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, + "%s", "frame not fwd'd, ttl 0"); + vap->iv_stats.is_mesh_fwd_ttl++; + return; + } + if (!(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) { + IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, + "%s", "frame not fwd'd, fwding disabled"); + vap->iv_stats.is_mesh_fwd_disabled++; + return; + } + mcopy = m_dup(m, M_DONTWAIT); + if (mcopy == NULL) { + IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, + "%s", "frame not fwd'd, cannot dup"); + vap->iv_stats.is_mesh_fwd_nobuf++; + ifp->if_oerrors++; + return; + } + mcopy = m_pullup(mcopy, ieee80211_hdrspace(ic, wh) + + sizeof(struct ieee80211_meshcntl)); + if (mcopy == NULL) { + IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, + "%s", "frame not fwd'd, too short"); + vap->iv_stats.is_mesh_fwd_tooshort++; + ifp->if_oerrors++; + m_freem(mcopy); + return; + } + whcopy = mtod(mcopy, struct ieee80211_frame *); + mccopy = (struct ieee80211_meshcntl *) + (mtod(mcopy, uint8_t *) + ieee80211_hdrspace(ic, wh)); + /* XXX clear other bits? */ + whcopy->i_fc[1] &= ~IEEE80211_FC1_RETRY; + IEEE80211_ADDR_COPY(whcopy->i_addr2, vap->iv_myaddr); + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { + ni = ieee80211_ref_node(vap->iv_bss); + mcopy->m_flags |= M_MCAST; + } else { + ni = mesh_find_txnode(vap, whcopy->i_addr3); + if (ni == NULL) { + IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, + "%s", "frame not fwd'd, no path"); + vap->iv_stats.is_mesh_fwd_nopath++; + m_freem(mcopy); + return; + } + IEEE80211_ADDR_COPY(whcopy->i_addr1, ni->ni_macaddr); + } + KASSERT(mccopy->mc_ttl > 0, ("%s called with wrong ttl", __func__)); + mccopy->mc_ttl--; + + /* XXX calculate priority so drivers can find the tx queue */ + M_WME_SETAC(mcopy, WME_AC_BE); + + /* XXX do we know m_nextpkt is NULL? */ + mcopy->m_pkthdr.rcvif = (void *) ni; + err = parent->if_transmit(parent, mcopy); + if (err != 0) { + /* NB: IFQ_HANDOFF reclaims mbuf */ + ieee80211_free_node(ni); + } else { + ifp->if_opackets++; + } +} + +static struct mbuf * +mesh_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, int meshdrlen) +{ +#define WHDIR(wh) ((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK) + uint8_t b[sizeof(struct ieee80211_qosframe_addr4) + + sizeof(struct ieee80211_meshcntl_ae11)]; + const struct ieee80211_qosframe_addr4 *wh; + const struct ieee80211_meshcntl_ae10 *mc; + struct ether_header *eh; + struct llc *llc; + int ae; + + if (m->m_len < hdrlen + sizeof(*llc) && + (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, + "discard data frame: %s", "m_pullup failed"); + vap->iv_stats.is_rx_tooshort++; + return NULL; + } + memcpy(b, mtod(m, caddr_t), hdrlen); + wh = (const struct ieee80211_qosframe_addr4 *)&b[0]; + mc = (const struct ieee80211_meshcntl_ae10 *)&b[hdrlen - meshdrlen]; + KASSERT(WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS || + WHDIR(wh) == IEEE80211_FC1_DIR_DSTODS, + ("bogus dir, fc 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1])); + + 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 *); + ae = mc->mc_flags & 3; + if (WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS) { + IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr1); + if (ae == 0) { + IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr3); + } else if (ae == 1) { + IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr4); + } else { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + (const struct ieee80211_frame *)wh, NULL, + "bad AE %d", ae); + vap->iv_stats.is_mesh_badae++; + m_freem(m); + return NULL; + } + } else { + if (ae == 0) { + IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr3); + IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr4); + } else if (ae == 2) { + IEEE80211_ADDR_COPY(eh->ether_dhost, mc->mc_addr4); + IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr5); + } else { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + (const struct ieee80211_frame *)wh, NULL, + "bad AE %d", ae); + vap->iv_stats.is_mesh_badae++; + m_freem(m); + return NULL; + } + } +#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; +#undef WDIR +} + +/* + * Return non-zero if the unicast mesh data frame should be processed + * locally. Frames that are not proxy'd have our address, otherwise + * we need to consult the routing table to look for a proxy entry. + */ +static __inline int +mesh_isucastforme(struct ieee80211vap *vap, const struct ieee80211_frame *wh, + const struct ieee80211_meshcntl *mc) +{ + int ae = mc->mc_flags & 3; + + KASSERT((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS, + ("bad dir 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1])); + KASSERT(ae == 0 || ae == 2, ("bad AE %d", ae)); + if (ae == 2) { /* ucast w/ proxy */ + const struct ieee80211_meshcntl_ae10 *mc10 = + (const struct ieee80211_meshcntl_ae10 *) mc; + struct ieee80211_mesh_route *rt = + ieee80211_mesh_rt_find(vap, mc10->mc_addr4); + /* check for proxy route to ourself */ + return (rt != NULL && + (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)); + } else /* ucast w/o proxy */ + return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr); +} + +static int +mesh_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; + const struct ieee80211_meshcntl *mc; + int hdrspace, meshdrlen, need_tap; + uint8_t dir, type, subtype, qos; + uint32_t seq; + uint8_t *addr; + ieee80211_seq rxseq; + + 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 %x", wh->i_fc[0]); + 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) { + 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; + } + } +#ifdef IEEE80211_DEBUG + /* + * It's easier, but too expensive, to simulate different mesh + * topologies by consulting the ACL policy very early, so do this + * only under DEBUG. + * + * NB: this check is also done upon peering link initiation. + */ + if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh->i_addr2)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL, + wh, NULL, "%s", "disallowed by ACL"); + vap->iv_stats.is_rx_acl++; + goto out; + } +#endif + switch (type) { + case IEEE80211_FC0_TYPE_DATA: + if (ni == vap->iv_bss) + goto out; + if (ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH, + ni->ni_macaddr, NULL, + "peer link not yet established (%d)", + ni->ni_mlstate); + vap->iv_stats.is_mesh_nolink++; + goto out; + } + if (dir != IEEE80211_FC1_DIR_FROMDS && + 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 err; + } + /* pull up enough to get to the mesh control */ + hdrspace = ieee80211_hdrspace(ic, wh); + if (m->m_len < hdrspace + sizeof(struct ieee80211_meshcntl) && + (m = m_pullup(m, hdrspace + + sizeof(struct ieee80211_meshcntl))) == 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 */ + } + /* + * Now calculate the full extent of the headers. Note + * mesh_decap will pull up anything we didn't get + * above when it strips the 802.11 headers. + */ + mc = (const struct ieee80211_meshcntl *) + (mtod(m, const uint8_t *) + hdrspace); + meshdrlen = sizeof(struct ieee80211_meshcntl) + + (mc->mc_flags & 3) * IEEE80211_ADDR_LEN; + hdrspace += meshdrlen; + seq = LE_READ_4(mc->mc_seq); + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + addr = wh->i_addr3; + else + addr = ((struct ieee80211_qosframe_addr4 *)wh)->i_addr4; + if (IEEE80211_ADDR_EQ(vap->iv_myaddr, addr)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + addr, "data", "%s", "not to me"); + vap->iv_stats.is_rx_wrongbss++; /* XXX kinda */ + goto out; + } + if (mesh_checkpseq(vap, addr, seq) != 0) { + vap->iv_stats.is_rx_dup++; + goto out; + } + + /* + * Potentially forward packet. See table s36 (p140) + * for the rules. XXX tap fwd'd packets not for us? + */ + if (dir == IEEE80211_FC1_DIR_FROMDS || + !mesh_isucastforme(vap, wh, mc)) { + mesh_forward(vap, m, mc); + if (dir == IEEE80211_FC1_DIR_DSTODS) + goto out; + /* NB: fall thru to deliver mcast frames locally */ + } + + /* + * 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 */ + + if (ieee80211_radiotap_active_vap(vap)) + ieee80211_radiotap_rx(vap, m); + need_tap = 0; + + /* + * Finally, strip the 802.11 header. + */ + m = mesh_decap(vap, m, hdrspace, meshdrlen); + 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; + } + if (qos & IEEE80211_QOS_AMSDU) { + m = ieee80211_decap_amsdu(ni, m); + if (m == NULL) + return IEEE80211_FC0_TYPE_DATA; + } + ieee80211_deliver_data(vap, ni, m); + return type; + 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, "mgt", "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) && + (vap->iv_ic->ic_flags & IEEE80211_F_SCAN)) || + 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; +} + +static void +mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, + int rssi, int nf) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + uint8_t *frm, *efrm; + + 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; + } + + /* The rest of this code assumes we are running */ + if (vap->iv_state != IEEE80211_S_RUN) + return; + /* + * Ignore non-mesh STAs. + */ + if ((scan.capinfo & + (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) || + scan.meshid == NULL || scan.meshconf == NULL) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "beacon", "%s", "not a mesh sta"); + vap->iv_stats.is_mesh_wrongmesh++; + return; + } + /* + * Ignore STAs for other mesh networks. + */ + if (memcmp(scan.meshid+2, ms->ms_id, ms->ms_idlen) != 0 || + mesh_verify_meshconf(vap, scan.meshconf)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "beacon", "%s", "not for our mesh"); + vap->iv_stats.is_mesh_wrongmesh++; + return; + } + /* + * Peer only based on the current ACL policy. + */ + if (vap->iv_acl != NULL && + !vap->iv_acl->iac_check(vap, wh->i_addr2)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL, + wh, NULL, "%s", "disallowed by ACL"); + vap->iv_stats.is_rx_acl++; + return; + } + /* + * Do neighbor discovery. + */ + 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); + } + /* + * Automatically peer with discovered nodes if possible. + * XXX backoff on repeated failure + */ + if (ni != vap->iv_bss && + (ms->ms_flags & IEEE80211_MESHFLAGS_AP) && + ni->ni_mlstate == IEEE80211_NODE_MESH_IDLE) { + uint16_t args[1]; + + ni->ni_mlpid = mesh_generateid(vap); + if (ni->ni_mlpid == 0) + return; + mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENSNT); + args[0] = ni->ni_mlpid; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_OPEN, args); + ni->ni_mlrcnt = 0; + mesh_peer_timeout_setup(ni); + } + break; + } + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + { + uint8_t *ssid, *meshid, *rates, *xrates; + uint8_t *sfrm; + + 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 + * [tlv] mesh id + */ + ssid = meshid = 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; + case IEEE80211_ELEMID_MESHID: + meshid = frm; + break; + } + frm += frm[2] + 2; + } + IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); + if (xrates != NULL) + IEEE80211_VERIFY_ELEMENT(xrates, + IEEE80211_RATE_MAXSIZE - rates[1], return); + if (meshid != NULL) + IEEE80211_VERIFY_ELEMENT(meshid, + IEEE80211_MESHID_LEN, return); + /* NB: meshid, not ssid */ + IEEE80211_VERIFY_SSID(vap->iv_bss, meshid, 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, 0); + break; + } + case IEEE80211_FC0_SUBTYPE_ACTION: + if (vap->iv_state != IEEE80211_S_RUN) { + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + /* + * We received an action for an unknown neighbor. + * XXX: wait for it to beacon or create ieee80211_node? + */ + if (ni == vap->iv_bss) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_MESH, + wh, NULL, "%s", "unknown node"); + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + /* + * Discard if not for us. + */ + if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && + !IEEE80211_IS_MULTICAST(wh->i_addr1)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_MESH, + wh, NULL, "%s", "not for me"); + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + /* XXX parse_action is a bit useless now */ + if (ieee80211_parse_action(ni, m0) == 0) + 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; + } +} + +/* + * Parse meshpeering action ie's for open+confirm frames; the + * important bits are returned in the supplied structure. + */ +static const struct ieee80211_meshpeer_ie * +mesh_parse_meshpeering_action(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, /* XXX for VERIFY_LENGTH */ + const uint8_t *frm, const uint8_t *efrm, + struct ieee80211_meshpeer_ie *mp, uint8_t subtype) +{ + struct ieee80211vap *vap = ni->ni_vap; + const struct ieee80211_meshpeer_ie *mpie; + const uint8_t *meshid, *meshconf, *meshpeer; + + meshid = meshconf = meshpeer = NULL; + while (efrm - frm > 1) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return NULL); + switch (*frm) { + case IEEE80211_ELEMID_MESHID: + meshid = frm; + break; + case IEEE80211_ELEMID_MESHCONF: + meshconf = frm; + break; + case IEEE80211_ELEMID_MESHPEER: + meshpeer = frm; + mpie = (const struct ieee80211_meshpeer_ie *) frm; + memset(mp, 0, sizeof(*mp)); + mp->peer_llinkid = LE_READ_2(&mpie->peer_llinkid); + /* NB: peer link ID is optional on these frames */ + if (subtype == IEEE80211_MESH_PEER_LINK_CLOSE && + mpie->peer_len == 8) { + mp->peer_linkid = 0; + mp->peer_rcode = LE_READ_2(&mpie->peer_linkid); + } else { + mp->peer_linkid = LE_READ_2(&mpie->peer_linkid); + mp->peer_rcode = LE_READ_2(&mpie->peer_rcode); + } + break; + } + frm += frm[1] + 2; + } + + /* + * Verify the contents of the frame. Action frames with + * close subtype don't have a Mesh Configuration IE. + * If if fails validation, close the peer link. + */ + KASSERT(meshpeer != NULL && + subtype != IEEE80211_ACTION_MESHPEERING_CLOSE, + ("parsing close action")); + + if (mesh_verify_meshid(vap, meshid) || + mesh_verify_meshpeer(vap, subtype, meshpeer) || + mesh_verify_meshconf(vap, meshconf)) { + uint16_t args[3]; + + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, + wh, NULL, "%s", "not for our mesh"); + vap->iv_stats.is_rx_mgtdiscard++; + switch (ni->ni_mlstate) { + case IEEE80211_NODE_MESH_IDLE: + case IEEE80211_NODE_MESH_ESTABLISHED: + case IEEE80211_NODE_MESH_HOLDING: + /* ignore */ + break; + case IEEE80211_NODE_MESH_OPENSNT: + case IEEE80211_NODE_MESH_OPENRCV: + case IEEE80211_NODE_MESH_CONFIRMRCV: + args[0] = ni->ni_mlpid; + args[1] = ni->ni_mllid; + args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + args); + mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); + mesh_peer_timeout_setup(ni); + break; + } + return NULL; + } + return (const struct ieee80211_meshpeer_ie *) mp; +} + +static int +mesh_recv_action_meshpeering_open(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_meshpeer_ie ie; + const struct ieee80211_meshpeer_ie *meshpeer; + uint16_t args[3]; + + /* +2+2 for action + code + capabilites */ + meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2, efrm, &ie, + IEEE80211_ACTION_MESHPEERING_OPEN); + if (meshpeer == NULL) { + return 0; + } + + /* XXX move up */ + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "recv PEER OPEN, lid 0x%x", meshpeer->peer_llinkid); + + switch (ni->ni_mlstate) { + case IEEE80211_NODE_MESH_IDLE: + mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV); + ni->ni_mllid = meshpeer->peer_llinkid; + ni->ni_mlpid = mesh_generateid(vap); + if (ni->ni_mlpid == 0) + return 0; /* XXX */ + args[0] = ni->ni_mlpid; + /* Announce we're open too... */ + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_OPEN, args); + /* ...and confirm the link. */ + args[0] = ni->ni_mlpid; + args[1] = ni->ni_mllid; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CONFIRM, + args); + mesh_peer_timeout_setup(ni); + break; + case IEEE80211_NODE_MESH_OPENRCV: + /* Wrong Link ID */ + if (ni->ni_mllid != meshpeer->peer_llinkid) { + args[0] = ni->ni_mllid; + args[1] = ni->ni_mlpid; + args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + args); + mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); + mesh_peer_timeout_setup(ni); + break; + } + /* Duplicate open, confirm again. */ + args[0] = ni->ni_mlpid; + args[1] = ni->ni_mllid; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CONFIRM, + args); + break; + case IEEE80211_NODE_MESH_OPENSNT: + ni->ni_mllid = meshpeer->peer_llinkid; + mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV); + args[0] = ni->ni_mlpid; + args[1] = ni->ni_mllid; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CONFIRM, + args); + /* NB: don't setup/clear any timeout */ + break; + case IEEE80211_NODE_MESH_CONFIRMRCV: + if (ni->ni_mlpid != meshpeer->peer_linkid || + ni->ni_mllid != meshpeer->peer_llinkid) { + args[0] = ni->ni_mlpid; + args[1] = ni->ni_mllid; + args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + args); + mesh_linkchange(ni, + IEEE80211_NODE_MESH_HOLDING); + mesh_peer_timeout_setup(ni); + break; + } + mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED); + ni->ni_mllid = meshpeer->peer_llinkid; + args[0] = ni->ni_mlpid; + args[1] = ni->ni_mllid; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CONFIRM, + args); + mesh_peer_timeout_stop(ni); + break; + case IEEE80211_NODE_MESH_ESTABLISHED: + if (ni->ni_mllid != meshpeer->peer_llinkid) { + args[0] = ni->ni_mllid; + args[1] = ni->ni_mlpid; + args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + args); + mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); + mesh_peer_timeout_setup(ni); + break; + } + args[0] = ni->ni_mlpid; + args[1] = ni->ni_mllid; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CONFIRM, + args); + break; + case IEEE80211_NODE_MESH_HOLDING: + args[0] = ni->ni_mlpid; + args[1] = meshpeer->peer_llinkid; + args[2] = IEEE80211_REASON_MESH_MAX_RETRIES; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + args); + break; + } + return 0; +} + +static int +mesh_recv_action_meshpeering_confirm(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_meshpeer_ie ie; + const struct ieee80211_meshpeer_ie *meshpeer; + uint16_t args[3]; + + /* +2+2+2+2 for action + code + capabilites + status code + AID */ + meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2+2+2+2, efrm, &ie, + IEEE80211_ACTION_MESHPEERING_CONFIRM); + if (meshpeer == NULL) { + return 0; + } + + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "recv PEER CONFIRM, local id 0x%x, peer id 0x%x", + meshpeer->peer_llinkid, meshpeer->peer_linkid); + + switch (ni->ni_mlstate) { + case IEEE80211_NODE_MESH_OPENRCV: + mesh_linkchange(ni, IEEE80211_NODE_MESH_ESTABLISHED); + mesh_peer_timeout_stop(ni); + break; + case IEEE80211_NODE_MESH_OPENSNT: + mesh_linkchange(ni, IEEE80211_NODE_MESH_CONFIRMRCV); + break; + case IEEE80211_NODE_MESH_HOLDING: + args[0] = ni->ni_mlpid; + args[1] = meshpeer->peer_llinkid; + args[2] = IEEE80211_REASON_MESH_MAX_RETRIES; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + args); + break; + case IEEE80211_NODE_MESH_CONFIRMRCV: + if (ni->ni_mllid != meshpeer->peer_llinkid) { + args[0] = ni->ni_mlpid; + args[1] = ni->ni_mllid; + args[2] = IEEE80211_REASON_PEER_LINK_CANCELED; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + args); + mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); + mesh_peer_timeout_setup(ni); + } + break; + default: + IEEE80211_DISCARD(vap, + IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, + wh, NULL, "received confirm in invalid state %d", + ni->ni_mlstate); + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + return 0; +} + +static int +mesh_recv_action_meshpeering_close(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + uint16_t args[3]; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, + ni, "%s", "recv PEER CLOSE"); + + switch (ni->ni_mlstate) { + case IEEE80211_NODE_MESH_IDLE: + /* ignore */ + break; + case IEEE80211_NODE_MESH_OPENRCV: + case IEEE80211_NODE_MESH_OPENSNT: + case IEEE80211_NODE_MESH_CONFIRMRCV: + case IEEE80211_NODE_MESH_ESTABLISHED: + args[0] = ni->ni_mlpid; + args[1] = ni->ni_mllid; + args[2] = IEEE80211_REASON_MESH_CLOSE_RCVD; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, + args); + mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); + mesh_peer_timeout_setup(ni); + break; + case IEEE80211_NODE_MESH_HOLDING: + mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE); + mesh_peer_timeout_setup(ni); + break; + } + return 0; +} + +/* + * Link Metric handling. + */ +static int +mesh_recv_action_meshlmetric_req(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + uint32_t metric; + + metric = mesh_airtime_calc(ni); + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHLMETRIC, + IEEE80211_ACTION_MESHLMETRIC_REP, + &metric); + return 0; +} + +static int +mesh_recv_action_meshlmetric_rep(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + return 0; +} + +static int +mesh_send_action(struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211_bpf_params params; + + memset(¶ms, 0, sizeof(params)); + params.ibp_pri = WME_AC_VO; + params.ibp_rate0 = ni->ni_txparms->mgmtrate; + /* XXX ucast/mcast */ + params.ibp_try0 = ni->ni_txparms->maxretry; + params.ibp_power = ni->ni_txpower; + return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION, + ¶ms); +} + +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) +#define ADDWORD(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = ((v) >> 8) & 0xff; \ + frm[2] = ((v) >> 16) & 0xff; \ + frm[3] = ((v) >> 24) & 0xff; \ + frm += 4; \ +} while (0) + +static int +mesh_send_action_meshpeering_open(struct ieee80211_node *ni, + int category, int action, void *args0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + uint16_t *args = args0; + const struct ieee80211_rateset *rs; + struct mbuf *m; + uint8_t *frm; + + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "send PEER OPEN action: localid 0x%x", args[0]); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) /* action+category */ + + sizeof(uint16_t) /* capabilites */ + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + 2 + IEEE80211_MESHID_LEN + + sizeof(struct ieee80211_meshconf_ie) + + sizeof(struct ieee80211_meshpeer_ie) + ); + if (m != NULL) { + /* + * mesh peer open action frame format: + * [1] category + * [1] action + * [2] capabilities + * [tlv] rates + * [tlv] xrates + * [tlv] mesh id + * [tlv] mesh conf + * [tlv] mesh peer link mgmt + */ + *frm++ = category; + *frm++ = action; + ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan)); + rs = ieee80211_get_suprates(ic, ic->ic_curchan); + frm = ieee80211_add_rates(frm, rs); + frm = ieee80211_add_xrates(frm, rs); + frm = ieee80211_add_meshid(frm, vap); + frm = ieee80211_add_meshconf(frm, vap); + frm = ieee80211_add_meshpeer(frm, IEEE80211_MESH_PEER_LINK_OPEN, + args[0], 0, 0); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + return mesh_send_action(ni, m); + } else { + vap->iv_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + return ENOMEM; + } +} + +static int +mesh_send_action_meshpeering_confirm(struct ieee80211_node *ni, + int category, int action, void *args0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + uint16_t *args = args0; + const struct ieee80211_rateset *rs; + struct mbuf *m; + uint8_t *frm; + + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "send PEER CONFIRM action: localid 0x%x, peerid 0x%x", + args[0], args[1]); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) /* action+category */ + + sizeof(uint16_t) /* capabilites */ + + sizeof(uint16_t) /* status code */ + + sizeof(uint16_t) /* AID */ + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + 2 + IEEE80211_MESHID_LEN + + sizeof(struct ieee80211_meshconf_ie) + + sizeof(struct ieee80211_meshpeer_ie) + ); + if (m != NULL) { + /* + * mesh peer confirm action frame format: + * [1] category + * [1] action + * [2] capabilities + * [2] status code + * [2] association id (peer ID) + * [tlv] rates + * [tlv] xrates + * [tlv] mesh id + * [tlv] mesh conf + * [tlv] mesh peer link mgmt + */ + *frm++ = category; + *frm++ = action; + ADDSHORT(frm, ieee80211_getcapinfo(vap, ni->ni_chan)); + ADDSHORT(frm, 0); /* status code */ + ADDSHORT(frm, args[1]); /* AID */ + rs = ieee80211_get_suprates(ic, ic->ic_curchan); + frm = ieee80211_add_rates(frm, rs); + frm = ieee80211_add_xrates(frm, rs); + frm = ieee80211_add_meshid(frm, vap); + frm = ieee80211_add_meshconf(frm, vap); + frm = ieee80211_add_meshpeer(frm, + IEEE80211_MESH_PEER_LINK_CONFIRM, + args[0], args[1], 0); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + return mesh_send_action(ni, m); + } else { + vap->iv_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + return ENOMEM; + } +} + +static int +mesh_send_action_meshpeering_close(struct ieee80211_node *ni, + int category, int action, void *args0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + uint16_t *args = args0; + struct mbuf *m; + uint8_t *frm; + + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "send PEER CLOSE action: localid 0x%x, peerid 0x%x reason %d", + args[0], args[1], args[2]); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) /* action+category */ + + sizeof(uint16_t) /* reason code */ + + 2 + IEEE80211_MESHID_LEN + + sizeof(struct ieee80211_meshpeer_ie) + ); + if (m != NULL) { + /* + * mesh peer close action frame format: + * [1] category + * [1] action + * [2] reason code + * [tlv] mesh id + * [tlv] mesh peer link mgmt + */ + *frm++ = category; + *frm++ = action; + ADDSHORT(frm, args[2]); /* reason code */ + frm = ieee80211_add_meshid(frm, vap); + frm = ieee80211_add_meshpeer(frm, + IEEE80211_MESH_PEER_LINK_CLOSE, + args[0], args[1], args[2]); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + return mesh_send_action(ni, m); + } else { + vap->iv_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + return ENOMEM; + } +} + +static int +mesh_send_action_meshlink_request(struct ieee80211_node *ni, + int category, int action, void *arg0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct mbuf *m; + uint8_t *frm; + + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "%s", "send LINK METRIC REQUEST action"); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) /* action+category */ + ); + if (m != NULL) { + /* + * mesh link metric request + * [1] category + * [1] action + */ + *frm++ = category; + *frm++ = action; + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + return mesh_send_action(ni, m); + } else { + vap->iv_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + return ENOMEM; + } +} + +static int +mesh_send_action_meshlink_reply(struct ieee80211_node *ni, + int category, int action, void *args0) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + uint32_t *metric = args0; + struct mbuf *m; + uint8_t *frm; + + IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, + "send LINK METRIC REPLY action: metric 0x%x", *metric); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) /* action+category */ + + sizeof(struct ieee80211_meshlmetric_ie) + ); + if (m != NULL) { + /* + * mesh link metric reply + * [1] category + * [1] action + * [tlv] mesh link metric + */ + *frm++ = category; + *frm++ = action; + frm = ieee80211_add_meshlmetric(frm, *metric); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + return mesh_send_action(ni, m); + } else { + vap->iv_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + return ENOMEM; + } +} + +static void +mesh_peer_timeout_setup(struct ieee80211_node *ni) +{ + switch (ni->ni_mlstate) { + case IEEE80211_NODE_MESH_HOLDING: + ni->ni_mltval = ieee80211_mesh_holdingtimeout; + break; + case IEEE80211_NODE_MESH_CONFIRMRCV: + ni->ni_mltval = ieee80211_mesh_confirmtimeout; + break; + case IEEE80211_NODE_MESH_IDLE: + ni->ni_mltval = 0; + break; + default: + ni->ni_mltval = ieee80211_mesh_retrytimeout; + break; + } + if (ni->ni_mltval) + callout_reset(&ni->ni_mltimer, ni->ni_mltval, + mesh_peer_timeout_cb, ni); +} + +/* + * Same as above but backoffs timer statisically 50%. + */ +static void +mesh_peer_timeout_backoff(struct ieee80211_node *ni) +{ + uint32_t r; + + r = arc4random(); + ni->ni_mltval += r % ni->ni_mltval; + callout_reset(&ni->ni_mltimer, ni->ni_mltval, mesh_peer_timeout_cb, + ni); +} + +static __inline void +mesh_peer_timeout_stop(struct ieee80211_node *ni) +{ + callout_drain(&ni->ni_mltimer); +} + +/* + * Mesh Peer Link Management FSM timeout handling. + */ +static void +mesh_peer_timeout_cb(void *arg) +{ + struct ieee80211_node *ni = (struct ieee80211_node *)arg; + uint16_t args[3]; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_MESH, + ni, "mesh link timeout, state %d, retry counter %d", + ni->ni_mlstate, ni->ni_mlrcnt); + + switch (ni->ni_mlstate) { + case IEEE80211_NODE_MESH_IDLE: + case IEEE80211_NODE_MESH_ESTABLISHED: + break; + case IEEE80211_NODE_MESH_OPENSNT: + case IEEE80211_NODE_MESH_OPENRCV: + if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) { + args[0] = ni->ni_mlpid; + args[2] = IEEE80211_REASON_MESH_MAX_RETRIES; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, args); + ni->ni_mlrcnt = 0; + mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); + mesh_peer_timeout_setup(ni); + } else { + args[0] = ni->ni_mlpid; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_OPEN, args); + ni->ni_mlrcnt++; + mesh_peer_timeout_backoff(ni); + } + break; + case IEEE80211_NODE_MESH_CONFIRMRCV: + if (ni->ni_mlrcnt == ieee80211_mesh_maxretries) { + args[0] = ni->ni_mlpid; + args[2] = IEEE80211_REASON_MESH_CONFIRM_TIMEOUT; + ieee80211_send_action(ni, + IEEE80211_ACTION_CAT_MESHPEERING, + IEEE80211_ACTION_MESHPEERING_CLOSE, args); + ni->ni_mlrcnt = 0; + mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING); + mesh_peer_timeout_setup(ni); + } else { + ni->ni_mlrcnt++; + mesh_peer_timeout_setup(ni); + } + break; + case IEEE80211_NODE_MESH_HOLDING: + mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE); + break; + } +} + +static int +mesh_verify_meshid(struct ieee80211vap *vap, const uint8_t *ie) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + + if (ie == NULL || ie[1] != ms->ms_idlen) + return 1; + return memcmp(ms->ms_id, ie + 2, ms->ms_idlen); +} + +/* + * Check if we are using the same algorithms for this mesh. + */ +static int +mesh_verify_meshconf(struct ieee80211vap *vap, const uint8_t *ie) +{ + const struct ieee80211_meshconf_ie *meshconf = + (const struct ieee80211_meshconf_ie *) ie; + const struct ieee80211_mesh_state *ms = vap->iv_mesh; + + if (meshconf == NULL) + return 1; + if (meshconf->conf_pselid != ms->ms_ppath->mpp_ie) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, + "unknown path selection algorithm: 0x%x\n", + meshconf->conf_pselid); + return 1; + } + if (meshconf->conf_pmetid != ms->ms_pmetric->mpm_ie) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, + "unknown path metric algorithm: 0x%x\n", + meshconf->conf_pmetid); + return 1; + } + if (meshconf->conf_ccid != 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, + "unknown congestion control algorithm: 0x%x\n", + meshconf->conf_ccid); + return 1; + } + if (meshconf->conf_syncid != IEEE80211_MESHCONF_SYNC_NEIGHOFF) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, + "unknown sync algorithm: 0x%x\n", + meshconf->conf_syncid); + return 1; + } + if (meshconf->conf_authid != 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, + "unknown auth auth algorithm: 0x%x\n", + meshconf->conf_pselid); + return 1; + } + /* Not accepting peers */ + if (!(meshconf->conf_cap & IEEE80211_MESHCONF_CAP_AP)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, + "not accepting peers: 0x%x\n", meshconf->conf_cap); + return 1; + } + return 0; +} + +static int +mesh_verify_meshpeer(struct ieee80211vap *vap, uint8_t subtype, + const uint8_t *ie) +{ + const struct ieee80211_meshpeer_ie *meshpeer = + (const struct ieee80211_meshpeer_ie *) ie; + + if (meshpeer == NULL || meshpeer->peer_len < 6 || + meshpeer->peer_len > 10) + return 1; + switch (subtype) { + case IEEE80211_MESH_PEER_LINK_OPEN: + if (meshpeer->peer_len != 6) + return 1; + break; + case IEEE80211_MESH_PEER_LINK_CONFIRM: + if (meshpeer->peer_len != 8) + return 1; + break; + case IEEE80211_MESH_PEER_LINK_CLOSE: + if (meshpeer->peer_len < 8) + return 1; + if (meshpeer->peer_len == 8 && meshpeer->peer_linkid != 0) + return 1; + if (meshpeer->peer_rcode == 0) + return 1; + break; + } + return 0; +} + +/* + * Add a Mesh ID IE to a frame. + */ +uint8_t * +ieee80211_add_meshid(uint8_t *frm, struct ieee80211vap *vap) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + + KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap")); + + *frm++ = IEEE80211_ELEMID_MESHID; + *frm++ = ms->ms_idlen; + memcpy(frm, ms->ms_id, ms->ms_idlen); + return frm + ms->ms_idlen; +} + +/* + * Add a Mesh Configuration IE to a frame. + * For now just use HWMP routing, Airtime link metric, Null Congestion + * Signaling, Null Sync Protocol and Null Authentication. + */ +uint8_t * +ieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap) +{ + const struct ieee80211_mesh_state *ms = vap->iv_mesh; + + KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap")); + + *frm++ = IEEE80211_ELEMID_MESHCONF; + *frm++ = sizeof(struct ieee80211_meshconf_ie) - 2; + *frm++ = ms->ms_ppath->mpp_ie; /* path selection */ + *frm++ = ms->ms_pmetric->mpm_ie; /* link metric */ + *frm++ = IEEE80211_MESHCONF_CC_DISABLED; + *frm++ = IEEE80211_MESHCONF_SYNC_NEIGHOFF; + *frm++ = IEEE80211_MESHCONF_AUTH_DISABLED; + /* NB: set the number of neighbors before the rest */ + *frm = (ms->ms_neighbors > 15 ? 15 : ms->ms_neighbors) << 1; + if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) + *frm |= IEEE80211_MESHCONF_FORM_MP; + frm += 1; + if (ms->ms_flags & IEEE80211_MESHFLAGS_AP) + *frm |= IEEE80211_MESHCONF_CAP_AP; + if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) + *frm |= IEEE80211_MESHCONF_CAP_FWRD; + frm += 1; + return frm; +} + +/* + * Add a Mesh Peer Management IE to a frame. + */ +uint8_t * +ieee80211_add_meshpeer(uint8_t *frm, uint8_t subtype, uint16_t localid, + uint16_t peerid, uint16_t reason) +{ + /* XXX change for AH */ + static const uint8_t meshpeerproto[4] = IEEE80211_MESH_PEER_PROTO; + + KASSERT(localid != 0, ("localid == 0")); + + *frm++ = IEEE80211_ELEMID_MESHPEER; + switch (subtype) { + case IEEE80211_MESH_PEER_LINK_OPEN: + *frm++ = 6; /* length */ + memcpy(frm, meshpeerproto, 4); + frm += 4; + ADDSHORT(frm, localid); /* local ID */ + break; + case IEEE80211_MESH_PEER_LINK_CONFIRM: + KASSERT(peerid != 0, ("sending peer confirm without peer id")); + *frm++ = 8; /* length */ + memcpy(frm, meshpeerproto, 4); + frm += 4; + ADDSHORT(frm, localid); /* local ID */ + ADDSHORT(frm, peerid); /* peer ID */ + break; + case IEEE80211_MESH_PEER_LINK_CLOSE: + if (peerid) + *frm++ = 10; /* length */ + else + *frm++ = 8; /* length */ + memcpy(frm, meshpeerproto, 4); + frm += 4; + ADDSHORT(frm, localid); /* local ID */ + if (peerid) + ADDSHORT(frm, peerid); /* peer ID */ + ADDSHORT(frm, reason); + break; + } + return frm; +} + +/* + * Compute an Airtime Link Metric for the link with this node. + * + * Based on Draft 3.0 spec (11B.10, p.149). + */ +/* + * Max 802.11s overhead. + */ +#define IEEE80211_MESH_MAXOVERHEAD \ + (sizeof(struct ieee80211_qosframe_addr4) \ + + sizeof(struct ieee80211_meshcntl_ae11) \ + + sizeof(struct llc) \ + + IEEE80211_ADDR_LEN \ + + IEEE80211_WEP_IVLEN \ + + IEEE80211_WEP_KIDLEN \ + + IEEE80211_WEP_CRCLEN \ + + IEEE80211_WEP_MICLEN \ + + IEEE80211_CRC_LEN) +uint32_t +mesh_airtime_calc(struct ieee80211_node *ni) +{ +#define M_BITS 8 +#define S_FACTOR (2 * M_BITS) + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ni->ni_vap->iv_ifp; + const static int nbits = 8192 << M_BITS; + uint32_t overhead, rate, errrate; + uint64_t res; + + /* Time to transmit a frame */ + rate = ni->ni_txrate; + overhead = ieee80211_compute_duration(ic->ic_rt, + ifp->if_mtu + IEEE80211_MESH_MAXOVERHEAD, rate, 0) << M_BITS; + /* Error rate in percentage */ + /* XXX assuming small failures are ok */ + errrate = (((ifp->if_oerrors + + ifp->if_ierrors) / 100) << M_BITS) / 100; + res = (overhead + (nbits / rate)) * + ((1 << S_FACTOR) / ((1 << M_BITS) - errrate)); + + return (uint32_t)(res >> S_FACTOR); +#undef M_BITS +#undef S_FACTOR +} + +/* + * Add a Mesh Link Metric report IE to a frame. + */ +uint8_t * +ieee80211_add_meshlmetric(uint8_t *frm, uint32_t metric) +{ + *frm++ = IEEE80211_ELEMID_MESHLINK; + *frm++ = 4; + ADDWORD(frm, metric); + return frm; +} +#undef ADDSHORT +#undef ADDWORD + +/* + * Initialize any mesh-specific node state. + */ +void +ieee80211_mesh_node_init(struct ieee80211vap *vap, struct ieee80211_node *ni) +{ + ni->ni_flags |= IEEE80211_NODE_QOS; + callout_init(&ni->ni_mltimer, CALLOUT_MPSAFE); +} + +/* + * Cleanup any mesh-specific node state. + */ +void +ieee80211_mesh_node_cleanup(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_mesh_state *ms = vap->iv_mesh; + + callout_drain(&ni->ni_mltimer); + /* NB: short-circuit callbacks after mesh_vdetach */ + if (vap->iv_mesh != NULL) + ms->ms_ppath->mpp_peerdown(ni); +} + +void +ieee80211_parse_meshid(struct ieee80211_node *ni, const uint8_t *ie) +{ + ni->ni_meshidlen = ie[1]; + memcpy(ni->ni_meshid, ie + 2, ie[1]); +} + +/* + * Setup mesh-specific node state on neighbor discovery. + */ +void +ieee80211_mesh_init_neighbor(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const struct ieee80211_scanparams *sp) +{ + ieee80211_parse_meshid(ni, sp->meshid); +} + +void +ieee80211_mesh_update_beacon(struct ieee80211vap *vap, + struct ieee80211_beacon_offsets *bo) +{ + KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap")); + + if (isset(bo->bo_flags, IEEE80211_BEACON_MESHCONF)) { + (void)ieee80211_add_meshconf(bo->bo_meshconf, vap); + clrbit(bo->bo_flags, IEEE80211_BEACON_MESHCONF); + } +} + +static int +mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + uint8_t tmpmeshid[IEEE80211_NWID_LEN]; + struct ieee80211_mesh_route *rt; + struct ieee80211req_mesh_route *imr; + size_t len, off; + uint8_t *p; + int error; + + if (vap->iv_opmode != IEEE80211_M_MBSS) + return ENOSYS; + + error = 0; + switch (ireq->i_type) { + case IEEE80211_IOC_MESH_ID: + ireq->i_len = ms->ms_idlen; + memcpy(tmpmeshid, ms->ms_id, ireq->i_len); + error = copyout(tmpmeshid, ireq->i_data, ireq->i_len); + break; + case IEEE80211_IOC_MESH_AP: + ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_AP) != 0; + break; + case IEEE80211_IOC_MESH_FWRD: + ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) != 0; + break; + case IEEE80211_IOC_MESH_TTL: + ireq->i_val = ms->ms_ttl; + break; + case IEEE80211_IOC_MESH_RTCMD: + switch (ireq->i_val) { + case IEEE80211_MESH_RTCMD_LIST: + len = 0; + MESH_RT_LOCK(ms); + TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) { + len += sizeof(*imr); + } + MESH_RT_UNLOCK(ms); + if (len > ireq->i_len || ireq->i_len < sizeof(*imr)) { + ireq->i_len = len; + return ENOMEM; + } + ireq->i_len = len; + /* XXX M_WAIT? */ + p = malloc(len, M_TEMP, M_NOWAIT | M_ZERO); + if (p == NULL) + return ENOMEM; + off = 0; + MESH_RT_LOCK(ms); + TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) { + if (off >= len) + break; + imr = (struct ieee80211req_mesh_route *) + (p + off); + imr->imr_flags = rt->rt_flags; + IEEE80211_ADDR_COPY(imr->imr_dest, + rt->rt_dest); + IEEE80211_ADDR_COPY(imr->imr_nexthop, + rt->rt_nexthop); + imr->imr_metric = rt->rt_metric; + imr->imr_nhops = rt->rt_nhops; + imr->imr_lifetime = rt->rt_lifetime; + imr->imr_lastmseq = rt->rt_lastmseq; + off += sizeof(*imr); + } + MESH_RT_UNLOCK(ms); + error = copyout(p, (uint8_t *)ireq->i_data, + ireq->i_len); + free(p, M_TEMP); + break; + case IEEE80211_MESH_RTCMD_FLUSH: + case IEEE80211_MESH_RTCMD_ADD: + case IEEE80211_MESH_RTCMD_DELETE: + return EINVAL; + default: + return ENOSYS; + } + break; + case IEEE80211_IOC_MESH_PR_METRIC: + len = strlen(ms->ms_pmetric->mpm_descr); + if (ireq->i_len < len) + return EINVAL; + ireq->i_len = len; + error = copyout(ms->ms_pmetric->mpm_descr, + (uint8_t *)ireq->i_data, len); + break; + case IEEE80211_IOC_MESH_PR_PATH: + len = strlen(ms->ms_ppath->mpp_descr); + if (ireq->i_len < len) + return EINVAL; + ireq->i_len = len; + error = copyout(ms->ms_ppath->mpp_descr, + (uint8_t *)ireq->i_data, len); + break; + default: + return ENOSYS; + } + + return error; +} +IEEE80211_IOCTL_GET(mesh, mesh_ioctl_get80211); + +static int +mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + uint8_t tmpmeshid[IEEE80211_NWID_LEN]; + uint8_t tmpaddr[IEEE80211_ADDR_LEN]; + char tmpproto[IEEE80211_MESH_PROTO_DSZ]; + int error; + + if (vap->iv_opmode != IEEE80211_M_MBSS) + return ENOSYS; + + error = 0; + switch (ireq->i_type) { + case IEEE80211_IOC_MESH_ID: + if (ireq->i_val != 0 || ireq->i_len > IEEE80211_MESHID_LEN) + return EINVAL; + error = copyin(ireq->i_data, tmpmeshid, ireq->i_len); + if (error != 0) + break; + memset(ms->ms_id, 0, IEEE80211_NWID_LEN); + ms->ms_idlen = ireq->i_len; + memcpy(ms->ms_id, tmpmeshid, ireq->i_len); + error = ENETRESET; + break; + case IEEE80211_IOC_MESH_AP: + if (ireq->i_val) + ms->ms_flags |= IEEE80211_MESHFLAGS_AP; + else + ms->ms_flags &= ~IEEE80211_MESHFLAGS_AP; + error = ENETRESET; + break; + case IEEE80211_IOC_MESH_FWRD: + if (ireq->i_val) + ms->ms_flags |= IEEE80211_MESHFLAGS_FWD; + else + ms->ms_flags &= ~IEEE80211_MESHFLAGS_FWD; + break; + case IEEE80211_IOC_MESH_TTL: + ms->ms_ttl = (uint8_t) ireq->i_val; + break; + case IEEE80211_IOC_MESH_RTCMD: + switch (ireq->i_val) { + case IEEE80211_MESH_RTCMD_LIST: + return EINVAL; + case IEEE80211_MESH_RTCMD_FLUSH: + ieee80211_mesh_rt_flush(vap); + break; + case IEEE80211_MESH_RTCMD_ADD: + if (IEEE80211_ADDR_EQ(vap->iv_myaddr, ireq->i_data) || + IEEE80211_ADDR_EQ(broadcastaddr, ireq->i_data)) + return EINVAL; + error = copyin(ireq->i_data, &tmpaddr, + IEEE80211_ADDR_LEN); + if (error == 0) + ieee80211_mesh_discover(vap, tmpaddr, NULL); + break; + case IEEE80211_MESH_RTCMD_DELETE: + ieee80211_mesh_rt_del(vap, ireq->i_data); + break; + default: + return ENOSYS; + } + break; + case IEEE80211_IOC_MESH_PR_METRIC: + error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto)); + if (error == 0) { + error = mesh_select_proto_metric(vap, tmpproto); + if (error == 0) + error = ENETRESET; + } + break; + case IEEE80211_IOC_MESH_PR_PATH: + error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto)); + if (error == 0) { + error = mesh_select_proto_path(vap, tmpproto); + if (error == 0) + error = ENETRESET; + } + break; + default: + return ENOSYS; + } + return error; +} +IEEE80211_IOCTL_SET(mesh, mesh_ioctl_set80211); diff --git a/freebsd/sys/net80211/ieee80211_mesh.h b/freebsd/sys/net80211/ieee80211_mesh.h new file mode 100644 index 00000000..e90cd402 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_mesh.h @@ -0,0 +1,503 @@ +/*- + * 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 _NET80211_IEEE80211_MESH_HH_ +#define _NET80211_IEEE80211_MESH_HH_ + +#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 +MALLOC_DECLARE(M_80211_MESH_RT); +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 *); +void ieee80211_mesh_update_beacon(struct ieee80211vap *, + struct ieee80211_beacon_offsets *); + +/* + * 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 /* !_NET80211_IEEE80211_MESH_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_monitor.c b/freebsd/sys/net80211/ieee80211_monitor.c new file mode 100644 index 00000000..b87c1d37 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_monitor.c @@ -0,0 +1,140 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 Monitor mode support. + */ +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/errno.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net/bpf.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/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; +} diff --git a/freebsd/sys/net80211/ieee80211_monitor.h b/freebsd/sys/net80211/ieee80211_monitor.h new file mode 100644 index 00000000..09f95dba --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_monitor.h @@ -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 _NET80211_IEEE80211_MONITOR_HH_ +#define _NET80211_IEEE80211_MONITOR_HH_ + +/* + * Monitor implementation definitions. + */ +void ieee80211_monitor_attach(struct ieee80211com *); +void ieee80211_monitor_detach(struct ieee80211com *); +#endif /* !_NET80211_IEEE80211_MONITOR_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_node.c b/freebsd/sys/net80211/ieee80211_node.c new file mode 100644 index 00000000..8b8335c1 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_node.c @@ -0,0 +1,2641 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_input.h> +#ifdef IEEE80211_SUPPORT_SUPERG +#include <freebsd/net80211/ieee80211_superg.h> +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include <freebsd/net80211/ieee80211_tdma.h> +#endif +#include <freebsd/net80211/ieee80211_wds.h> +#include <freebsd/net80211/ieee80211_mesh.h> +#include <freebsd/net80211/ieee80211_ratectl.h> + +#include <freebsd/net/bpf.h> + +/* + * IEEE80211_NODE_HASHSIZE must be a power of 2. + */ +CTASSERT((IEEE80211_NODE_HASHSIZE & (IEEE80211_NODE_HASHSIZE-1)) == 0); + +/* + * Association id's are managed with a bit vector. + */ +#define IEEE80211_AID_SET(_vap, b) \ + ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] |= \ + (1 << (IEEE80211_AID(b) % 32))) +#define IEEE80211_AID_CLR(_vap, b) \ + ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] &= \ + ~(1 << (IEEE80211_AID(b) % 32))) +#define IEEE80211_AID_ISSET(_vap, b) \ + ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32))) + +#ifdef IEEE80211_DEBUG_REFCNT +#define REFCNT_LOC "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line +#else +#define REFCNT_LOC "%s %p<%s> refcnt %d\n", __func__ +#endif + +static int ieee80211_sta_join1(struct ieee80211_node *); + +static struct ieee80211_node *node_alloc(struct ieee80211vap *, + const uint8_t [IEEE80211_ADDR_LEN]); +static void node_cleanup(struct ieee80211_node *); +static void node_free(struct ieee80211_node *); +static void node_age(struct ieee80211_node *); +static int8_t node_getrssi(const struct ieee80211_node *); +static void node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *); +static void node_getmimoinfo(const struct ieee80211_node *, + struct ieee80211_mimo_info *); + +static void _ieee80211_free_node(struct ieee80211_node *); + +static void ieee80211_node_table_init(struct ieee80211com *ic, + struct ieee80211_node_table *nt, const char *name, + int inact, int keymaxix); +static void ieee80211_node_table_reset(struct ieee80211_node_table *, + struct ieee80211vap *); +static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt); +static void ieee80211_erp_timeout(struct ieee80211com *); + +MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state"); +MALLOC_DEFINE(M_80211_NODE_IE, "80211nodeie", "802.11 node ie"); + +void +ieee80211_node_attach(struct ieee80211com *ic) +{ + /* XXX really want maxlen enforced per-sta */ + ieee80211_ageq_init(&ic->ic_stageq, ic->ic_max_keyix * 8, + "802.11 staging q"); + ieee80211_node_table_init(ic, &ic->ic_sta, "station", + IEEE80211_INACT_INIT, ic->ic_max_keyix); + callout_init(&ic->ic_inact, CALLOUT_MPSAFE); + callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, + ieee80211_node_timeout, ic); + + ic->ic_node_alloc = node_alloc; + ic->ic_node_free = node_free; + ic->ic_node_cleanup = node_cleanup; + ic->ic_node_age = node_age; + ic->ic_node_drain = node_age; /* NB: same as age */ + ic->ic_node_getrssi = node_getrssi; + ic->ic_node_getsignal = node_getsignal; + ic->ic_node_getmimoinfo = node_getmimoinfo; + + /* + * Set flags to be propagated to all vap's; + * these define default behaviour/configuration. + */ + ic->ic_flags_ext |= IEEE80211_FEXT_INACT; /* inactivity processing */ +} + +void +ieee80211_node_detach(struct ieee80211com *ic) +{ + + callout_drain(&ic->ic_inact); + ieee80211_node_table_cleanup(&ic->ic_sta); + ieee80211_ageq_cleanup(&ic->ic_stageq); +} + +void +ieee80211_node_vattach(struct ieee80211vap *vap) +{ + /* NB: driver can override */ + vap->iv_max_aid = IEEE80211_AID_DEF; + + /* default station inactivity timer setings */ + vap->iv_inact_init = IEEE80211_INACT_INIT; + vap->iv_inact_auth = IEEE80211_INACT_AUTH; + vap->iv_inact_run = IEEE80211_INACT_RUN; + vap->iv_inact_probe = IEEE80211_INACT_PROBE; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_INACT, + "%s: init %u auth %u run %u probe %u\n", __func__, + vap->iv_inact_init, vap->iv_inact_auth, + vap->iv_inact_run, vap->iv_inact_probe); +} + +void +ieee80211_node_latevattach(struct ieee80211vap *vap) +{ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + /* XXX should we allow max aid to be zero? */ + if (vap->iv_max_aid < IEEE80211_AID_MIN) { + vap->iv_max_aid = IEEE80211_AID_MIN; + if_printf(vap->iv_ifp, + "WARNING: max aid too small, changed to %d\n", + vap->iv_max_aid); + } + vap->iv_aid_bitmap = (uint32_t *) malloc( + howmany(vap->iv_max_aid, 32) * sizeof(uint32_t), + M_80211_NODE, M_NOWAIT | M_ZERO); + if (vap->iv_aid_bitmap == NULL) { + /* XXX no way to recover */ + printf("%s: no memory for AID bitmap, max aid %d!\n", + __func__, vap->iv_max_aid); + vap->iv_max_aid = 0; + } + } + + ieee80211_reset_bss(vap); + + vap->iv_auth = ieee80211_authenticator_get(vap->iv_bss->ni_authmode); +} + +void +ieee80211_node_vdetach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + ieee80211_node_table_reset(&ic->ic_sta, vap); + if (vap->iv_bss != NULL) { + ieee80211_free_node(vap->iv_bss); + vap->iv_bss = NULL; + } + if (vap->iv_aid_bitmap != NULL) { + free(vap->iv_aid_bitmap, M_80211_NODE); + vap->iv_aid_bitmap = NULL; + } +} + +/* + * Port authorize/unauthorize interfaces for use by an authenticator. + */ + +void +ieee80211_node_authorize(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + + ni->ni_flags |= IEEE80211_NODE_AUTH; + ni->ni_inact_reload = vap->iv_inact_run; + ni->ni_inact = ni->ni_inact_reload; + + IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, + "%s: inact_reload %u", __func__, ni->ni_inact_reload); +} + +void +ieee80211_node_unauthorize(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + + ni->ni_flags &= ~IEEE80211_NODE_AUTH; + ni->ni_inact_reload = vap->iv_inact_auth; + if (ni->ni_inact > ni->ni_inact_reload) + ni->ni_inact = ni->ni_inact_reload; + + IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, + "%s: inact_reload %u inact %u", __func__, + ni->ni_inact_reload, ni->ni_inact); +} + +/* + * Fix tx parameters for a node according to ``association state''. + */ +void +ieee80211_node_setuptxparms(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + enum ieee80211_phymode mode; + + if (ni->ni_flags & IEEE80211_NODE_HT) { + if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) + mode = IEEE80211_MODE_11NA; + else + mode = IEEE80211_MODE_11NG; + } else { /* legacy rate handling */ + if (IEEE80211_IS_CHAN_ST(ni->ni_chan)) + mode = IEEE80211_MODE_STURBO_A; + else if (IEEE80211_IS_CHAN_HALF(ni->ni_chan)) + mode = IEEE80211_MODE_HALF; + else if (IEEE80211_IS_CHAN_QUARTER(ni->ni_chan)) + mode = IEEE80211_MODE_QUARTER; + /* NB: 108A should be handled as 11a */ + else if (IEEE80211_IS_CHAN_A(ni->ni_chan)) + mode = IEEE80211_MODE_11A; + else if (IEEE80211_IS_CHAN_108G(ni->ni_chan) || + (ni->ni_flags & IEEE80211_NODE_ERP)) + mode = IEEE80211_MODE_11G; + else + mode = IEEE80211_MODE_11B; + } + ni->ni_txparms = &vap->iv_txparms[mode]; +} + +/* + * Set/change the channel. The rate set is also updated as + * to insure a consistent view by drivers. + * XXX should be private but hostap needs it to deal with CSA + */ +void +ieee80211_node_set_chan(struct ieee80211_node *ni, + struct ieee80211_channel *chan) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; + enum ieee80211_phymode mode; + + KASSERT(chan != IEEE80211_CHAN_ANYC, ("no channel")); + + ni->ni_chan = chan; + mode = ieee80211_chan2mode(chan); + if (IEEE80211_IS_CHAN_HT(chan)) { + /* + * XXX Gotta be careful here; the rate set returned by + * ieee80211_get_suprates is actually any HT rate + * set so blindly copying it will be bad. We must + * install the legacy rate est in ni_rates and the + * HT rate set in ni_htrates. + */ + ni->ni_htrates = *ieee80211_get_suphtrates(ic, chan); + /* + * Setup bss tx parameters based on operating mode. We + * use legacy rates when operating in a mixed HT+non-HT bss + * and non-ERP rates in 11g for mixed ERP+non-ERP bss. + */ + if (mode == IEEE80211_MODE_11NA && + (vap->iv_flags_ht & IEEE80211_FHT_PUREN) == 0) + mode = IEEE80211_MODE_11A; + else if (mode == IEEE80211_MODE_11NG && + (vap->iv_flags_ht & IEEE80211_FHT_PUREN) == 0) + mode = IEEE80211_MODE_11G; + if (mode == IEEE80211_MODE_11G && + (vap->iv_flags & IEEE80211_F_PUREG) == 0) + mode = IEEE80211_MODE_11B; + } + ni->ni_txparms = &vap->iv_txparms[mode]; + ni->ni_rates = *ieee80211_get_suprates(ic, chan); +} + +static __inline void +copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss) +{ + /* propagate useful state */ + nbss->ni_authmode = obss->ni_authmode; + nbss->ni_txpower = obss->ni_txpower; + nbss->ni_vlan = obss->ni_vlan; + /* XXX statistics? */ + /* XXX legacy WDS bssid? */ +} + +void +ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: creating %s on channel %u\n", __func__, + ieee80211_opmode_name[vap->iv_opmode], + ieee80211_chan2ieee(ic, chan)); + + ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); + if (ni == NULL) { + /* XXX recovery? */ + return; + } + IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr); + ni->ni_esslen = vap->iv_des_ssid[0].len; + memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen); + if (vap->iv_bss != NULL) + copy_bss(ni, vap->iv_bss); + ni->ni_intval = ic->ic_bintval; + if (vap->iv_flags & IEEE80211_F_PRIVACY) + ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; + if (ic->ic_phytype == IEEE80211_T_FH) { + ni->ni_fhdwell = 200; /* XXX */ + ni->ni_fhindex = 1; + } + if (vap->iv_opmode == IEEE80211_M_IBSS) { + vap->iv_flags |= IEEE80211_F_SIBSS; + ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS; /* XXX */ + if (vap->iv_flags & IEEE80211_F_DESBSSID) + IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid); + else { + get_random_bytes(ni->ni_bssid, IEEE80211_ADDR_LEN); + /* clear group bit, add local bit */ + ni->ni_bssid[0] = (ni->ni_bssid[0] &~ 0x01) | 0x02; + } + } else if (vap->iv_opmode == IEEE80211_M_AHDEMO) { + if (vap->iv_flags & IEEE80211_F_DESBSSID) + IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid); + else +#ifdef IEEE80211_SUPPORT_TDMA + if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) +#endif + memset(ni->ni_bssid, 0, IEEE80211_ADDR_LEN); +#ifdef IEEE80211_SUPPORT_MESH + } else if (vap->iv_opmode == IEEE80211_M_MBSS) { + ni->ni_meshidlen = vap->iv_mesh->ms_idlen; + memcpy(ni->ni_meshid, vap->iv_mesh->ms_id, ni->ni_meshidlen); +#endif + } + /* + * Fix the channel and related attributes. + */ + /* clear DFS CAC state on previous channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + ic->ic_bsschan->ic_freq != chan->ic_freq && + IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) + ieee80211_dfs_cac_clear(ic, ic->ic_bsschan); + ic->ic_bsschan = chan; + ieee80211_node_set_chan(ni, chan); + ic->ic_curmode = ieee80211_chan2mode(chan); + /* + * Do mode-specific setup. + */ + if (IEEE80211_IS_CHAN_FULL(chan)) { + if (IEEE80211_IS_CHAN_ANYG(chan)) { + /* + * Use a mixed 11b/11g basic rate set. + */ + ieee80211_setbasicrates(&ni->ni_rates, + IEEE80211_MODE_11G); + if (vap->iv_flags & IEEE80211_F_PUREG) { + /* + * Also mark OFDM rates basic so 11b + * stations do not join (WiFi compliance). + */ + ieee80211_addbasicrates(&ni->ni_rates, + IEEE80211_MODE_11A); + } + } else if (IEEE80211_IS_CHAN_B(chan)) { + /* + * Force pure 11b rate set. + */ + ieee80211_setbasicrates(&ni->ni_rates, + IEEE80211_MODE_11B); + } + } + + (void) ieee80211_sta_join1(ieee80211_ref_node(ni)); +} + +/* + * Reset bss state on transition to the INIT state. + * Clear any stations from the table (they have been + * deauth'd) and reset the bss node (clears key, rate + * etc. state). + */ +void +ieee80211_reset_bss(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni, *obss; + + ieee80211_node_table_reset(&ic->ic_sta, vap); + /* XXX multi-bss: wrong */ + ieee80211_reset_erp(ic); + + ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); + KASSERT(ni != NULL, ("unable to setup initial BSS node")); + obss = vap->iv_bss; + vap->iv_bss = ieee80211_ref_node(ni); + if (obss != NULL) { + copy_bss(ni, obss); + ni->ni_intval = ic->ic_bintval; + ieee80211_free_node(obss); + } else + IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr); +} + +static int +match_ssid(const struct ieee80211_node *ni, + int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + int i; + + for (i = 0; i < nssid; i++) { + if (ni->ni_esslen == ssids[i].len && + memcmp(ni->ni_essid, ssids[i].ssid, ni->ni_esslen) == 0) + return 1; + } + return 0; +} + +/* + * Test a node for suitability/compatibility. + */ +static int +check_bss(struct ieee80211vap *vap, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + uint8_t rate; + + if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) + return 0; + if (vap->iv_opmode == IEEE80211_M_IBSS) { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) + return 0; + } else { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) + return 0; + } + if (vap->iv_flags & IEEE80211_F_PRIVACY) { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) + return 0; + } else { + /* XXX does this mean privacy is supported or required? */ + if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) + return 0; + } + rate = ieee80211_fix_rate(ni, &ni->ni_rates, + IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); + if (rate & IEEE80211_RATE_BASIC) + return 0; + if (vap->iv_des_nssid != 0 && + !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid)) + return 0; + if ((vap->iv_flags & IEEE80211_F_DESBSSID) && + !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid)) + return 0; + return 1; +} + +#ifdef IEEE80211_DEBUG +/* + * Display node suitability/compatibility. + */ +static void +check_bss_debug(struct ieee80211vap *vap, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + uint8_t rate; + int fail; + + fail = 0; + if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) + fail |= 0x01; + if (vap->iv_opmode == IEEE80211_M_IBSS) { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) + fail |= 0x02; + } else { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) + fail |= 0x02; + } + if (vap->iv_flags & IEEE80211_F_PRIVACY) { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) + fail |= 0x04; + } else { + /* XXX does this mean privacy is supported or required? */ + if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) + fail |= 0x04; + } + rate = ieee80211_fix_rate(ni, &ni->ni_rates, + IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); + if (rate & IEEE80211_RATE_BASIC) + fail |= 0x08; + if (vap->iv_des_nssid != 0 && + !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid)) + fail |= 0x10; + if ((vap->iv_flags & IEEE80211_F_DESBSSID) && + !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid)) + fail |= 0x20; + + printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr)); + printf(" %s%c", ether_sprintf(ni->ni_bssid), fail & 0x20 ? '!' : ' '); + printf(" %3d%c", + ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' '); + printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, + fail & 0x08 ? '!' : ' '); + printf(" %4s%c", + (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : + (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : + "????", + fail & 0x02 ? '!' : ' '); + printf(" %3s%c ", + (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? "wep" : "no", + fail & 0x04 ? '!' : ' '); + ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); + printf("%s\n", fail & 0x10 ? "!" : ""); +} +#endif /* IEEE80211_DEBUG */ + +/* + * Handle 802.11 ad hoc network merge. The + * convention, set by the Wireless Ethernet Compatibility Alliance + * (WECA), is that an 802.11 station will change its BSSID to match + * the "oldest" 802.11 ad hoc network, on the same channel, that + * has the station's desired SSID. The "oldest" 802.11 network + * sends beacons with the greatest TSF timestamp. + * + * The caller is assumed to validate TSF's before attempting a merge. + * + * Return !0 if the BSSID changed, 0 otherwise. + */ +int +ieee80211_ibss_merge(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; +#ifdef IEEE80211_DEBUG + struct ieee80211com *ic = ni->ni_ic; +#endif + + if (ni == vap->iv_bss || + IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) { + /* unchanged, nothing to do */ + return 0; + } + if (!check_bss(vap, ni)) { + /* capabilities mismatch */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, + "%s: merge failed, capabilities mismatch\n", __func__); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_assoc(vap)) + check_bss_debug(vap, ni); +#endif + vap->iv_stats.is_ibss_capmismatch++; + return 0; + } + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, + "%s: new bssid %s: %s preamble, %s slot time%s\n", __func__, + ether_sprintf(ni->ni_bssid), + ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", + ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", + ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "" + ); + return ieee80211_sta_join1(ieee80211_ref_node(ni)); +} + +/* + * Calculate HT channel promotion flags for all vaps. + * This assumes ni_chan have been setup for each vap. + */ +static int +gethtadjustflags(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + int flags; + + flags = 0; + /* XXX locking */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap->iv_state < IEEE80211_S_RUN) + continue; + switch (vap->iv_opmode) { + case IEEE80211_M_WDS: + case IEEE80211_M_STA: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + case IEEE80211_M_IBSS: + case IEEE80211_M_MBSS: + flags |= ieee80211_htchanflags(vap->iv_bss->ni_chan); + break; + default: + break; + } + } + return flags; +} + +/* + * Check if the current channel needs to change based on whether + * any vap's are using HT20/HT40. This is used to sync the state + * of ic_curchan after a channel width change on a running vap. + */ +void +ieee80211_sync_curchan(struct ieee80211com *ic) +{ + struct ieee80211_channel *c; + + c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, gethtadjustflags(ic)); + if (c != ic->ic_curchan) { + ic->ic_curchan = c; + ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); + ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); + IEEE80211_UNLOCK(ic); + ic->ic_set_channel(ic); + ieee80211_radiotap_chan_change(ic); + IEEE80211_LOCK(ic); + } +} + +/* + * Setup the current channel. The request channel may be + * promoted if other vap's are operating with HT20/HT40. + */ +void +ieee80211_setupcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) +{ + if (ic->ic_htcaps & IEEE80211_HTC_HT) { + int flags = gethtadjustflags(ic); + /* + * Check for channel promotion required to support the + * set of running vap's. This assumes we are called + * after ni_chan is setup for each vap. + */ + /* NB: this assumes IEEE80211_FHT_USEHT40 > IEEE80211_FHT_HT */ + if (flags > ieee80211_htchanflags(c)) + c = ieee80211_ht_adjust_channel(ic, c, flags); + } + ic->ic_bsschan = ic->ic_curchan = c; + ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); + ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); +} + +/* + * Change the current channel. The channel change is guaranteed to have + * happened before the next state change. + */ +void +ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) +{ + ieee80211_setupcurchan(ic, c); + ieee80211_runtask(ic, &ic->ic_chan_task); +} + +/* + * Join the specified IBSS/BSS network. The node is assumed to + * be passed in with a held reference. + */ +static int +ieee80211_sta_join1(struct ieee80211_node *selbs) +{ + struct ieee80211vap *vap = selbs->ni_vap; + struct ieee80211com *ic = selbs->ni_ic; + struct ieee80211_node *obss; + int canreassoc; + + /* + * Committed to selbs, setup state. + */ + obss = vap->iv_bss; + /* + * Check if old+new node have the same address in which + * case we can reassociate when operating in sta mode. + */ + canreassoc = (obss != NULL && + vap->iv_state == IEEE80211_S_RUN && + IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr)); + vap->iv_bss = selbs; /* NB: caller assumed to bump refcnt */ + if (obss != NULL) { + copy_bss(selbs, obss); + ieee80211_node_decref(obss); /* iv_bss reference */ + ieee80211_free_node(obss); /* station table reference */ + obss = NULL; /* NB: guard against later use */ + } + + /* + * Delete unusable rates; we've already checked + * that the negotiated rate set is acceptable. + */ + ieee80211_fix_rate(vap->iv_bss, &vap->iv_bss->ni_rates, + IEEE80211_F_DODEL | IEEE80211_F_JOIN); + + ieee80211_setcurchan(ic, selbs->ni_chan); + /* + * Set the erp state (mostly the slot time) to deal with + * the auto-select case; this should be redundant if the + * mode is locked. + */ + ieee80211_reset_erp(ic); + ieee80211_wme_initparams(vap); + + if (vap->iv_opmode == IEEE80211_M_STA) { + if (canreassoc) { + /* Reassociate */ + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1); + } else { + /* + * Act as if we received a DEAUTH frame in case we + * are invoked from the RUN state. This will cause + * us to try to re-authenticate if we are operating + * as a station. + */ + ieee80211_new_state(vap, IEEE80211_S_AUTH, + IEEE80211_FC0_SUBTYPE_DEAUTH); + } + } else + ieee80211_new_state(vap, IEEE80211_S_RUN, -1); + return 1; +} + +int +ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan, + const struct ieee80211_scan_entry *se) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + + ni = ieee80211_alloc_node(&ic->ic_sta, vap, se->se_macaddr); + if (ni == NULL) { + /* XXX msg */ + return 0; + } + /* + * Expand scan state into node's format. + * XXX may not need all this stuff + */ + IEEE80211_ADDR_COPY(ni->ni_bssid, se->se_bssid); + ni->ni_esslen = se->se_ssid[1]; + memcpy(ni->ni_essid, se->se_ssid+2, ni->ni_esslen); + ni->ni_tstamp.tsf = se->se_tstamp.tsf; + ni->ni_intval = se->se_intval; + ni->ni_capinfo = se->se_capinfo; + ni->ni_chan = chan; + ni->ni_timoff = se->se_timoff; + ni->ni_fhdwell = se->se_fhdwell; + ni->ni_fhindex = se->se_fhindex; + ni->ni_erp = se->se_erp; + IEEE80211_RSSI_LPF(ni->ni_avgrssi, se->se_rssi); + ni->ni_noise = se->se_noise; + if (vap->iv_opmode == IEEE80211_M_STA) { + /* NB: only infrastructure mode requires an associd */ + ni->ni_flags |= IEEE80211_NODE_ASSOCID; + } + + if (ieee80211_ies_init(&ni->ni_ies, se->se_ies.data, se->se_ies.len)) { + ieee80211_ies_expand(&ni->ni_ies); +#ifdef IEEE80211_SUPPORT_SUPERG + if (ni->ni_ies.ath_ie != NULL) + ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); +#endif + if (ni->ni_ies.htcap_ie != NULL) + ieee80211_parse_htcap(ni, ni->ni_ies.htcap_ie); + if (ni->ni_ies.htinfo_ie != NULL) + ieee80211_parse_htinfo(ni, ni->ni_ies.htinfo_ie); +#ifdef IEEE80211_SUPPORT_MESH + if (ni->ni_ies.meshid_ie != NULL) + ieee80211_parse_meshid(ni, ni->ni_ies.meshid_ie); +#endif +#ifdef IEEE80211_SUPPORT_TDMA + if (ni->ni_ies.tdma_ie != NULL) + ieee80211_parse_tdma(ni, ni->ni_ies.tdma_ie); +#endif + } + + vap->iv_dtim_period = se->se_dtimperiod; + vap->iv_dtim_count = 0; + + /* NB: must be after ni_chan is setup */ + ieee80211_setup_rates(ni, se->se_rates, se->se_xrates, + IEEE80211_F_DOSORT); + if (ieee80211_iserp_rateset(&ni->ni_rates)) + ni->ni_flags |= IEEE80211_NODE_ERP; + ieee80211_node_setuptxparms(ni); + ieee80211_ratectl_node_init(ni); + + return ieee80211_sta_join1(ieee80211_ref_node(ni)); +} + +/* + * Leave the specified IBSS/BSS network. The node is assumed to + * be passed in with a held reference. + */ +void +ieee80211_sta_leave(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + ic->ic_node_cleanup(ni); + ieee80211_notify_node_leave(ni); +} + +/* + * Send a deauthenticate frame and drop the station. + */ +void +ieee80211_node_deauth(struct ieee80211_node *ni, int reason) +{ + /* NB: bump the refcnt to be sure temporay nodes are not reclaimed */ + ieee80211_ref_node(ni); + if (ni->ni_associd != 0) + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason); + ieee80211_node_leave(ni); + ieee80211_free_node(ni); +} + +static struct ieee80211_node * +node_alloc(struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_node *ni; + + ni = (struct ieee80211_node *) malloc(sizeof(struct ieee80211_node), + M_80211_NODE, M_NOWAIT | M_ZERO); + return ni; +} + +/* + * Initialize an ie blob with the specified data. If previous + * data exists re-use the data block. As a side effect we clear + * all references to specific ie's; the caller is required to + * recalculate them. + */ +int +ieee80211_ies_init(struct ieee80211_ies *ies, const uint8_t *data, int len) +{ + /* NB: assumes data+len are the last fields */ + memset(ies, 0, offsetof(struct ieee80211_ies, data)); + if (ies->data != NULL && ies->len != len) { + /* data size changed */ + free(ies->data, M_80211_NODE_IE); + ies->data = NULL; + } + if (ies->data == NULL) { + ies->data = (uint8_t *) malloc(len, M_80211_NODE_IE, M_NOWAIT); + if (ies->data == NULL) { + ies->len = 0; + /* NB: pointers have already been zero'd above */ + return 0; + } + } + memcpy(ies->data, data, len); + ies->len = len; + return 1; +} + +/* + * Reclaim storage for an ie blob. + */ +void +ieee80211_ies_cleanup(struct ieee80211_ies *ies) +{ + if (ies->data != NULL) + free(ies->data, M_80211_NODE_IE); +} + +/* + * Expand an ie blob data contents and to fillin individual + * ie pointers. The data blob is assumed to be well-formed; + * we don't do any validity checking of ie lengths. + */ +void +ieee80211_ies_expand(struct ieee80211_ies *ies) +{ + uint8_t *ie; + int ielen; + + ie = ies->data; + ielen = ies->len; + while (ielen > 0) { + switch (ie[0]) { + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(ie)) + ies->wpa_ie = ie; + else if (iswmeoui(ie)) + ies->wme_ie = ie; +#ifdef IEEE80211_SUPPORT_SUPERG + else if (isatherosoui(ie)) + ies->ath_ie = ie; +#endif +#ifdef IEEE80211_SUPPORT_TDMA + else if (istdmaoui(ie)) + ies->tdma_ie = ie; +#endif + break; + case IEEE80211_ELEMID_RSN: + ies->rsn_ie = ie; + break; + case IEEE80211_ELEMID_HTCAP: + ies->htcap_ie = ie; + break; +#ifdef IEEE80211_SUPPORT_MESH + case IEEE80211_ELEMID_MESHID: + ies->meshid_ie = ie; + break; +#endif + } + ielen -= 2 + ie[1]; + ie += 2 + ie[1]; + } +} + +/* + * Reclaim any resources in a node and reset any critical + * state. Typically nodes are free'd immediately after, + * but in some cases the storage may be reused so we need + * to insure consistent state (should probably fix that). + */ +static void +node_cleanup(struct ieee80211_node *ni) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + int i; + + /* NB: preserve ni_table */ + if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) { + if (vap->iv_opmode != IEEE80211_M_STA) + vap->iv_ps_sta--; + 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); + } + /* + * Cleanup any HT-related state. + */ + if (ni->ni_flags & IEEE80211_NODE_HT) + ieee80211_ht_node_cleanup(ni); +#ifdef IEEE80211_SUPPORT_SUPERG + else if (ni->ni_ath_flags & IEEE80211_NODE_ATH) + ieee80211_ff_node_cleanup(ni); +#endif +#ifdef IEEE80211_SUPPORT_MESH + /* + * Cleanup any mesh-related state. + */ + if (vap->iv_opmode == IEEE80211_M_MBSS) + ieee80211_mesh_node_cleanup(ni); +#endif + /* + * Clear any staging queue entries. + */ + ieee80211_ageq_drain_node(&ic->ic_stageq, ni); + + /* + * Clear AREF flag that marks the authorization refcnt bump + * has happened. This is probably not needed as the node + * should always be removed from the table so not found but + * do it just in case. + * Likewise clear the ASSOCID flag as these flags are intended + * to be managed in tandem. + */ + ni->ni_flags &= ~(IEEE80211_NODE_AREF | IEEE80211_NODE_ASSOCID); + + /* + * Drain power save queue and, if needed, clear TIM. + */ + if (ieee80211_node_psq_drain(ni) != 0 && vap->iv_set_tim != NULL) + vap->iv_set_tim(ni, 0); + + ni->ni_associd = 0; + if (ni->ni_challenge != NULL) { + free(ni->ni_challenge, M_80211_NODE); + ni->ni_challenge = NULL; + } + /* + * Preserve SSID, WPA, and WME ie's so the bss node is + * reusable during a re-auth/re-assoc state transition. + * If we remove these data they will not be recreated + * because they come from a probe-response or beacon frame + * which cannot be expected prior to the association-response. + * This should not be an issue when operating in other modes + * as stations leaving always go through a full state transition + * which will rebuild this state. + * + * XXX does this leave us open to inheriting old state? + */ + for (i = 0; i < N(ni->ni_rxfrag); i++) + if (ni->ni_rxfrag[i] != NULL) { + m_freem(ni->ni_rxfrag[i]); + ni->ni_rxfrag[i] = NULL; + } + /* + * Must be careful here to remove any key map entry w/o a LOR. + */ + ieee80211_node_delucastkey(ni); +#undef N +} + +static void +node_free(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + ieee80211_ratectl_node_deinit(ni); + ic->ic_node_cleanup(ni); + ieee80211_ies_cleanup(&ni->ni_ies); + ieee80211_psq_cleanup(&ni->ni_psq); + free(ni, M_80211_NODE); +} + +static void +node_age(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + + IEEE80211_NODE_LOCK_ASSERT(&vap->iv_ic->ic_sta); + + /* + * Age frames on the power save queue. + */ + if (ieee80211_node_psq_age(ni) != 0 && + ni->ni_psq.psq_len == 0 && vap->iv_set_tim != NULL) + vap->iv_set_tim(ni, 0); + /* + * Age out HT resources (e.g. frames on the + * A-MPDU reorder queues). + */ + if (ni->ni_associd != 0 && (ni->ni_flags & IEEE80211_NODE_HT)) + ieee80211_ht_node_age(ni); +} + +static int8_t +node_getrssi(const struct ieee80211_node *ni) +{ + uint32_t avgrssi = ni->ni_avgrssi; + int32_t rssi; + + if (avgrssi == IEEE80211_RSSI_DUMMY_MARKER) + return 0; + rssi = IEEE80211_RSSI_GET(avgrssi); + return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi; +} + +static void +node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) +{ + *rssi = node_getrssi(ni); + *noise = ni->ni_noise; +} + +static void +node_getmimoinfo(const struct ieee80211_node *ni, + struct ieee80211_mimo_info *info) +{ + /* XXX zero data? */ +} + +struct ieee80211_node * +ieee80211_alloc_node(struct ieee80211_node_table *nt, + struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct ieee80211com *ic = nt->nt_ic; + struct ieee80211_node *ni; + int hash; + + ni = ic->ic_node_alloc(vap, macaddr); + if (ni == NULL) { + vap->iv_stats.is_rx_nodealloc++; + return NULL; + } + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "%s %p<%s> in %s table\n", __func__, ni, + ether_sprintf(macaddr), nt->nt_name); + + IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); + hash = IEEE80211_NODE_HASH(ic, macaddr); + ieee80211_node_initref(ni); /* mark referenced */ + ni->ni_chan = IEEE80211_CHAN_ANYC; + ni->ni_authmode = IEEE80211_AUTH_OPEN; + ni->ni_txpower = ic->ic_txpowlimit; /* max power */ + ni->ni_txparms = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE); + ni->ni_avgrssi = IEEE80211_RSSI_DUMMY_MARKER; + ni->ni_inact_reload = nt->nt_inact_init; + ni->ni_inact = ni->ni_inact_reload; + ni->ni_ath_defkeyix = 0x7fff; + ieee80211_psq_init(&ni->ni_psq, "unknown"); +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode == IEEE80211_M_MBSS) + ieee80211_mesh_node_init(vap, ni); +#endif + IEEE80211_NODE_LOCK(nt); + TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list); + LIST_INSERT_HEAD(&nt->nt_hash[hash], ni, ni_hash); + ni->ni_table = nt; + ni->ni_vap = vap; + ni->ni_ic = ic; + IEEE80211_NODE_UNLOCK(nt); + + IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, + "%s: inact_reload %u", __func__, ni->ni_inact_reload); + + ieee80211_ratectl_node_init(ni); + + return ni; +} + +/* + * Craft a temporary node suitable for sending a management frame + * to the specified station. We craft only as much state as we + * need to do the work since the node will be immediately reclaimed + * once the send completes. + */ +struct ieee80211_node * +ieee80211_tmp_node(struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + + ni = ic->ic_node_alloc(vap, macaddr); + if (ni != NULL) { + struct ieee80211_node *bss = vap->iv_bss; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "%s %p<%s>\n", __func__, ni, ether_sprintf(macaddr)); + + ni->ni_table = NULL; /* NB: pedantic */ + ni->ni_ic = ic; /* NB: needed to set channel */ + ni->ni_vap = vap; + + IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); + IEEE80211_ADDR_COPY(ni->ni_bssid, bss->ni_bssid); + ieee80211_node_initref(ni); /* mark referenced */ + /* NB: required by ieee80211_fix_rate */ + ieee80211_node_set_chan(ni, bss->ni_chan); + ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey, + IEEE80211_KEYIX_NONE); + ni->ni_txpower = bss->ni_txpower; + /* XXX optimize away */ + ieee80211_psq_init(&ni->ni_psq, "unknown"); + + ieee80211_ratectl_node_init(ni); + } else { + /* XXX msg */ + vap->iv_stats.is_rx_nodealloc++; + } + return ni; +} + +struct ieee80211_node * +ieee80211_dup_bss(struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + + ni = ieee80211_alloc_node(&ic->ic_sta, vap, macaddr); + if (ni != NULL) { + struct ieee80211_node *bss = vap->iv_bss; + /* + * Inherit from iv_bss. + */ + copy_bss(ni, bss); + IEEE80211_ADDR_COPY(ni->ni_bssid, bss->ni_bssid); + ieee80211_node_set_chan(ni, bss->ni_chan); + } + return ni; +} + +/* + * Create a bss node for a legacy WDS vap. The far end does + * not associate so we just create create a new node and + * simulate an association. The caller is responsible for + * installing the node as the bss node and handling any further + * setup work like authorizing the port. + */ +struct ieee80211_node * +ieee80211_node_create_wds(struct ieee80211vap *vap, + const uint8_t bssid[IEEE80211_ADDR_LEN], struct ieee80211_channel *chan) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + + /* XXX check if node already in sta table? */ + ni = ieee80211_alloc_node(&ic->ic_sta, vap, bssid); + if (ni != NULL) { + ni->ni_wdsvap = vap; + IEEE80211_ADDR_COPY(ni->ni_bssid, bssid); + /* + * Inherit any manually configured settings. + */ + copy_bss(ni, vap->iv_bss); + ieee80211_node_set_chan(ni, chan); + /* NB: propagate ssid so available to WPA supplicant */ + ni->ni_esslen = vap->iv_des_ssid[0].len; + memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen); + /* NB: no associd for peer */ + /* + * There are no management frames to use to + * discover neighbor capabilities, so blindly + * propagate the local configuration. + */ + if (vap->iv_flags & IEEE80211_F_WME) + ni->ni_flags |= IEEE80211_NODE_QOS; +#ifdef IEEE80211_SUPPORT_SUPERG + if (vap->iv_flags & IEEE80211_F_FF) + ni->ni_flags |= IEEE80211_NODE_FF; +#endif + if ((ic->ic_htcaps & IEEE80211_HTC_HT) && + (vap->iv_flags_ht & IEEE80211_FHT_HT)) { + /* + * Device is HT-capable and HT is enabled for + * the vap; setup HT operation. On return + * ni_chan will be adjusted to an HT channel. + */ + ieee80211_ht_wds_init(ni); + } else { + struct ieee80211_channel *c = ni->ni_chan; + /* + * Force a legacy channel to be used. + */ + c = ieee80211_find_channel(ic, + c->ic_freq, c->ic_flags &~ IEEE80211_CHAN_HT); + KASSERT(c != NULL, ("no legacy channel, %u/%x", + ni->ni_chan->ic_freq, ni->ni_chan->ic_flags)); + ni->ni_chan = c; + } + } + return ni; +} + +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_node_locked_debug(struct ieee80211_node_table *nt, + const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) +#else +ieee80211_find_node_locked(struct ieee80211_node_table *nt, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +#endif +{ + struct ieee80211_node *ni; + int hash; + + IEEE80211_NODE_LOCK_ASSERT(nt); + + hash = IEEE80211_NODE_HASH(nt->nt_ic, macaddr); + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { + if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { + ieee80211_ref_node(ni); /* mark referenced */ +#ifdef IEEE80211_DEBUG_REFCNT + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, + "%s (%s:%u) %p<%s> refcnt %d\n", __func__, + func, line, + ni, ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)); +#endif + return ni; + } + } + return NULL; +} + +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_node_debug(struct ieee80211_node_table *nt, + const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) +#else +ieee80211_find_node(struct ieee80211_node_table *nt, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +#endif +{ + struct ieee80211_node *ni; + + IEEE80211_NODE_LOCK(nt); + ni = ieee80211_find_node_locked(nt, macaddr); + IEEE80211_NODE_UNLOCK(nt); + return ni; +} + +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_vap_node_locked_debug(struct ieee80211_node_table *nt, + const struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) +#else +ieee80211_find_vap_node_locked(struct ieee80211_node_table *nt, + const struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +#endif +{ + struct ieee80211_node *ni; + int hash; + + IEEE80211_NODE_LOCK_ASSERT(nt); + + hash = IEEE80211_NODE_HASH(nt->nt_ic, macaddr); + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { + if (ni->ni_vap == vap && + IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { + ieee80211_ref_node(ni); /* mark referenced */ +#ifdef IEEE80211_DEBUG_REFCNT + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, + "%s (%s:%u) %p<%s> refcnt %d\n", __func__, + func, line, + ni, ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)); +#endif + return ni; + } + } + return NULL; +} + +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_vap_node_debug(struct ieee80211_node_table *nt, + const struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) +#else +ieee80211_find_vap_node(struct ieee80211_node_table *nt, + const struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +#endif +{ + struct ieee80211_node *ni; + + IEEE80211_NODE_LOCK(nt); + ni = ieee80211_find_vap_node_locked(nt, vap, macaddr); + IEEE80211_NODE_UNLOCK(nt); + return ni; +} + +/* + * Fake up a node; this handles node discovery in adhoc mode. + * Note that for the driver's benefit we we treat this like + * an association so the driver has an opportunity to setup + * it's private state. + */ +struct ieee80211_node * +ieee80211_fakeup_adhoc_node(struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_node *ni; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "%s: mac<%s>\n", __func__, ether_sprintf(macaddr)); + ni = ieee80211_dup_bss(vap, macaddr); + if (ni != NULL) { + struct ieee80211com *ic = vap->iv_ic; + + /* XXX no rate negotiation; just dup */ + ni->ni_rates = vap->iv_bss->ni_rates; + if (ieee80211_iserp_rateset(&ni->ni_rates)) + ni->ni_flags |= IEEE80211_NODE_ERP; + if (vap->iv_opmode == IEEE80211_M_AHDEMO) { + /* + * In adhoc demo mode there are no management + * frames to use to discover neighbor capabilities, + * so blindly propagate the local configuration + * so we can do interesting things (e.g. use + * WME to disable ACK's). + */ + if (vap->iv_flags & IEEE80211_F_WME) + ni->ni_flags |= IEEE80211_NODE_QOS; +#ifdef IEEE80211_SUPPORT_SUPERG + if (vap->iv_flags & IEEE80211_F_FF) + ni->ni_flags |= IEEE80211_NODE_FF; +#endif + } + ieee80211_node_setuptxparms(ni); + ieee80211_ratectl_node_init(ni); + if (ic->ic_newassoc != NULL) + ic->ic_newassoc(ni, 1); + /* XXX not right for 802.1x/WPA */ + ieee80211_node_authorize(ni); + } + return ni; +} + +void +ieee80211_init_neighbor(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const struct ieee80211_scanparams *sp) +{ + ni->ni_esslen = sp->ssid[1]; + memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]); + IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); + memcpy(ni->ni_tstamp.data, sp->tstamp, sizeof(ni->ni_tstamp)); + ni->ni_intval = sp->bintval; + ni->ni_capinfo = sp->capinfo; + ni->ni_chan = ni->ni_ic->ic_curchan; + ni->ni_fhdwell = sp->fhdwell; + ni->ni_fhindex = sp->fhindex; + ni->ni_erp = sp->erp; + ni->ni_timoff = sp->timoff; +#ifdef IEEE80211_SUPPORT_MESH + if (ni->ni_vap->iv_opmode == IEEE80211_M_MBSS) + ieee80211_mesh_init_neighbor(ni, wh, sp); +#endif + if (ieee80211_ies_init(&ni->ni_ies, sp->ies, sp->ies_len)) { + ieee80211_ies_expand(&ni->ni_ies); + if (ni->ni_ies.wme_ie != NULL) + ni->ni_flags |= IEEE80211_NODE_QOS; + else + ni->ni_flags &= ~IEEE80211_NODE_QOS; +#ifdef IEEE80211_SUPPORT_SUPERG + if (ni->ni_ies.ath_ie != NULL) + ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); +#endif + } + + /* NB: must be after ni_chan is setup */ + ieee80211_setup_rates(ni, sp->rates, sp->xrates, + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); +} + +/* + * Do node discovery in adhoc mode on receipt of a beacon + * or probe response frame. Note that for the driver's + * benefit we we treat this like an association so the + * driver has an opportunity to setup it's private state. + */ +struct ieee80211_node * +ieee80211_add_neighbor(struct ieee80211vap *vap, + const struct ieee80211_frame *wh, + const struct ieee80211_scanparams *sp) +{ + struct ieee80211_node *ni; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "%s: mac<%s>\n", __func__, ether_sprintf(wh->i_addr2)); + ni = ieee80211_dup_bss(vap, wh->i_addr2);/* XXX alloc_node? */ + if (ni != NULL) { + struct ieee80211com *ic = vap->iv_ic; + + ieee80211_init_neighbor(ni, wh, sp); + if (ieee80211_iserp_rateset(&ni->ni_rates)) + ni->ni_flags |= IEEE80211_NODE_ERP; + ieee80211_node_setuptxparms(ni); + ieee80211_ratectl_node_init(ni); + if (ic->ic_newassoc != NULL) + ic->ic_newassoc(ni, 1); + /* XXX not right for 802.1x/WPA */ + ieee80211_node_authorize(ni); + } + return ni; +} + +#define IS_PROBEREQ(wh) \ + ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK|IEEE80211_FC0_SUBTYPE_MASK)) \ + == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ)) +#define IS_BCAST_PROBEREQ(wh) \ + (IS_PROBEREQ(wh) && IEEE80211_IS_MULTICAST( \ + ((const struct ieee80211_frame *)(wh))->i_addr3)) + +static __inline struct ieee80211_node * +_find_rxnode(struct ieee80211_node_table *nt, + const struct ieee80211_frame_min *wh) +{ + if (IS_BCAST_PROBEREQ(wh)) + return NULL; /* spam bcast probe req to all vap's */ + return ieee80211_find_node_locked(nt, wh->i_addr2); +} + +/* + * Locate the node for sender, track state, and then pass the + * (referenced) node up to the 802.11 layer for its use. Note + * we can return NULL if the sender is not in the table. + */ +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_rxnode_debug(struct ieee80211com *ic, + const struct ieee80211_frame_min *wh, const char *func, int line) +#else +ieee80211_find_rxnode(struct ieee80211com *ic, + const struct ieee80211_frame_min *wh) +#endif +{ + struct ieee80211_node_table *nt; + struct ieee80211_node *ni; + + nt = &ic->ic_sta; + IEEE80211_NODE_LOCK(nt); + ni = _find_rxnode(nt, wh); + IEEE80211_NODE_UNLOCK(nt); + + return ni; +} + +/* + * Like ieee80211_find_rxnode but use the supplied h/w + * key index as a hint to locate the node in the key + * mapping table. If an entry is present at the key + * index we return it; otherwise do a normal lookup and + * update the mapping table if the station has a unicast + * key assigned to it. + */ +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_rxnode_withkey_debug(struct ieee80211com *ic, + const struct ieee80211_frame_min *wh, ieee80211_keyix keyix, + const char *func, int line) +#else +ieee80211_find_rxnode_withkey(struct ieee80211com *ic, + const struct ieee80211_frame_min *wh, ieee80211_keyix keyix) +#endif +{ + struct ieee80211_node_table *nt; + struct ieee80211_node *ni; + + nt = &ic->ic_sta; + IEEE80211_NODE_LOCK(nt); + if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) + ni = nt->nt_keyixmap[keyix]; + else + ni = NULL; + if (ni == NULL) { + ni = _find_rxnode(nt, wh); + if (ni != NULL && nt->nt_keyixmap != NULL) { + /* + * If the station has a unicast key cache slot + * assigned update the key->node mapping table. + */ + keyix = ni->ni_ucastkey.wk_rxkeyix; + /* XXX can keyixmap[keyix] != NULL? */ + if (keyix < nt->nt_keyixmax && + nt->nt_keyixmap[keyix] == NULL) { + IEEE80211_DPRINTF(ni->ni_vap, + IEEE80211_MSG_NODE, + "%s: add key map entry %p<%s> refcnt %d\n", + __func__, ni, ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)+1); + nt->nt_keyixmap[keyix] = ieee80211_ref_node(ni); + } + } + } else { + if (IS_BCAST_PROBEREQ(wh)) + ni = NULL; /* spam bcast probe req to all vap's */ + else + ieee80211_ref_node(ni); + } + IEEE80211_NODE_UNLOCK(nt); + + return ni; +} +#undef IS_BCAST_PROBEREQ +#undef IS_PROBEREQ + +/* + * Return a reference to the appropriate node for sending + * a data frame. This handles node discovery in adhoc networks. + */ +struct ieee80211_node * +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_find_txnode_debug(struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN], + const char *func, int line) +#else +ieee80211_find_txnode(struct ieee80211vap *vap, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +#endif +{ + struct ieee80211_node_table *nt = &vap->iv_ic->ic_sta; + struct ieee80211_node *ni; + + /* + * The destination address should be in the node table + * unless this is a multicast/broadcast frame. We can + * also optimize station mode operation, all frames go + * to the bss node. + */ + /* XXX can't hold lock across dup_bss 'cuz of recursive locking */ + IEEE80211_NODE_LOCK(nt); + if (vap->iv_opmode == IEEE80211_M_STA || + vap->iv_opmode == IEEE80211_M_WDS || + IEEE80211_IS_MULTICAST(macaddr)) + ni = ieee80211_ref_node(vap->iv_bss); + else + ni = ieee80211_find_node_locked(nt, macaddr); + IEEE80211_NODE_UNLOCK(nt); + + if (ni == NULL) { + if (vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_AHDEMO) { + /* + * In adhoc mode cons up a node for the destination. + * Note that we need an additional reference for the + * caller to be consistent with + * ieee80211_find_node_locked. + */ + ni = ieee80211_fakeup_adhoc_node(vap, macaddr); + if (ni != NULL) + (void) ieee80211_ref_node(ni); + } else { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, macaddr, + "no node, discard frame (%s)", __func__); + vap->iv_stats.is_tx_nonode++; + } + } + return ni; +} + +static void +_ieee80211_free_node(struct ieee80211_node *ni) +{ + struct ieee80211_node_table *nt = ni->ni_table; + + /* + * NB: careful about referencing the vap as it may be + * gone if the last reference was held by a driver. + * We know the com will always be present so it's safe + * to use ni_ic below to reclaim resources. + */ +#if 0 + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "%s %p<%s> in %s table\n", __func__, ni, + ether_sprintf(ni->ni_macaddr), + nt != NULL ? nt->nt_name : "<gone>"); +#endif + if (ni->ni_associd != 0) { + struct ieee80211vap *vap = ni->ni_vap; + if (vap->iv_aid_bitmap != NULL) + IEEE80211_AID_CLR(vap, ni->ni_associd); + } + if (nt != NULL) { + TAILQ_REMOVE(&nt->nt_node, ni, ni_list); + LIST_REMOVE(ni, ni_hash); + } + ni->ni_ic->ic_node_free(ni); +} + +void +#ifdef IEEE80211_DEBUG_REFCNT +ieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line) +#else +ieee80211_free_node(struct ieee80211_node *ni) +#endif +{ + struct ieee80211_node_table *nt = ni->ni_table; + +#ifdef IEEE80211_DEBUG_REFCNT + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, + "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni, + ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1); +#endif + if (nt != NULL) { + IEEE80211_NODE_LOCK(nt); + if (ieee80211_node_dectestref(ni)) { + /* + * Last reference, reclaim state. + */ + _ieee80211_free_node(ni); + } else if (ieee80211_node_refcnt(ni) == 1 && + nt->nt_keyixmap != NULL) { + ieee80211_keyix keyix; + /* + * Check for a last reference in the key mapping table. + */ + keyix = ni->ni_ucastkey.wk_rxkeyix; + if (keyix < nt->nt_keyixmax && + nt->nt_keyixmap[keyix] == ni) { + IEEE80211_DPRINTF(ni->ni_vap, + IEEE80211_MSG_NODE, + "%s: %p<%s> clear key map entry", __func__, + ni, ether_sprintf(ni->ni_macaddr)); + nt->nt_keyixmap[keyix] = NULL; + ieee80211_node_decref(ni); /* XXX needed? */ + _ieee80211_free_node(ni); + } + } + IEEE80211_NODE_UNLOCK(nt); + } else { + if (ieee80211_node_dectestref(ni)) + _ieee80211_free_node(ni); + } +} + +/* + * Reclaim a unicast key and clear any key cache state. + */ +int +ieee80211_node_delucastkey(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211_node *nikey; + ieee80211_keyix keyix; + int isowned, status; + + /* + * NB: We must beware of LOR here; deleting the key + * can cause the crypto layer to block traffic updates + * which can generate a LOR against the node table lock; + * grab it here and stash the key index for our use below. + * + * Must also beware of recursion on the node table lock. + * When called from node_cleanup we may already have + * the node table lock held. Unfortunately there's no + * way to separate out this path so we must do this + * conditionally. + */ + isowned = IEEE80211_NODE_IS_LOCKED(nt); + if (!isowned) + IEEE80211_NODE_LOCK(nt); + nikey = NULL; + status = 1; /* NB: success */ + if (ni->ni_ucastkey.wk_keyix != IEEE80211_KEYIX_NONE) { + keyix = ni->ni_ucastkey.wk_rxkeyix; + status = ieee80211_crypto_delkey(ni->ni_vap, &ni->ni_ucastkey); + if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) { + nikey = nt->nt_keyixmap[keyix]; + nt->nt_keyixmap[keyix] = NULL; + } + } + if (!isowned) + IEEE80211_NODE_UNLOCK(nt); + + if (nikey != NULL) { + KASSERT(nikey == ni, + ("key map out of sync, ni %p nikey %p", ni, nikey)); + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, + "%s: delete key map entry %p<%s> refcnt %d\n", + __func__, ni, ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)-1); + ieee80211_free_node(ni); + } + return status; +} + +/* + * Reclaim a node. If this is the last reference count then + * do the normal free work. Otherwise remove it from the node + * table and mark it gone by clearing the back-reference. + */ +static void +node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni) +{ + ieee80211_keyix keyix; + + IEEE80211_NODE_LOCK_ASSERT(nt); + + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, + "%s: remove %p<%s> from %s table, refcnt %d\n", + __func__, ni, ether_sprintf(ni->ni_macaddr), + nt->nt_name, ieee80211_node_refcnt(ni)-1); + /* + * Clear any entry in the unicast key mapping table. + * We need to do it here so rx lookups don't find it + * in the mapping table even if it's not in the hash + * table. We cannot depend on the mapping table entry + * being cleared because the node may not be free'd. + */ + keyix = ni->ni_ucastkey.wk_rxkeyix; + if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax && + nt->nt_keyixmap[keyix] == ni) { + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, + "%s: %p<%s> clear key map entry %u\n", + __func__, ni, ether_sprintf(ni->ni_macaddr), keyix); + nt->nt_keyixmap[keyix] = NULL; + ieee80211_node_decref(ni); /* NB: don't need free */ + } + if (!ieee80211_node_dectestref(ni)) { + /* + * Other references are present, just remove the + * node from the table so it cannot be found. When + * the references are dropped storage will be + * reclaimed. + */ + TAILQ_REMOVE(&nt->nt_node, ni, ni_list); + LIST_REMOVE(ni, ni_hash); + ni->ni_table = NULL; /* clear reference */ + } else + _ieee80211_free_node(ni); +} + +/* + * Node table support. + */ + +static void +ieee80211_node_table_init(struct ieee80211com *ic, + struct ieee80211_node_table *nt, + const char *name, int inact, int keyixmax) +{ + struct ifnet *ifp = ic->ic_ifp; + + nt->nt_ic = ic; + IEEE80211_NODE_LOCK_INIT(nt, ifp->if_xname); + IEEE80211_NODE_ITERATE_LOCK_INIT(nt, ifp->if_xname); + TAILQ_INIT(&nt->nt_node); + nt->nt_name = name; + nt->nt_scangen = 1; + nt->nt_inact_init = inact; + nt->nt_keyixmax = keyixmax; + if (nt->nt_keyixmax > 0) { + nt->nt_keyixmap = (struct ieee80211_node **) malloc( + keyixmax * sizeof(struct ieee80211_node *), + M_80211_NODE, M_NOWAIT | M_ZERO); + if (nt->nt_keyixmap == NULL) + if_printf(ic->ic_ifp, + "Cannot allocate key index map with %u entries\n", + keyixmax); + } else + nt->nt_keyixmap = NULL; +} + +static void +ieee80211_node_table_reset(struct ieee80211_node_table *nt, + struct ieee80211vap *match) +{ + struct ieee80211_node *ni, *next; + + IEEE80211_NODE_LOCK(nt); + TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next) { + if (match != NULL && ni->ni_vap != match) + continue; + /* XXX can this happen? if so need's work */ + if (ni->ni_associd != 0) { + struct ieee80211vap *vap = ni->ni_vap; + + if (vap->iv_auth->ia_node_leave != NULL) + vap->iv_auth->ia_node_leave(ni); + if (vap->iv_aid_bitmap != NULL) + IEEE80211_AID_CLR(vap, ni->ni_associd); + } + ni->ni_wdsvap = NULL; /* clear reference */ + node_reclaim(nt, ni); + } + if (match != NULL && match->iv_opmode == IEEE80211_M_WDS) { + /* + * Make a separate pass to clear references to this vap + * held by DWDS entries. They will not be matched above + * because ni_vap will point to the ap vap but we still + * need to clear ni_wdsvap when the WDS vap is destroyed + * and/or reset. + */ + TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next) + if (ni->ni_wdsvap == match) + ni->ni_wdsvap = NULL; + } + IEEE80211_NODE_UNLOCK(nt); +} + +static void +ieee80211_node_table_cleanup(struct ieee80211_node_table *nt) +{ + ieee80211_node_table_reset(nt, NULL); + if (nt->nt_keyixmap != NULL) { +#ifdef DIAGNOSTIC + /* XXX verify all entries are NULL */ + int i; + for (i = 0; i < nt->nt_keyixmax; i++) + if (nt->nt_keyixmap[i] != NULL) + printf("%s: %s[%u] still active\n", __func__, + nt->nt_name, i); +#endif + free(nt->nt_keyixmap, M_80211_NODE); + nt->nt_keyixmap = NULL; + } + IEEE80211_NODE_ITERATE_LOCK_DESTROY(nt); + IEEE80211_NODE_LOCK_DESTROY(nt); +} + +/* + * Timeout inactive stations and do related housekeeping. + * Note that we cannot hold the node lock while sending a + * frame as this would lead to a LOR. Instead we use a + * generation number to mark nodes that we've scanned and + * drop the lock and restart a scan if we have to time out + * a node. Since we are single-threaded by virtue of + * controlling the inactivity timer we can be sure this will + * process each node only once. + */ +static void +ieee80211_timeout_stations(struct ieee80211com *ic) +{ + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211vap *vap; + struct ieee80211_node *ni; + int gen = 0; + + IEEE80211_NODE_ITERATE_LOCK(nt); + gen = ++nt->nt_scangen; +restart: + IEEE80211_NODE_LOCK(nt); + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + if (ni->ni_scangen == gen) /* previously handled */ + continue; + ni->ni_scangen = gen; + /* + * Ignore entries for which have yet to receive an + * authentication frame. These are transient and + * will be reclaimed when the last reference to them + * goes away (when frame xmits complete). + */ + vap = ni->ni_vap; + /* + * Only process stations when in RUN state. This + * insures, for example, that we don't timeout an + * inactive station during CAC. Note that CSA state + * is actually handled in ieee80211_node_timeout as + * it applies to more than timeout processing. + */ + if (vap->iv_state != IEEE80211_S_RUN) + continue; + /* XXX can vap be NULL? */ + if ((vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_STA) && + (ni->ni_flags & IEEE80211_NODE_AREF) == 0) + continue; + /* + * Free fragment if not needed anymore + * (last fragment older than 1s). + * XXX doesn't belong here, move to node_age + */ + if (ni->ni_rxfrag[0] != NULL && + ticks > ni->ni_rxfragstamp + hz) { + m_freem(ni->ni_rxfrag[0]); + ni->ni_rxfrag[0] = NULL; + } + if (ni->ni_inact > 0) { + ni->ni_inact--; + IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, + "%s: inact %u inact_reload %u nrates %u", + __func__, ni->ni_inact, ni->ni_inact_reload, + ni->ni_rates.rs_nrates); + } + /* + * Special case ourself; we may be idle for extended periods + * of time and regardless reclaiming our state is wrong. + * XXX run ic_node_age + */ + if (ni == vap->iv_bss) + continue; + if (ni->ni_associd != 0 || + (vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_AHDEMO)) { + /* + * Age/drain resources held by the station. + */ + ic->ic_node_age(ni); + /* + * Probe the station before time it out. We + * send a null data frame which may not be + * universally supported by drivers (need it + * for ps-poll support so it should be...). + * + * XXX don't probe the station unless we've + * received a frame from them (and have + * some idea of the rates they are capable + * of); this will get fixed more properly + * soon with better handling of the rate set. + */ + if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) && + (0 < ni->ni_inact && + ni->ni_inact <= vap->iv_inact_probe) && + ni->ni_rates.rs_nrates != 0) { + IEEE80211_NOTE(vap, + IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, + ni, "%s", + "probe station due to inactivity"); + /* + * Grab a reference before unlocking the table + * so the node cannot be reclaimed before we + * send the frame. ieee80211_send_nulldata + * understands we've done this and reclaims the + * ref for us as needed. + */ + ieee80211_ref_node(ni); + IEEE80211_NODE_UNLOCK(nt); + ieee80211_send_nulldata(ni); + /* XXX stat? */ + goto restart; + } + } + if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) && + ni->ni_inact <= 0) { + IEEE80211_NOTE(vap, + IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni, + "station timed out due to inactivity " + "(refcnt %u)", ieee80211_node_refcnt(ni)); + /* + * Send a deauthenticate frame and drop the station. + * This is somewhat complicated due to reference counts + * and locking. At this point a station will typically + * have a reference count of 1. ieee80211_node_leave + * will do a "free" of the node which will drop the + * reference count. But in the meantime a reference + * wil be held by the deauth frame. The actual reclaim + * of the node will happen either after the tx is + * completed or by ieee80211_node_leave. + * + * Separately we must drop the node lock before sending + * in case the driver takes a lock, as this can result + * in a LOR between the node lock and the driver lock. + */ + ieee80211_ref_node(ni); + IEEE80211_NODE_UNLOCK(nt); + if (ni->ni_associd != 0) { + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_EXPIRE); + } + ieee80211_node_leave(ni); + ieee80211_free_node(ni); + vap->iv_stats.is_node_timeout++; + goto restart; + } + } + IEEE80211_NODE_UNLOCK(nt); + + IEEE80211_NODE_ITERATE_UNLOCK(nt); +} + +/* + * Aggressively reclaim resources. This should be used + * only in a critical situation to reclaim mbuf resources. + */ +void +ieee80211_drain(struct ieee80211com *ic) +{ + struct ieee80211_node_table *nt = &ic->ic_sta; + struct ieee80211vap *vap; + struct ieee80211_node *ni; + + IEEE80211_NODE_LOCK(nt); + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + /* + * Ignore entries for which have yet to receive an + * authentication frame. These are transient and + * will be reclaimed when the last reference to them + * goes away (when frame xmits complete). + */ + vap = ni->ni_vap; + /* + * Only process stations when in RUN state. This + * insures, for example, that we don't timeout an + * inactive station during CAC. Note that CSA state + * is actually handled in ieee80211_node_timeout as + * it applies to more than timeout processing. + */ + if (vap->iv_state != IEEE80211_S_RUN) + continue; + /* XXX can vap be NULL? */ + if ((vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_STA) && + (ni->ni_flags & IEEE80211_NODE_AREF) == 0) + continue; + /* + * Free fragments. + * XXX doesn't belong here, move to node_drain + */ + if (ni->ni_rxfrag[0] != NULL) { + m_freem(ni->ni_rxfrag[0]); + ni->ni_rxfrag[0] = NULL; + } + /* + * Drain resources held by the station. + */ + ic->ic_node_drain(ni); + } + IEEE80211_NODE_UNLOCK(nt); +} + +/* + * Per-ieee80211com inactivity timer callback. + */ +void +ieee80211_node_timeout(void *arg) +{ + struct ieee80211com *ic = arg; + + /* + * Defer timeout processing if a channel switch is pending. + * We typically need to be mute so not doing things that + * might generate frames is good to handle in one place. + * Supressing the station timeout processing may extend the + * lifetime of inactive stations (by not decrementing their + * idle counters) but this should be ok unless the CSA is + * active for an unusually long time. + */ + if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) { + ieee80211_scan_timeout(ic); + ieee80211_timeout_stations(ic); + ieee80211_ageq_age(&ic->ic_stageq, IEEE80211_INACT_WAIT); + + IEEE80211_LOCK(ic); + ieee80211_erp_timeout(ic); + ieee80211_ht_timeout(ic); + IEEE80211_UNLOCK(ic); + } + callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, + ieee80211_node_timeout, ic); +} + +void +ieee80211_iterate_nodes(struct ieee80211_node_table *nt, + ieee80211_iter_func *f, void *arg) +{ + struct ieee80211_node *ni; + u_int gen; + + IEEE80211_NODE_ITERATE_LOCK(nt); + gen = ++nt->nt_scangen; +restart: + IEEE80211_NODE_LOCK(nt); + TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { + if (ni->ni_scangen != gen) { + ni->ni_scangen = gen; + (void) ieee80211_ref_node(ni); + IEEE80211_NODE_UNLOCK(nt); + (*f)(arg, ni); + ieee80211_free_node(ni); + goto restart; + } + } + IEEE80211_NODE_UNLOCK(nt); + + IEEE80211_NODE_ITERATE_UNLOCK(nt); +} + +void +ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni) +{ + printf("0x%p: mac %s refcnt %d\n", ni, + ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)); + printf("\tscangen %u authmode %u flags 0x%x\n", + ni->ni_scangen, ni->ni_authmode, ni->ni_flags); + printf("\tassocid 0x%x txpower %u vlan %u\n", + ni->ni_associd, ni->ni_txpower, ni->ni_vlan); + 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); + printf("\trssi %d noise %d intval %u capinfo 0x%x\n", + node_getrssi(ni), ni->ni_noise, + ni->ni_intval, ni->ni_capinfo); + printf("\tbssid %s essid \"%.*s\" channel %u:0x%x\n", + ether_sprintf(ni->ni_bssid), + ni->ni_esslen, ni->ni_essid, + ni->ni_chan->ic_freq, ni->ni_chan->ic_flags); + printf("\tinact %u inact_reload %u txrate %u\n", + ni->ni_inact, ni->ni_inact_reload, ni->ni_txrate); + printf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n", + ni->ni_htcap, ni->ni_htparam, + ni->ni_htctlchan, ni->ni_ht2ndchan); + printf("\thtopmode %x htstbc %x chw %u\n", + ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw); +} + +void +ieee80211_dump_nodes(struct ieee80211_node_table *nt) +{ + ieee80211_iterate_nodes(nt, + (ieee80211_iter_func *) ieee80211_dump_node, nt); +} + +static void +ieee80211_notify_erp_locked(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK_ASSERT(ic); + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + ieee80211_beacon_notify(vap, IEEE80211_BEACON_ERP); +} + +void +ieee80211_notify_erp(struct ieee80211com *ic) +{ + IEEE80211_LOCK(ic); + ieee80211_notify_erp_locked(ic); + IEEE80211_UNLOCK(ic); +} + +/* + * Handle a station joining an 11g network. + */ +static void +ieee80211_node_join_11g(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + IEEE80211_LOCK_ASSERT(ic); + + /* + * Station isn't capable of short slot time. Bump + * the count of long slot time stations and disable + * use of short slot time. Note that the actual switch + * over to long slot time use may not occur until the + * next beacon transmission (per sec. 7.3.1.4 of 11g). + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { + ic->ic_longslotsta++; + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, + "station needs long slot time, count %d", + ic->ic_longslotsta); + /* XXX vap's w/ conflicting needs won't work */ + if (!IEEE80211_IS_CHAN_108G(ic->ic_bsschan)) { + /* + * Don't force slot time when switched to turbo + * mode as non-ERP stations won't be present; this + * need only be done when on the normal G channel. + */ + ieee80211_set_shortslottime(ic, 0); + } + } + /* + * If the new station is not an ERP station + * then bump the counter and enable protection + * if configured. + */ + if (!ieee80211_iserp_rateset(&ni->ni_rates)) { + ic->ic_nonerpsta++; + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, + "station is !ERP, %d non-ERP stations associated", + ic->ic_nonerpsta); + /* + * If station does not support short preamble + * then we must enable use of Barker preamble. + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) { + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, + "%s", "station needs long preamble"); + ic->ic_flags |= IEEE80211_F_USEBARKER; + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + } + /* + * If protection is configured and this is the first + * indication we should use protection, enable it. + */ + if (ic->ic_protmode != IEEE80211_PROT_NONE && + ic->ic_nonerpsta == 1 && + (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) { + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC, + "%s: enable use of protection\n", __func__); + ic->ic_flags |= IEEE80211_F_USEPROT; + ieee80211_notify_erp_locked(ic); + } + } else + ni->ni_flags |= IEEE80211_NODE_ERP; +} + +void +ieee80211_node_join(struct ieee80211_node *ni, int resp) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; + int newassoc; + + if (ni->ni_associd == 0) { + uint16_t aid; + + KASSERT(vap->iv_aid_bitmap != NULL, ("no aid bitmap")); + /* + * It would be good to search the bitmap + * more efficiently, but this will do for now. + */ + for (aid = 1; aid < vap->iv_max_aid; aid++) { + if (!IEEE80211_AID_ISSET(vap, aid)) + break; + } + if (aid >= vap->iv_max_aid) { + IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_TOOMANY); + ieee80211_node_leave(ni); + return; + } + ni->ni_associd = aid | 0xc000; + ni->ni_jointime = time_uptime; + IEEE80211_LOCK(ic); + IEEE80211_AID_SET(vap, ni->ni_associd); + vap->iv_sta_assoc++; + ic->ic_sta_assoc++; + + if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + ieee80211_ht_node_join(ni); + if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && + IEEE80211_IS_CHAN_FULL(ic->ic_bsschan)) + ieee80211_node_join_11g(ni); + IEEE80211_UNLOCK(ic); + + newassoc = 1; + } else + newassoc = 0; + + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni, + "station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s", + IEEE80211_NODE_AID(ni), + ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long", + ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", + ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "", + ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", + ni->ni_flags & IEEE80211_NODE_HT ? + (ni->ni_chw == 40 ? ", HT40" : ", HT20") : "", + ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", + ni->ni_flags & IEEE80211_NODE_MIMO_RTS ? " (+SMPS-DYN)" : + ni->ni_flags & IEEE80211_NODE_MIMO_PS ? " (+SMPS)" : "", + ni->ni_flags & IEEE80211_NODE_RIFS ? " (+RIFS)" : "", + IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ? + ", fast-frames" : "", + IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ? + ", turbo" : "" + ); + + ieee80211_node_setuptxparms(ni); + ieee80211_ratectl_node_init(ni); + /* give driver a chance to setup state like ni_txrate */ + if (ic->ic_newassoc != NULL) + ic->ic_newassoc(ni, newassoc); + IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_SUCCESS); + /* tell the authenticator about new station */ + if (vap->iv_auth->ia_node_join != NULL) + vap->iv_auth->ia_node_join(ni); + ieee80211_notify_node_join(ni, + resp == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); +} + +static void +disable_protection(struct ieee80211com *ic) +{ + KASSERT(ic->ic_nonerpsta == 0 && + (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0, + ("%d non ERP stations, flags 0x%x", ic->ic_nonerpsta, + ic->ic_flags_ext)); + + ic->ic_flags &= ~IEEE80211_F_USEPROT; + /* XXX verify mode? */ + if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) { + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } + ieee80211_notify_erp_locked(ic); +} + +/* + * Handle a station leaving an 11g network. + */ +static void +ieee80211_node_leave_11g(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + IEEE80211_LOCK_ASSERT(ic); + + KASSERT(IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan), + ("not in 11g, bss %u:0x%x", ic->ic_bsschan->ic_freq, + ic->ic_bsschan->ic_flags)); + + /* + * If a long slot station do the slot time bookkeeping. + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { + KASSERT(ic->ic_longslotsta > 0, + ("bogus long slot station count %d", ic->ic_longslotsta)); + ic->ic_longslotsta--; + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, + "long slot time station leaves, count now %d", + ic->ic_longslotsta); + if (ic->ic_longslotsta == 0) { + /* + * Re-enable use of short slot time if supported + * and not operating in IBSS mode (per spec). + */ + if ((ic->ic_caps & IEEE80211_C_SHSLOT) && + ic->ic_opmode != IEEE80211_M_IBSS) { + IEEE80211_DPRINTF(ni->ni_vap, + IEEE80211_MSG_ASSOC, + "%s: re-enable use of short slot time\n", + __func__); + ieee80211_set_shortslottime(ic, 1); + } + } + } + /* + * If a non-ERP station do the protection-related bookkeeping. + */ + if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) { + KASSERT(ic->ic_nonerpsta > 0, + ("bogus non-ERP station count %d", ic->ic_nonerpsta)); + ic->ic_nonerpsta--; + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, + "non-ERP station leaves, count now %d%s", ic->ic_nonerpsta, + (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) ? + " (non-ERP sta present)" : ""); + if (ic->ic_nonerpsta == 0 && + (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) { + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC, + "%s: disable use of protection\n", __func__); + disable_protection(ic); + } + } +} + +/* + * Time out presence of an overlapping bss with non-ERP + * stations. When operating in hostap mode we listen for + * beacons from other stations and if we identify a non-ERP + * station is present we enable protection. To identify + * when all non-ERP stations are gone we time out this + * condition. + */ +static void +ieee80211_erp_timeout(struct ieee80211com *ic) +{ + + IEEE80211_LOCK_ASSERT(ic); + + if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) && + time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) { +#if 0 + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, + "%s", "age out non-ERP sta present on channel"); +#endif + ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR; + if (ic->ic_nonerpsta == 0) + disable_protection(ic); + } +} + +/* + * Handle bookkeeping for station deauthentication/disassociation + * when operating as an ap. + */ +void +ieee80211_node_leave(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_node_table *nt = ni->ni_table; + + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni, + "station with aid %d leaves", IEEE80211_NODE_AID(ni)); + + KASSERT(vap->iv_opmode != IEEE80211_M_STA, + ("unexpected operating mode %u", vap->iv_opmode)); + /* + * If node wasn't previously associated all + * we need to do is reclaim the reference. + */ + /* XXX ibss mode bypasses 11g and notification */ + if (ni->ni_associd == 0) + goto done; + /* + * Tell the authenticator the station is leaving. + * Note that we must do this before yanking the + * association id as the authenticator uses the + * associd to locate it's state block. + */ + if (vap->iv_auth->ia_node_leave != NULL) + vap->iv_auth->ia_node_leave(ni); + + IEEE80211_LOCK(ic); + IEEE80211_AID_CLR(vap, ni->ni_associd); + ni->ni_associd = 0; + vap->iv_sta_assoc--; + ic->ic_sta_assoc--; + + if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) + ieee80211_ht_node_leave(ni); + if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && + IEEE80211_IS_CHAN_FULL(ic->ic_bsschan)) + ieee80211_node_leave_11g(ni); + IEEE80211_UNLOCK(ic); + /* + * Cleanup station state. In particular clear various + * state that might otherwise be reused if the node + * is reused before the reference count goes to zero + * (and memory is reclaimed). + */ + ieee80211_sta_leave(ni); +done: + /* + * Remove the node from any table it's recorded in and + * drop the caller's reference. Removal from the table + * is important to insure the node is not reprocessed + * for inactivity. + */ + if (nt != NULL) { + IEEE80211_NODE_LOCK(nt); + node_reclaim(nt, ni); + IEEE80211_NODE_UNLOCK(nt); + } else + ieee80211_free_node(ni); +} + +struct rssiinfo { + struct ieee80211vap *vap; + int rssi_samples; + uint32_t rssi_total; +}; + +static void +get_hostap_rssi(void *arg, struct ieee80211_node *ni) +{ + struct rssiinfo *info = arg; + struct ieee80211vap *vap = ni->ni_vap; + int8_t rssi; + + if (info->vap != vap) + return; + /* only associated stations */ + if (ni->ni_associd == 0) + return; + rssi = vap->iv_ic->ic_node_getrssi(ni); + if (rssi != 0) { + info->rssi_samples++; + info->rssi_total += rssi; + } +} + +static void +get_adhoc_rssi(void *arg, struct ieee80211_node *ni) +{ + struct rssiinfo *info = arg; + struct ieee80211vap *vap = ni->ni_vap; + int8_t rssi; + + if (info->vap != vap) + return; + /* only neighbors */ + /* XXX check bssid */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) + return; + rssi = vap->iv_ic->ic_node_getrssi(ni); + if (rssi != 0) { + info->rssi_samples++; + info->rssi_total += rssi; + } +} + +#ifdef IEEE80211_SUPPORT_MESH +static void +get_mesh_rssi(void *arg, struct ieee80211_node *ni) +{ + struct rssiinfo *info = arg; + struct ieee80211vap *vap = ni->ni_vap; + int8_t rssi; + + if (info->vap != vap) + return; + /* only neighbors that peered successfully */ + if (ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) + return; + rssi = vap->iv_ic->ic_node_getrssi(ni); + if (rssi != 0) { + info->rssi_samples++; + info->rssi_total += rssi; + } +} +#endif /* IEEE80211_SUPPORT_MESH */ + +int8_t +ieee80211_getrssi(struct ieee80211vap *vap) +{ +#define NZ(x) ((x) == 0 ? 1 : (x)) + struct ieee80211com *ic = vap->iv_ic; + struct rssiinfo info; + + info.rssi_total = 0; + info.rssi_samples = 0; + info.vap = vap; + switch (vap->iv_opmode) { + case IEEE80211_M_IBSS: /* average of all ibss neighbors */ + case IEEE80211_M_AHDEMO: /* average of all neighbors */ + ieee80211_iterate_nodes(&ic->ic_sta, get_adhoc_rssi, &info); + break; + case IEEE80211_M_HOSTAP: /* average of all associated stations */ + ieee80211_iterate_nodes(&ic->ic_sta, get_hostap_rssi, &info); + break; +#ifdef IEEE80211_SUPPORT_MESH + case IEEE80211_M_MBSS: /* average of all mesh neighbors */ + ieee80211_iterate_nodes(&ic->ic_sta, get_mesh_rssi, &info); + break; +#endif + case IEEE80211_M_MONITOR: /* XXX */ + case IEEE80211_M_STA: /* use stats from associated ap */ + default: + if (vap->iv_bss != NULL) + info.rssi_total = ic->ic_node_getrssi(vap->iv_bss); + info.rssi_samples = 1; + break; + } + return info.rssi_total / NZ(info.rssi_samples); +#undef NZ +} + +void +ieee80211_getsignal(struct ieee80211vap *vap, int8_t *rssi, int8_t *noise) +{ + + if (vap->iv_bss == NULL) /* NB: shouldn't happen */ + return; + vap->iv_ic->ic_node_getsignal(vap->iv_bss, rssi, noise); + /* for non-station mode return avg'd rssi accounting */ + if (vap->iv_opmode != IEEE80211_M_STA) + *rssi = ieee80211_getrssi(vap); +} diff --git a/freebsd/sys/net80211/ieee80211_node.h b/freebsd/sys/net80211/ieee80211_node.h new file mode 100644 index 00000000..cf47a101 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_node.h @@ -0,0 +1,456 @@ +/*- + * 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 _NET80211_IEEE80211_NODE_HH_ +#define _NET80211_IEEE80211_NODE_HH_ + +#include <freebsd/net80211/ieee80211_ioctl.h> /* for ieee80211_nodestats */ +#include <freebsd/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 */ + void *ni_rctls; /* private ratectl state */ + uint64_t ni_spare[3]; +}; +MALLOC_DECLARE(M_80211_NODE); +MALLOC_DECLARE(M_80211_NODE_IE); + +#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 /* _NET80211_IEEE80211_NODE_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_output.c b/freebsd/sys/net80211/ieee80211_output.c new file mode 100644 index 00000000..b7abe9ec --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_output.c @@ -0,0 +1,3043 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_inet6.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/endian.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/bpf.h> +#include <freebsd/net/ethernet.h> +#include <freebsd/net/if.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_vlan_var.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_regdomain.h> +#ifdef IEEE80211_SUPPORT_SUPERG +#include <freebsd/net80211/ieee80211_superg.h> +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include <freebsd/net80211/ieee80211_tdma.h> +#endif +#include <freebsd/net80211/ieee80211_wds.h> +#include <freebsd/net80211/ieee80211_mesh.h> + +#ifdef INET +#include <freebsd/netinet/in.h> +#include <freebsd/netinet/if_ether.h> +#include <freebsd/netinet/in_systm.h> +#include <freebsd/netinet/ip.h> +#endif +#ifdef INET6 +#include <freebsd/netinet/ip6.h> +#endif + +#include <freebsd/security/mac/mac_framework.h> + +#define ETHER_HEADER_COPY(dst, src) \ + memcpy(dst, src, sizeof(struct ether_header)) + +/* unalligned little endian access */ +#define LE_WRITE_2(p, v) do { \ + ((uint8_t *)(p))[0] = (v) & 0xff; \ + ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ +} while (0) +#define LE_WRITE_4(p, v) do { \ + ((uint8_t *)(p))[0] = (v) & 0xff; \ + ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff; \ + ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff; \ + ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff; \ +} while (0) + +static int ieee80211_fragment(struct ieee80211vap *, struct mbuf *, + u_int hdrsize, u_int ciphdrsize, u_int mtu); +static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); + +#ifdef IEEE80211_DEBUG +/* + * Decide if an outbound 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_PROBE_RESP: + return (vap->iv_opmode == IEEE80211_M_IBSS); + } + return 1; +} +#endif + +/* + * Start method for vap's. All packets from the stack come + * through here. We handle common processing of the packets + * before dispatching them to the underlying device. + */ +void +ieee80211_start(struct ifnet *ifp) +{ +#define IS_DWDS(vap) \ + (vap->iv_opmode == IEEE80211_M_WDS && \ + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *parent = ic->ic_ifp; + struct ieee80211_node *ni; + struct mbuf *m; + struct ether_header *eh; + int error; + + /* NB: parent must be up and running */ + if (!IFNET_IS_UP_RUNNING(parent)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "%s: ignore queue, parent %s not up+running\n", + __func__, parent->if_xname); + /* XXX stat */ + return; + } + if (vap->iv_state == IEEE80211_S_SLEEP) { + /* + * In power save, wakeup device for transmit. + */ + ieee80211_new_state(vap, IEEE80211_S_RUN, 0); + return; + } + /* + * No data frames go out unless we're running. + * Note in particular this covers CAC and CSA + * states (though maybe we should check muting + * for CSA). + */ + if (vap->iv_state != IEEE80211_S_RUN) { + IEEE80211_LOCK(ic); + /* re-check under the com lock to avoid races */ + if (vap->iv_state != IEEE80211_S_RUN) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "%s: ignore queue, in %s state\n", + __func__, ieee80211_state_name[vap->iv_state]); + vap->iv_stats.is_tx_badstate++; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + IEEE80211_UNLOCK(ic); + return; + } + IEEE80211_UNLOCK(ic); + } + for (;;) { + IFQ_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + /* + * Sanitize mbuf flags for net80211 use. We cannot + * clear M_PWR_SAV or M_MORE_DATA because these may + * be set for frames that are re-submitted from the + * power save queue. + * + * NB: This must be done before ieee80211_classify as + * it marks EAPOL in frames with M_EAPOL. + */ + m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); + /* + * Cancel any background scan. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) + ieee80211_cancel_anyscan(vap); + /* + * Find the node for the destination so we can do + * things like power save and fast frames aggregation. + * + * NB: past this point various code assumes the first + * mbuf has the 802.3 header present (and contiguous). + */ + ni = NULL; + if (m->m_len < sizeof(struct ether_header) && + (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "discard frame, %s\n", "m_pullup failed"); + vap->iv_stats.is_tx_nobuf++; /* XXX */ + ifp->if_oerrors++; + continue; + } + eh = mtod(m, struct ether_header *); + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + if (IS_DWDS(vap)) { + /* + * Only unicast frames from the above go out + * DWDS vaps; multicast frames are handled by + * dispatching the frame as it comes through + * the AP vap (see below). + */ + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS, + eh->ether_dhost, "mcast", "%s", "on DWDS"); + vap->iv_stats.is_dwds_mcast++; + m_freem(m); + continue; + } + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + /* + * Spam DWDS vap's w/ multicast traffic. + */ + /* XXX only if dwds in use? */ + ieee80211_dwds_mcast(vap, m); + } + } +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode != IEEE80211_M_MBSS) { +#endif + ni = ieee80211_find_txnode(vap, eh->ether_dhost); + if (ni == NULL) { + /* NB: ieee80211_find_txnode does stat+msg */ + ifp->if_oerrors++; + m_freem(m); + continue; + } + if (ni->ni_associd == 0 && + (ni->ni_flags & IEEE80211_NODE_ASSOCID)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, + eh->ether_dhost, NULL, + "sta not associated (type 0x%04x)", + htons(eh->ether_type)); + vap->iv_stats.is_tx_notassoc++; + ifp->if_oerrors++; + m_freem(m); + ieee80211_free_node(ni); + continue; + } +#ifdef IEEE80211_SUPPORT_MESH + } else { + if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) { + /* + * Proxy station only if configured. + */ + if (!ieee80211_mesh_isproxyena(vap)) { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_OUTPUT | + IEEE80211_MSG_MESH, + eh->ether_dhost, NULL, + "%s", "proxy not enabled"); + vap->iv_stats.is_mesh_notproxy++; + ifp->if_oerrors++; + m_freem(m); + continue; + } + ieee80211_mesh_proxy_check(vap, eh->ether_shost); + } + ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m); + if (ni == NULL) { + /* + * NB: ieee80211_mesh_discover holds/disposes + * frame (e.g. queueing on path discovery). + */ + ifp->if_oerrors++; + continue; + } + } +#endif + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + (m->m_flags & M_PWR_SAV) == 0) { + /* + * Station in power save mode; pass the frame + * to the 802.11 layer and continue. We'll get + * the frame back when the time is right. + * XXX lose WDS vap linkage? + */ + (void) ieee80211_pwrsave(ni, m); + ieee80211_free_node(ni); + continue; + } + /* calculate priority so drivers can find the tx queue */ + if (ieee80211_classify(ni, m)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, + eh->ether_dhost, NULL, + "%s", "classification failure"); + vap->iv_stats.is_tx_classify++; + ifp->if_oerrors++; + m_freem(m); + ieee80211_free_node(ni); + continue; + } + /* + * Stash the node pointer. Note that we do this after + * any call to ieee80211_dwds_mcast because that code + * uses any existing value for rcvif to identify the + * interface it (might have been) received on. + */ + m->m_pkthdr.rcvif = (void *)ni; + + BPF_MTAP(ifp, m); /* 802.3 tx */ + + /* + * Check if A-MPDU tx aggregation is setup or if we + * should try to enable it. The sta must be associated + * with HT and A-MPDU enabled for use. When the policy + * routine decides we should enable A-MPDU we issue an + * ADDBA request and wait for a reply. The frame being + * encapsulated will go out w/o using A-MPDU, or possibly + * it might be collected by the driver and held/retransmit. + * The default ic_ampdu_enable routine handles staggering + * ADDBA requests in case the receiver NAK's us or we are + * otherwise unable to establish a BA stream. + */ + if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && + (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) && + (m->m_flags & M_EAPOL) == 0) { + const int ac = M_WME_GETAC(m); + struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; + + ieee80211_txampdu_count_packet(tap); + if (IEEE80211_AMPDU_RUNNING(tap)) { + /* + * Operational, mark frame for aggregation. + * + * XXX do tx aggregation here + */ + m->m_flags |= M_AMPDU_MPDU; + } else if (!IEEE80211_AMPDU_REQUESTED(tap) && + ic->ic_ampdu_enable(ni, tap)) { + /* + * Not negotiated yet, request service. + */ + ieee80211_ampdu_request(ni, tap); + /* XXX hold frame for reply? */ + } + } +#ifdef IEEE80211_SUPPORT_SUPERG + else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) { + m = ieee80211_ff_check(ni, m); + if (m == NULL) { + /* NB: any ni ref held on stageq */ + continue; + } + } +#endif /* IEEE80211_SUPPORT_SUPERG */ + if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { + /* + * Encapsulate the packet in prep for transmission. + */ + m = ieee80211_encap(vap, ni, m); + if (m == NULL) { + /* NB: stat+msg handled in ieee80211_encap */ + ieee80211_free_node(ni); + continue; + } + } + + error = parent->if_transmit(parent, m); + if (error != 0) { + /* NB: IFQ_HANDOFF reclaims mbuf */ + ieee80211_free_node(ni); + } else { + ifp->if_opackets++; + } + ic->ic_lastdata = ticks; + } +#undef IS_DWDS +} + +/* + * 802.11 output routine. This is (currently) used only to + * connect bpf write calls to the 802.11 layer for injecting + * raw 802.11 frames. + */ +int +ieee80211_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct route *ro) +{ +#define senderr(e) do { error = (e); goto bad;} while (0) + struct ieee80211_node *ni = NULL; + struct ieee80211vap *vap; + struct ieee80211_frame *wh; + int error; + + if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { + /* + * Short-circuit requests if the vap is marked OACTIVE + * as this can happen because a packet came down through + * ieee80211_start before the vap entered RUN state in + * which case it's ok to just drop the frame. This + * should not be necessary but callers of if_output don't + * check OACTIVE. + */ + senderr(ENETDOWN); + } + vap = ifp->if_softc; + /* + * Hand to the 802.3 code if not tagged as + * a raw 802.11 frame. + */ + if (dst->sa_family != AF_IEEE80211) + return vap->iv_output(ifp, m, dst, ro); +#ifdef MAC + error = mac_ifnet_check_transmit(ifp, m); + if (error) + senderr(error); +#endif + if (ifp->if_flags & IFF_MONITOR) + senderr(ENETDOWN); + if (!IFNET_IS_UP_RUNNING(ifp)) + senderr(ENETDOWN); + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, + "block %s frame in CAC state\n", "raw data"); + vap->iv_stats.is_tx_badstate++; + senderr(EIO); /* XXX */ + } + /* XXX bypass bridge, pfil, carp, etc. */ + + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) + senderr(EIO); /* XXX */ + wh = mtod(m, struct ieee80211_frame *); + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != + IEEE80211_FC0_VERSION_0) + senderr(EIO); /* XXX */ + + /* locate destination node */ + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + case IEEE80211_FC1_DIR_FROMDS: + ni = ieee80211_find_txnode(vap, wh->i_addr1); + break; + case IEEE80211_FC1_DIR_TODS: + case IEEE80211_FC1_DIR_DSTODS: + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) + senderr(EIO); /* XXX */ + ni = ieee80211_find_txnode(vap, wh->i_addr3); + break; + default: + senderr(EIO); /* XXX */ + } + if (ni == NULL) { + /* + * Permit packets w/ bpf params through regardless + * (see below about sa_len). + */ + if (dst->sa_len == 0) + senderr(EHOSTUNREACH); + ni = ieee80211_ref_node(vap->iv_bss); + } + + /* + * Sanitize mbuf for net80211 flags leaked from above. + * + * NB: This must be done before ieee80211_classify as + * it marks EAPOL in frames with M_EAPOL. + */ + m->m_flags &= ~M_80211_TX; + + /* calculate priority so drivers can find the tx queue */ + /* XXX assumes an 802.3 frame */ + if (ieee80211_classify(ni, m)) + senderr(EIO); /* XXX */ + + ifp->if_opackets++; + IEEE80211_NODE_STAT(ni, tx_data); + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { + IEEE80211_NODE_STAT(ni, tx_mcast); + m->m_flags |= M_MCAST; + } else + IEEE80211_NODE_STAT(ni, tx_ucast); + /* NB: ieee80211_encap does not include 802.11 header */ + IEEE80211_NODE_STAT_ADD(ni, tx_bytes, m->m_pkthdr.len); + + /* + * NB: DLT_IEEE802_11_RADIO identifies the parameters are + * present by setting the sa_len field of the sockaddr (yes, + * this is a hack). + * NB: we assume sa_data is suitably aligned to cast. + */ + return vap->iv_ic->ic_raw_xmit(ni, m, + (const struct ieee80211_bpf_params *)(dst->sa_len ? + dst->sa_data : NULL)); +bad: + if (m != NULL) + m_freem(m); + if (ni != NULL) + ieee80211_free_node(ni); + ifp->if_oerrors++; + return error; +#undef senderr +} + +/* + * Set the direction field and address fields of an outgoing + * frame. Note this should be called early on in constructing + * a frame as it sets i_fc[1]; other bits can then be or'd in. + */ +void +ieee80211_send_setup( + struct ieee80211_node *ni, + struct mbuf *m, + int type, int tid, + const uint8_t sa[IEEE80211_ADDR_LEN], + const uint8_t da[IEEE80211_ADDR_LEN], + const uint8_t bssid[IEEE80211_ADDR_LEN]) +{ +#define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); + ieee80211_seq seqno; + + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; + if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { + switch (vap->iv_opmode) { + case IEEE80211_M_STA: + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + IEEE80211_ADDR_COPY(wh->i_addr1, bssid); + IEEE80211_ADDR_COPY(wh->i_addr2, sa); + IEEE80211_ADDR_COPY(wh->i_addr3, da); + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, sa); + IEEE80211_ADDR_COPY(wh->i_addr3, bssid); + break; + case IEEE80211_M_HOSTAP: + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, bssid); + IEEE80211_ADDR_COPY(wh->i_addr3, sa); + break; + case IEEE80211_M_WDS: + wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, da); + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); + break; + case IEEE80211_M_MBSS: +#ifdef IEEE80211_SUPPORT_MESH + /* XXX add support for proxied addresses */ + if (IEEE80211_IS_MULTICAST(da)) { + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + /* XXX next hop */ + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, + vap->iv_myaddr); + } else { + wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, + vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, da); + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); + } +#endif + break; + case IEEE80211_M_MONITOR: /* NB: to quiet compiler */ + break; + } + } else { + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, da); + IEEE80211_ADDR_COPY(wh->i_addr2, sa); +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode == IEEE80211_M_MBSS) + IEEE80211_ADDR_COPY(wh->i_addr3, sa); + else +#endif + IEEE80211_ADDR_COPY(wh->i_addr3, bssid); + } + *(uint16_t *)&wh->i_dur[0] = 0; + + seqno = ni->ni_txseqs[tid]++; + *(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + M_SEQNO_SET(m, seqno); + + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + m->m_flags |= M_MCAST; +#undef WH4 +} + +/* + * Send a management frame to the specified node. The node pointer + * must have a reference as the pointer will be passed to the driver + * and potentially held for a long time. If the frame is successfully + * dispatched to the driver, then it is responsible for freeing the + * reference (and potentially free'ing up any associated storage); + * otherwise deal with reclaiming any reference (on error). + */ +int +ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type, + struct ieee80211_bpf_params *params) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + + KASSERT(ni != NULL, ("null node")); + + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, + ni, "block %s frame in CAC state", + ieee80211_mgt_subtype_name[ + (type & IEEE80211_FC0_SUBTYPE_MASK) >> + IEEE80211_FC0_SUBTYPE_SHIFT]); + vap->iv_stats.is_tx_badstate++; + ieee80211_free_node(ni); + m_freem(m); + return EIO; /* XXX */ + } + + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + if (m == NULL) { + ieee80211_free_node(ni); + return ENOMEM; + } + + wh = mtod(m, struct ieee80211_frame *); + ieee80211_send_setup(ni, m, + IEEE80211_FC0_TYPE_MGT | type, IEEE80211_NONQOS_TID, + vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); + if (params->ibp_flags & IEEE80211_BPF_CRYPTO) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1, + "encrypting frame (%s)", __func__); + wh->i_fc[1] |= IEEE80211_FC1_WEP; + } + m->m_flags |= M_ENCAP; /* mark encapsulated */ + + KASSERT(type != IEEE80211_FC0_SUBTYPE_PROBE_RESP, ("probe response?")); + M_WME_SETAC(m, params->ibp_pri); + +#ifdef IEEE80211_DEBUG + /* avoid printing too many frames */ + if ((ieee80211_msg_debug(vap) && doprint(vap, type)) || + ieee80211_msg_dumppkts(vap)) { + printf("[%s] send %s on channel %u\n", + ether_sprintf(wh->i_addr1), + ieee80211_mgt_subtype_name[ + (type & IEEE80211_FC0_SUBTYPE_MASK) >> + IEEE80211_FC0_SUBTYPE_SHIFT], + ieee80211_chan2ieee(ic, ic->ic_curchan)); + } +#endif + IEEE80211_NODE_STAT(ni, tx_mgmt); + + return ic->ic_raw_xmit(ni, m, params); +} + +/* + * Send a null data frame to the specified node. If the station + * is setup for QoS then a QoS Null Data frame is constructed. + * If this is a WDS station then a 4-address frame is constructed. + * + * NB: the caller is assumed to have setup a node reference + * for use; this is necessary to deal with a race condition + * when probing for inactive stations. Like ieee80211_mgmt_output + * we must cleanup any node reference on error; however we + * can safely just unref it as we know it will never be the + * last reference to the node. + */ +int +ieee80211_send_nulldata(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct mbuf *m; + struct ieee80211_frame *wh; + int hdrlen; + uint8_t *frm; + + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, + ni, "block %s frame in CAC state", "null data"); + ieee80211_unref_node(&ni); + vap->iv_stats.is_tx_badstate++; + return EIO; /* XXX */ + } + + if (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) + hdrlen = sizeof(struct ieee80211_qosframe); + else + hdrlen = sizeof(struct ieee80211_frame); + /* NB: only WDS vap's get 4-address frames */ + if (vap->iv_opmode == IEEE80211_M_WDS) + hdrlen += IEEE80211_ADDR_LEN; + if (ic->ic_flags & IEEE80211_F_DATAPAD) + hdrlen = roundup(hdrlen, sizeof(uint32_t)); + + m = ieee80211_getmgtframe(&frm, ic->ic_headroom + hdrlen, 0); + if (m == NULL) { + /* XXX debug msg */ + ieee80211_unref_node(&ni); + vap->iv_stats.is_tx_nobuf++; + return ENOMEM; + } + KASSERT(M_LEADINGSPACE(m) >= hdrlen, + ("leading space %zd", M_LEADINGSPACE(m))); + M_PREPEND(m, hdrlen, M_DONTWAIT); + if (m == NULL) { + /* NB: cannot happen */ + ieee80211_free_node(ni); + return ENOMEM; + } + + wh = mtod(m, struct ieee80211_frame *); /* NB: a little lie */ + if (ni->ni_flags & IEEE80211_NODE_QOS) { + const int tid = WME_AC_TO_TID(WME_AC_BE); + uint8_t *qos; + + ieee80211_send_setup(ni, m, + IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS_NULL, + tid, vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); + + if (vap->iv_opmode == IEEE80211_M_WDS) + qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; + else + qos = ((struct ieee80211_qosframe *) wh)->i_qos; + qos[0] = tid & IEEE80211_QOS_TID; + if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[WME_AC_BE].wmep_noackPolicy) + qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; + qos[1] = 0; + } else { + ieee80211_send_setup(ni, m, + IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, + IEEE80211_NONQOS_TID, + vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); + } + if (vap->iv_opmode != IEEE80211_M_WDS) { + /* NB: power management bit is never sent by an AP */ + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + vap->iv_opmode != IEEE80211_M_HOSTAP) + wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; + } + m->m_len = m->m_pkthdr.len = hdrlen; + m->m_flags |= M_ENCAP; /* mark encapsulated */ + + M_WME_SETAC(m, WME_AC_BE); + + IEEE80211_NODE_STAT(ni, tx_data); + + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, ni, + "send %snull data frame on channel %u, pwr mgt %s", + ni->ni_flags & IEEE80211_NODE_QOS ? "QoS " : "", + ieee80211_chan2ieee(ic, ic->ic_curchan), + wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); + + return ic->ic_raw_xmit(ni, m, NULL); +} + +/* + * Assign priority to a frame based on any vlan tag assigned + * to the station and/or any Diffserv setting in an IP header. + * Finally, if an ACM policy is setup (in station mode) it's + * applied. + */ +int +ieee80211_classify(struct ieee80211_node *ni, struct mbuf *m) +{ + const struct ether_header *eh = mtod(m, struct ether_header *); + int v_wme_ac, d_wme_ac, ac; + + /* + * Always promote PAE/EAPOL frames to high priority. + */ + if (eh->ether_type == htons(ETHERTYPE_PAE)) { + /* NB: mark so others don't need to check header */ + m->m_flags |= M_EAPOL; + ac = WME_AC_VO; + goto done; + } + /* + * Non-qos traffic goes to BE. + */ + if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) { + ac = WME_AC_BE; + goto done; + } + + /* + * If node has a vlan tag then all traffic + * to it must have a matching tag. + */ + v_wme_ac = 0; + if (ni->ni_vlan != 0) { + if ((m->m_flags & M_VLANTAG) == 0) { + IEEE80211_NODE_STAT(ni, tx_novlantag); + return 1; + } + if (EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) != + EVL_VLANOFTAG(ni->ni_vlan)) { + IEEE80211_NODE_STAT(ni, tx_vlanmismatch); + return 1; + } + /* map vlan priority to AC */ + v_wme_ac = TID_TO_WME_AC(EVL_PRIOFTAG(ni->ni_vlan)); + } + + /* XXX m_copydata may be too slow for fast path */ +#ifdef INET + if (eh->ether_type == htons(ETHERTYPE_IP)) { + uint8_t tos; + /* + * IP frame, map the DSCP bits from the TOS field. + */ + /* NB: ip header may not be in first mbuf */ + m_copydata(m, sizeof(struct ether_header) + + offsetof(struct ip, ip_tos), sizeof(tos), &tos); + tos >>= 5; /* NB: ECN + low 3 bits of DSCP */ + d_wme_ac = TID_TO_WME_AC(tos); + } else { +#endif /* INET */ +#ifdef INET6 + if (eh->ether_type == htons(ETHERTYPE_IPV6)) { + uint32_t flow; + uint8_t tos; + /* + * IPv6 frame, map the DSCP bits from the TOS field. + */ + m_copydata(m, sizeof(struct ether_header) + + offsetof(struct ip6_hdr, ip6_flow), sizeof(flow), + (caddr_t) &flow); + tos = (uint8_t)(ntohl(flow) >> 20); + tos >>= 5; /* NB: ECN + low 3 bits of DSCP */ + d_wme_ac = TID_TO_WME_AC(tos); + } else { +#endif /* INET6 */ + d_wme_ac = WME_AC_BE; +#ifdef INET6 + } +#endif +#ifdef INET + } +#endif + /* + * Use highest priority AC. + */ + if (v_wme_ac > d_wme_ac) + ac = v_wme_ac; + else + ac = d_wme_ac; + + /* + * Apply ACM policy. + */ + if (ni->ni_vap->iv_opmode == IEEE80211_M_STA) { + static const int acmap[4] = { + WME_AC_BK, /* WME_AC_BE */ + WME_AC_BK, /* WME_AC_BK */ + WME_AC_BE, /* WME_AC_VI */ + WME_AC_VI, /* WME_AC_VO */ + }; + struct ieee80211com *ic = ni->ni_ic; + + while (ac != WME_AC_BK && + ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm) + ac = acmap[ac]; + } +done: + M_WME_SETAC(m, ac); + return 0; +} + +/* + * Insure there is sufficient contiguous space to encapsulate the + * 802.11 data frame. If room isn't already there, arrange for it. + * Drivers and cipher modules assume we have done the necessary work + * and fail rudely if they don't find the space they need. + */ +struct mbuf * +ieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize, + struct ieee80211_key *key, struct mbuf *m) +{ +#define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) + int needed_space = vap->iv_ic->ic_headroom + hdrsize; + + if (key != NULL) { + /* XXX belongs in crypto code? */ + needed_space += key->wk_cipher->ic_header; + /* XXX frags */ + /* + * When crypto is being done in the host we must insure + * the data are writable for the cipher routines; clone + * a writable mbuf chain. + * XXX handle SWMIC specially + */ + if (key->wk_flags & (IEEE80211_KEY_SWENCRYPT|IEEE80211_KEY_SWENMIC)) { + m = m_unshare(m, M_NOWAIT); + if (m == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "%s: cannot get writable mbuf\n", __func__); + vap->iv_stats.is_tx_nobuf++; /* XXX new stat */ + return NULL; + } + } + } + /* + * We know we are called just before stripping an Ethernet + * header and prepending an LLC header. This means we know + * there will be + * sizeof(struct ether_header) - sizeof(struct llc) + * bytes recovered to which we need additional space for the + * 802.11 header and any crypto header. + */ + /* XXX check trailing space and copy instead? */ + if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) { + struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type); + if (n == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "%s: cannot expand storage\n", __func__); + vap->iv_stats.is_tx_nobuf++; + m_freem(m); + return NULL; + } + KASSERT(needed_space <= MHLEN, + ("not enough room, need %u got %zu\n", needed_space, MHLEN)); + /* + * Setup new mbuf to have leading space to prepend the + * 802.11 header and any crypto header bits that are + * required (the latter are added when the driver calls + * back to ieee80211_crypto_encap to do crypto encapsulation). + */ + /* NB: must be first 'cuz it clobbers m_data */ + m_move_pkthdr(n, m); + n->m_len = 0; /* NB: m_gethdr does not set */ + n->m_data += needed_space; + /* + * Pull up Ethernet header to create the expected layout. + * We could use m_pullup but that's overkill (i.e. we don't + * need the actual data) and it cannot fail so do it inline + * for speed. + */ + /* NB: struct ether_header is known to be contiguous */ + n->m_len += sizeof(struct ether_header); + m->m_len -= sizeof(struct ether_header); + m->m_data += sizeof(struct ether_header); + /* + * Replace the head of the chain. + */ + n->m_next = m; + m = n; + } + return m; +#undef TO_BE_RECLAIMED +} + +/* + * Return the transmit key to use in sending a unicast frame. + * If a unicast key is set we use that. When no unicast key is set + * we fall back to the default transmit key. + */ +static __inline struct ieee80211_key * +ieee80211_crypto_getucastkey(struct ieee80211vap *vap, + struct ieee80211_node *ni) +{ + if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) { + if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || + IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) + return NULL; + return &vap->iv_nw_keys[vap->iv_def_txkey]; + } else { + return &ni->ni_ucastkey; + } +} + +/* + * Return the transmit key to use in sending a multicast frame. + * Multicast traffic always uses the group key which is installed as + * the default tx key. + */ +static __inline struct ieee80211_key * +ieee80211_crypto_getmcastkey(struct ieee80211vap *vap, + struct ieee80211_node *ni) +{ + if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || + IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) + return NULL; + return &vap->iv_nw_keys[vap->iv_def_txkey]; +} + +/* + * Encapsulate an outbound data frame. The mbuf chain is updated. + * If an error is encountered NULL is returned. The caller is required + * to provide a node reference and pullup the ethernet header in the + * first mbuf. + * + * NB: Packet is assumed to be processed by ieee80211_classify which + * marked EAPOL frames w/ M_EAPOL. + */ +struct mbuf * +ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, + struct mbuf *m) +{ +#define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh)) + struct ieee80211com *ic = ni->ni_ic; +#ifdef IEEE80211_SUPPORT_MESH + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_meshcntl_ae10 *mc; +#endif + struct ether_header eh; + struct ieee80211_frame *wh; + struct ieee80211_key *key; + struct llc *llc; + int hdrsize, hdrspace, datalen, addqos, txfrag, is4addr; + ieee80211_seq seqno; + int meshhdrsize, meshae; + uint8_t *qos; + + /* + * Copy existing Ethernet header to a safe place. The + * rest of the code assumes it's ok to strip it when + * reorganizing state for the final encapsulation. + */ + KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); + ETHER_HEADER_COPY(&eh, mtod(m, caddr_t)); + + /* + * Insure space for additional headers. First identify + * transmit key to use in calculating any buffer adjustments + * required. This is also used below to do privacy + * encapsulation work. Then calculate the 802.11 header + * size and any padding required by the driver. + * + * Note key may be NULL if we fall back to the default + * transmit key and that is not set. In that case the + * buffer may not be expanded as needed by the cipher + * routines, but they will/should discard it. + */ + if (vap->iv_flags & IEEE80211_F_PRIVACY) { + if (vap->iv_opmode == IEEE80211_M_STA || + !IEEE80211_IS_MULTICAST(eh.ether_dhost) || + (vap->iv_opmode == IEEE80211_M_WDS && + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))) + key = ieee80211_crypto_getucastkey(vap, ni); + else + key = ieee80211_crypto_getmcastkey(vap, ni); + if (key == NULL && (m->m_flags & M_EAPOL) == 0) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, + eh.ether_dhost, + "no default transmit key (%s) deftxkey %u", + __func__, vap->iv_def_txkey); + vap->iv_stats.is_tx_nodefkey++; + goto bad; + } + } else + key = NULL; + /* + * XXX Some ap's don't handle QoS-encapsulated EAPOL + * frames so suppress use. This may be an issue if other + * ap's require all data frames to be QoS-encapsulated + * once negotiated in which case we'll need to make this + * configurable. + */ + addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) && + (m->m_flags & M_EAPOL) == 0; + if (addqos) + hdrsize = sizeof(struct ieee80211_qosframe); + else + hdrsize = sizeof(struct ieee80211_frame); +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode == IEEE80211_M_MBSS) { + /* + * Mesh data frames are encapsulated according to the + * rules of Section 11B.8.5 (p.139 of D3.0 spec). + * o Group Addressed data (aka multicast) originating + * at the local sta are sent w/ 3-address format and + * address extension mode 00 + * o Individually Addressed data (aka unicast) originating + * at the local sta are sent w/ 4-address format and + * address extension mode 00 + * o Group Addressed data forwarded from a non-mesh sta are + * sent w/ 3-address format and address extension mode 01 + * o Individually Address data from another sta are sent + * w/ 4-address format and address extension mode 10 + */ + is4addr = 0; /* NB: don't use, disable */ + if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) + hdrsize += IEEE80211_ADDR_LEN; /* unicast are 4-addr */ + meshhdrsize = sizeof(struct ieee80211_meshcntl); + /* XXX defines for AE modes */ + if (IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) { + if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) + meshae = 0; + else + meshae = 4; /* NB: pseudo */ + } else if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) { + meshae = 1; + meshhdrsize += 1*IEEE80211_ADDR_LEN; + } else { + meshae = 2; + meshhdrsize += 2*IEEE80211_ADDR_LEN; + } + } else { +#endif + /* + * 4-address frames need to be generated for: + * o packets sent through a WDS vap (IEEE80211_M_WDS) + * o packets sent through a vap marked for relaying + * (e.g. a station operating with dynamic WDS) + */ + is4addr = vap->iv_opmode == IEEE80211_M_WDS || + ((vap->iv_flags_ext & IEEE80211_FEXT_4ADDR) && + !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)); + if (is4addr) + hdrsize += IEEE80211_ADDR_LEN; + meshhdrsize = meshae = 0; +#ifdef IEEE80211_SUPPORT_MESH + } +#endif + /* + * Honor driver DATAPAD requirement. + */ + if (ic->ic_flags & IEEE80211_F_DATAPAD) + hdrspace = roundup(hdrsize, sizeof(uint32_t)); + else + hdrspace = hdrsize; + + if (__predict_true((m->m_flags & M_FF) == 0)) { + /* + * Normal frame. + */ + m = ieee80211_mbuf_adjust(vap, hdrspace + meshhdrsize, key, m); + if (m == NULL) { + /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ + goto bad; + } + /* NB: this could be optimized 'cuz of ieee80211_mbuf_adjust */ + 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; + } else { +#ifdef IEEE80211_SUPPORT_SUPERG + /* + * Aggregated frame. + */ + m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key); + if (m == NULL) +#endif + goto bad; + } + datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ + + M_PREPEND(m, hdrspace + meshhdrsize, M_DONTWAIT); + if (m == NULL) { + vap->iv_stats.is_tx_nobuf++; + goto bad; + } + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; + *(uint16_t *)wh->i_dur = 0; + qos = NULL; /* NB: quiet compiler */ + if (is4addr) { + wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); + } else switch (vap->iv_opmode) { + case IEEE80211_M_STA: + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); + /* + * NB: always use the bssid from iv_bss as the + * neighbor's may be stale after an ibss merge + */ + IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_bssid); + break; + case IEEE80211_M_HOSTAP: + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); + break; +#ifdef IEEE80211_SUPPORT_MESH + case IEEE80211_M_MBSS: + /* NB: offset by hdrspace to deal with DATAPAD */ + mc = (struct ieee80211_meshcntl_ae10 *) + (mtod(m, uint8_t *) + hdrspace); + switch (meshae) { + case 0: /* ucast, no proxy */ + wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); + mc->mc_flags = 0; + qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; + break; + case 4: /* mcast, no proxy */ + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); + mc->mc_flags = 0; /* NB: AE is really 0 */ + qos = ((struct ieee80211_qosframe *) wh)->i_qos; + break; + case 1: /* mcast, proxy */ + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_myaddr); + mc->mc_flags = 1; + IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_shost); + qos = ((struct ieee80211_qosframe *) wh)->i_qos; + break; + case 2: /* ucast, proxy */ + wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + /* XXX not right, need MeshDA */ + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); + /* XXX assume are MeshSA */ + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, vap->iv_myaddr); + mc->mc_flags = 2; + IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_dhost); + IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_shost); + qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; + break; + default: + KASSERT(0, ("meshae %d", meshae)); + break; + } + mc->mc_ttl = ms->ms_ttl; + ms->ms_seq++; + LE_WRITE_4(mc->mc_seq, ms->ms_seq); + break; +#endif + case IEEE80211_M_WDS: /* NB: is4addr should always be true */ + default: + goto bad; + } + if (m->m_flags & M_MORE_DATA) + wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; + if (addqos) { + int ac, tid; + + if (is4addr) { + qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; + /* NB: mesh case handled earlier */ + } else if (vap->iv_opmode != IEEE80211_M_MBSS) + qos = ((struct ieee80211_qosframe *) wh)->i_qos; + ac = M_WME_GETAC(m); + /* map from access class/queue to 11e header priorty value */ + tid = WME_AC_TO_TID(ac); + qos[0] = tid & IEEE80211_QOS_TID; + if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) + qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; + qos[1] = 0; + wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; + + if ((m->m_flags & M_AMPDU_MPDU) == 0) { + /* + * NB: don't assign a sequence # to potential + * aggregates; we expect this happens at the + * point the frame comes off any aggregation q + * as otherwise we may introduce holes in the + * BA sequence space and/or make window accouting + * more difficult. + * + * XXX may want to control this with a driver + * capability; this may also change when we pull + * aggregation up into net80211 + */ + seqno = ni->ni_txseqs[tid]++; + *(uint16_t *)wh->i_seq = + htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + M_SEQNO_SET(m, seqno); + } + } else { + seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; + *(uint16_t *)wh->i_seq = + htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + M_SEQNO_SET(m, seqno); + } + + + /* check if xmit fragmentation is required */ + txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold && + !IEEE80211_IS_MULTICAST(wh->i_addr1) && + (vap->iv_caps & IEEE80211_C_TXFRAG) && + (m->m_flags & (M_FF | M_AMPDU_MPDU)) == 0); + if (key != NULL) { + /* + * IEEE 802.1X: send EAPOL frames always in the clear. + * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set. + */ + if ((m->m_flags & M_EAPOL) == 0 || + ((vap->iv_flags & IEEE80211_F_WPA) && + (vap->iv_opmode == IEEE80211_M_STA ? + !IEEE80211_KEY_UNDEFINED(key) : + !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) { + wh->i_fc[1] |= IEEE80211_FC1_WEP; + if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, + eh.ether_dhost, + "%s", "enmic failed, discard frame"); + vap->iv_stats.is_crypto_enmicfail++; + goto bad; + } + } + } + if (txfrag && !ieee80211_fragment(vap, m, hdrsize, + key != NULL ? key->wk_cipher->ic_header : 0, vap->iv_fragthreshold)) + goto bad; + + m->m_flags |= M_ENCAP; /* mark encapsulated */ + + IEEE80211_NODE_STAT(ni, tx_data); + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { + IEEE80211_NODE_STAT(ni, tx_mcast); + m->m_flags |= M_MCAST; + } else + IEEE80211_NODE_STAT(ni, tx_ucast); + IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); + + return m; +bad: + if (m != NULL) + m_freem(m); + return NULL; +#undef WH4 +} + +/* + * Fragment the frame according to the specified mtu. + * The size of the 802.11 header (w/o padding) is provided + * so we don't need to recalculate it. We create a new + * mbuf for each fragment and chain it through m_nextpkt; + * we might be able to optimize this by reusing the original + * packet's mbufs but that is significantly more complicated. + */ +static int +ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, + u_int hdrsize, u_int ciphdrsize, u_int mtu) +{ + struct ieee80211_frame *wh, *whf; + struct mbuf *m, *prev, *next; + u_int totalhdrsize, fragno, fragsize, off, remainder, payload; + + KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?")); + KASSERT(m0->m_pkthdr.len > mtu, + ("pktlen %u mtu %u", m0->m_pkthdr.len, mtu)); + + wh = mtod(m0, struct ieee80211_frame *); + /* NB: mark the first frag; it will be propagated below */ + wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG; + totalhdrsize = hdrsize + ciphdrsize; + fragno = 1; + off = mtu - ciphdrsize; + remainder = m0->m_pkthdr.len - off; + prev = m0; + do { + fragsize = totalhdrsize + remainder; + if (fragsize > mtu) + fragsize = mtu; + /* XXX fragsize can be >2048! */ + KASSERT(fragsize < MCLBYTES, + ("fragment size %u too big!", fragsize)); + if (fragsize > MHLEN) + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + else + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) + goto bad; + /* leave room to prepend any cipher header */ + m_align(m, fragsize - ciphdrsize); + + /* + * Form the header in the fragment. Note that since + * we mark the first fragment with the MORE_FRAG bit + * it automatically is propagated to each fragment; we + * need only clear it on the last fragment (done below). + */ + whf = mtod(m, struct ieee80211_frame *); + memcpy(whf, wh, hdrsize); + *(uint16_t *)&whf->i_seq[0] |= htole16( + (fragno & IEEE80211_SEQ_FRAG_MASK) << + IEEE80211_SEQ_FRAG_SHIFT); + fragno++; + + payload = fragsize - totalhdrsize; + /* NB: destination is known to be contiguous */ + m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrsize); + m->m_len = hdrsize + payload; + m->m_pkthdr.len = hdrsize + payload; + m->m_flags |= M_FRAG; + + /* chain up the fragment */ + prev->m_nextpkt = m; + prev = m; + + /* deduct fragment just formed */ + remainder -= payload; + off += payload; + } while (remainder != 0); + + /* set the last fragment */ + m->m_flags |= M_LASTFRAG; + whf->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG; + + /* strip first mbuf now that everything has been copied */ + m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize))); + m0->m_flags |= M_FIRSTFRAG | M_FRAG; + + vap->iv_stats.is_tx_fragframes++; + vap->iv_stats.is_tx_frags += fragno-1; + + return 1; +bad: + /* reclaim fragments but leave original frame for caller to free */ + for (m = m0->m_nextpkt; m != NULL; m = next) { + next = m->m_nextpkt; + m->m_nextpkt = NULL; /* XXX paranoid */ + m_freem(m); + } + m0->m_nextpkt = NULL; + return 0; +} + +/* + * Add a supported rates element id to a frame. + */ +uint8_t * +ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs) +{ + int nrates; + + *frm++ = IEEE80211_ELEMID_RATES; + nrates = rs->rs_nrates; + if (nrates > IEEE80211_RATE_SIZE) + nrates = IEEE80211_RATE_SIZE; + *frm++ = nrates; + memcpy(frm, rs->rs_rates, nrates); + return frm + nrates; +} + +/* + * Add an extended supported rates element id to a frame. + */ +uint8_t * +ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) +{ + /* + * Add an extended supported rates element if operating in 11g mode. + */ + if (rs->rs_nrates > IEEE80211_RATE_SIZE) { + int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; + *frm++ = IEEE80211_ELEMID_XRATES; + *frm++ = nrates; + memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); + frm += nrates; + } + return frm; +} + +/* + * Add an ssid element to a frame. + */ +static uint8_t * +ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) +{ + *frm++ = IEEE80211_ELEMID_SSID; + *frm++ = len; + memcpy(frm, ssid, len); + return frm + len; +} + +/* + * Add an erp element to a frame. + */ +static uint8_t * +ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic) +{ + uint8_t erp; + + *frm++ = IEEE80211_ELEMID_ERP; + *frm++ = 1; + erp = 0; + if (ic->ic_nonerpsta != 0) + erp |= IEEE80211_ERP_NON_ERP_PRESENT; + if (ic->ic_flags & IEEE80211_F_USEPROT) + erp |= IEEE80211_ERP_USE_PROTECTION; + if (ic->ic_flags & IEEE80211_F_USEBARKER) + erp |= IEEE80211_ERP_LONG_PREAMBLE; + *frm++ = erp; + return frm; +} + +/* + * Add a CFParams element to a frame. + */ +static uint8_t * +ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic) +{ +#define ADDSHORT(frm, v) do { \ + LE_WRITE_2(frm, v); \ + frm += 2; \ +} while (0) + *frm++ = IEEE80211_ELEMID_CFPARMS; + *frm++ = 6; + *frm++ = 0; /* CFP count */ + *frm++ = 2; /* CFP period */ + ADDSHORT(frm, 0); /* CFP MaxDuration (TU) */ + ADDSHORT(frm, 0); /* CFP CurRemaining (TU) */ + return frm; +#undef ADDSHORT +} + +static __inline uint8_t * +add_appie(uint8_t *frm, const struct ieee80211_appie *ie) +{ + memcpy(frm, ie->ie_data, ie->ie_len); + return frm + ie->ie_len; +} + +static __inline uint8_t * +add_ie(uint8_t *frm, const uint8_t *ie) +{ + memcpy(frm, ie, 2 + ie[1]); + return frm + 2 + ie[1]; +} + +#define WME_OUI_BYTES 0x00, 0x50, 0xf2 +/* + * Add a WME information element to a frame. + */ +static uint8_t * +ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme) +{ + static const struct ieee80211_wme_info info = { + .wme_id = IEEE80211_ELEMID_VENDOR, + .wme_len = sizeof(struct ieee80211_wme_info) - 2, + .wme_oui = { WME_OUI_BYTES }, + .wme_type = WME_OUI_TYPE, + .wme_subtype = WME_INFO_OUI_SUBTYPE, + .wme_version = WME_VERSION, + .wme_info = 0, + }; + memcpy(frm, &info, sizeof(info)); + return frm + sizeof(info); +} + +/* + * Add a WME parameters element to a frame. + */ +static uint8_t * +ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme) +{ +#define SM(_v, _f) (((_v) << _f##_S) & _f) +#define ADDSHORT(frm, v) do { \ + LE_WRITE_2(frm, v); \ + frm += 2; \ +} while (0) + /* NB: this works 'cuz a param has an info at the front */ + static const struct ieee80211_wme_info param = { + .wme_id = IEEE80211_ELEMID_VENDOR, + .wme_len = sizeof(struct ieee80211_wme_param) - 2, + .wme_oui = { WME_OUI_BYTES }, + .wme_type = WME_OUI_TYPE, + .wme_subtype = WME_PARAM_OUI_SUBTYPE, + .wme_version = WME_VERSION, + }; + int i; + + memcpy(frm, ¶m, sizeof(param)); + frm += __offsetof(struct ieee80211_wme_info, wme_info); + *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */ + *frm++ = 0; /* reserved field */ + for (i = 0; i < WME_NUM_AC; i++) { + const struct wmeParams *ac = + &wme->wme_bssChanParams.cap_wmeParams[i]; + *frm++ = SM(i, WME_PARAM_ACI) + | SM(ac->wmep_acm, WME_PARAM_ACM) + | SM(ac->wmep_aifsn, WME_PARAM_AIFSN) + ; + *frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX) + | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN) + ; + ADDSHORT(frm, ac->wmep_txopLimit); + } + return frm; +#undef SM +#undef ADDSHORT +} +#undef WME_OUI_BYTES + +/* + * Add an 11h Power Constraint element to a frame. + */ +static uint8_t * +ieee80211_add_powerconstraint(uint8_t *frm, struct ieee80211vap *vap) +{ + const struct ieee80211_channel *c = vap->iv_bss->ni_chan; + /* XXX per-vap tx power limit? */ + int8_t limit = vap->iv_ic->ic_txpowlimit / 2; + + frm[0] = IEEE80211_ELEMID_PWRCNSTR; + frm[1] = 1; + frm[2] = c->ic_maxregpower > limit ? c->ic_maxregpower - limit : 0; + return frm + 3; +} + +/* + * Add an 11h Power Capability element to a frame. + */ +static uint8_t * +ieee80211_add_powercapability(uint8_t *frm, const struct ieee80211_channel *c) +{ + frm[0] = IEEE80211_ELEMID_PWRCAP; + frm[1] = 2; + frm[2] = c->ic_minpower; + frm[3] = c->ic_maxpower; + return frm + 4; +} + +/* + * Add an 11h Supported Channels element to a frame. + */ +static uint8_t * +ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic) +{ + static const int ielen = 26; + + frm[0] = IEEE80211_ELEMID_SUPPCHAN; + frm[1] = ielen; + /* XXX not correct */ + memcpy(frm+2, ic->ic_chan_avail, ielen); + return frm + 2 + ielen; +} + +/* + * Add an 11h Channel Switch Announcement element to a frame. + * Note that we use the per-vap CSA count to adjust the global + * counter so we can use this routine to form probe response + * frames and get the current count. + */ +static uint8_t * +ieee80211_add_csa(uint8_t *frm, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_csa_ie *csa = (struct ieee80211_csa_ie *) frm; + + csa->csa_ie = IEEE80211_ELEMID_CSA; + csa->csa_len = 3; + csa->csa_mode = 1; /* XXX force quiet on channel */ + csa->csa_newchan = ieee80211_chan2ieee(ic, ic->ic_csa_newchan); + csa->csa_count = ic->ic_csa_count - vap->iv_csa_count; + return frm + sizeof(*csa); +} + +/* + * Add an 11h country information element to a frame. + */ +static uint8_t * +ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic) +{ + + if (ic->ic_countryie == NULL || + ic->ic_countryie_chan != ic->ic_bsschan) { + /* + * Handle lazy construction of ie. This is done on + * first use and after a channel change that requires + * re-calculation. + */ + if (ic->ic_countryie != NULL) + free(ic->ic_countryie, M_80211_NODE_IE); + ic->ic_countryie = ieee80211_alloc_countryie(ic); + if (ic->ic_countryie == NULL) + return frm; + ic->ic_countryie_chan = ic->ic_bsschan; + } + return add_appie(frm, ic->ic_countryie); +} + +/* + * Send a probe request frame with the specified ssid + * and any optional information element data. + */ +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) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ieee80211_bpf_params params; + struct ieee80211_frame *wh; + const struct ieee80211_rateset *rs; + struct mbuf *m; + uint8_t *frm; + + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, + "block %s frame in CAC state", "probe request"); + vap->iv_stats.is_tx_badstate++; + return EIO; /* XXX */ + } + + /* + * Hold a reference on the node so it doesn't go away until after + * the xmit is complete all the way in the driver. On error we + * will remove our reference. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", + __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + /* + * prreq frame format + * [tlv] ssid + * [tlv] supported rates + * [tlv] RSN (optional) + * [tlv] extended supported rates + * [tlv] WPA (optional) + * [tlv] user-specified ie's + */ + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + 2 + IEEE80211_NWID_LEN + + 2 + IEEE80211_RATE_SIZE + + sizeof(struct ieee80211_ie_wpa) + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + sizeof(struct ieee80211_ie_wpa) + + (vap->iv_appie_probereq != NULL ? + vap->iv_appie_probereq->ie_len : 0) + ); + if (m == NULL) { + vap->iv_stats.is_tx_nobuf++; + ieee80211_free_node(ni); + return ENOMEM; + } + + frm = ieee80211_add_ssid(frm, ssid, ssidlen); + rs = ieee80211_get_suprates(ic, ic->ic_curchan); + frm = ieee80211_add_rates(frm, rs); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain? */ + } + frm = ieee80211_add_xrates(frm, rs); + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain? */ + } + if (vap->iv_appie_probereq != NULL) + frm = add_appie(frm, vap->iv_appie_probereq); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + + KASSERT(M_LEADINGSPACE(m) >= sizeof(struct ieee80211_frame), + ("leading space %zd", M_LEADINGSPACE(m))); + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + if (m == NULL) { + /* NB: cannot happen */ + ieee80211_free_node(ni); + return ENOMEM; + } + + wh = mtod(m, struct ieee80211_frame *); + ieee80211_send_setup(ni, m, + IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, + IEEE80211_NONQOS_TID, sa, da, bssid); + /* XXX power management? */ + m->m_flags |= M_ENCAP; /* mark encapsulated */ + + M_WME_SETAC(m, WME_AC_BE); + + IEEE80211_NODE_STAT(ni, tx_probereq); + IEEE80211_NODE_STAT(ni, tx_mgmt); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + "send probe req on channel %u bssid %s ssid \"%.*s\"\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid), + ssidlen, ssid); + + memset(¶ms, 0, sizeof(params)); + params.ibp_pri = M_WME_GETAC(m); + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + params.ibp_rate0 = tp->mgmtrate; + if (IEEE80211_IS_MULTICAST(da)) { + params.ibp_flags |= IEEE80211_BPF_NOACK; + params.ibp_try0 = 1; + } else + params.ibp_try0 = tp->maxretry; + params.ibp_power = ni->ni_txpower; + return ic->ic_raw_xmit(ni, m, ¶ms); +} + +/* + * Calculate capability information for mgt frames. + */ +uint16_t +ieee80211_getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan) +{ + struct ieee80211com *ic = vap->iv_ic; + uint16_t capinfo; + + KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode")); + + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + capinfo = IEEE80211_CAPINFO_ESS; + else if (vap->iv_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; + else + capinfo = 0; + if (vap->iv_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + if (IEEE80211_IS_CHAN_5GHZ(chan) && (vap->iv_flags & IEEE80211_F_DOTH)) + capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; + return capinfo; +} + +/* + * Send a management frame. The node is for the destination (or ic_bss + * when in station mode). Nodes other than ic_bss have their reference + * count bumped to reflect our use for an indeterminant time. + */ +int +ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) +{ +#define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT) +#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_node *bss = vap->iv_bss; + struct ieee80211_bpf_params params; + struct mbuf *m; + uint8_t *frm; + uint16_t capinfo; + int has_challenge, is_shared_key, ret, status; + + KASSERT(ni != NULL, ("null node")); + + /* + * Hold a reference on the node so it doesn't go away until after + * the xmit is complete all the way in the driver. On error we + * will remove our reference. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", + __func__, __LINE__, + ni, ether_sprintf(ni->ni_macaddr), + ieee80211_node_refcnt(ni)+1); + ieee80211_ref_node(ni); + + memset(¶ms, 0, sizeof(params)); + switch (type) { + + case IEEE80211_FC0_SUBTYPE_AUTH: + status = arg >> 16; + arg &= 0xffff; + has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || + arg == IEEE80211_AUTH_SHARED_RESPONSE) && + ni->ni_challenge != NULL); + + /* + * Deduce whether we're doing open authentication or + * shared key authentication. We do the latter if + * we're in the middle of a shared key authentication + * handshake or if we're initiating an authentication + * request and configured to use shared key. + */ + is_shared_key = has_challenge || + arg >= IEEE80211_AUTH_SHARED_RESPONSE || + (arg == IEEE80211_AUTH_SHARED_REQUEST && + bss->ni_authmode == IEEE80211_AUTH_SHARED); + + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + 3 * sizeof(uint16_t) + + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? + sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0) + ); + if (m == NULL) + senderr(ENOMEM, is_tx_nobuf); + + ((uint16_t *)frm)[0] = + (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) + : htole16(IEEE80211_AUTH_ALG_OPEN); + ((uint16_t *)frm)[1] = htole16(arg); /* sequence number */ + ((uint16_t *)frm)[2] = htole16(status);/* status */ + + if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { + ((uint16_t *)frm)[3] = + htole16((IEEE80211_CHALLENGE_LEN << 8) | + IEEE80211_ELEMID_CHALLENGE); + memcpy(&((uint16_t *)frm)[4], ni->ni_challenge, + IEEE80211_CHALLENGE_LEN); + m->m_pkthdr.len = m->m_len = + 4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN; + if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { + IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, + "request encrypt frame (%s)", __func__); + /* mark frame for encryption */ + params.ibp_flags |= IEEE80211_BPF_CRYPTO; + } + } else + m->m_pkthdr.len = m->m_len = 3 * sizeof(uint16_t); + + /* XXX not right for shared key */ + if (status == IEEE80211_STATUS_SUCCESS) + IEEE80211_NODE_STAT(ni, tx_auth); + else + IEEE80211_NODE_STAT(ni, tx_auth_fail); + + if (vap->iv_opmode == IEEE80211_M_STA) + ieee80211_add_callback(m, ieee80211_tx_mgt_cb, + (void *) vap->iv_state); + break; + + case IEEE80211_FC0_SUBTYPE_DEAUTH: + IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, + "send station deauthenticate (reason %d)", arg); + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t)); + if (m == NULL) + senderr(ENOMEM, is_tx_nobuf); + *(uint16_t *)frm = htole16(arg); /* reason */ + m->m_pkthdr.len = m->m_len = sizeof(uint16_t); + + IEEE80211_NODE_STAT(ni, tx_deauth); + IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg); + + ieee80211_node_unauthorize(ni); /* port closed */ + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + /* + * asreq frame format + * [2] capability information + * [2] listen interval + * [6*] current AP address (reassoc only) + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + * [4] power capability (optional) + * [28] supported channels (optional) + * [tlv] HT capabilities + * [tlv] WME (optional) + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Atheros capabilities (if negotiated) + * [tlv] AppIE's (optional) + */ + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) + + sizeof(uint16_t) + + IEEE80211_ADDR_LEN + + 2 + IEEE80211_NWID_LEN + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + 4 + + 2 + 26 + + sizeof(struct ieee80211_wme_info) + + sizeof(struct ieee80211_ie_htcap) + + 4 + sizeof(struct ieee80211_ie_htcap) +#ifdef IEEE80211_SUPPORT_SUPERG + + sizeof(struct ieee80211_ath_ie) +#endif + + (vap->iv_appie_wpa != NULL ? + vap->iv_appie_wpa->ie_len : 0) + + (vap->iv_appie_assocreq != NULL ? + vap->iv_appie_assocreq->ie_len : 0) + ); + if (m == NULL) + senderr(ENOMEM, is_tx_nobuf); + + KASSERT(vap->iv_opmode == IEEE80211_M_STA, + ("wrong mode %u", vap->iv_opmode)); + capinfo = IEEE80211_CAPINFO_ESS; + if (vap->iv_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + /* + * NB: Some 11a AP's reject the request when + * short premable is set. + */ + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && + (ic->ic_caps & IEEE80211_C_SHSLOT)) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) && + (vap->iv_flags & IEEE80211_F_DOTH)) + capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; + *(uint16_t *)frm = htole16(capinfo); + frm += 2; + + KASSERT(bss->ni_intval != 0, ("beacon interval is zero!")); + *(uint16_t *)frm = htole16(howmany(ic->ic_lintval, + bss->ni_intval)); + frm += 2; + + if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { + IEEE80211_ADDR_COPY(frm, bss->ni_bssid); + frm += IEEE80211_ADDR_LEN; + } + + frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); + frm = ieee80211_add_rates(frm, &ni->ni_rates); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain? */ + } + frm = ieee80211_add_xrates(frm, &ni->ni_rates); + if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) { + frm = ieee80211_add_powercapability(frm, + ic->ic_curchan); + frm = ieee80211_add_supportedchannels(frm, ic); + } + if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && + ni->ni_ies.htcap_ie != NULL && + ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) + frm = ieee80211_add_htcap(frm, ni); + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain */ + } + if ((ic->ic_flags & IEEE80211_F_WME) && + ni->ni_ies.wme_ie != NULL) + frm = ieee80211_add_wme_info(frm, &ic->ic_wme); + if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && + ni->ni_ies.htcap_ie != NULL && + ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR) + frm = ieee80211_add_htcap_vendor(frm, ni); +#ifdef IEEE80211_SUPPORT_SUPERG + if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) { + frm = ieee80211_add_ath(frm, + IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), + ((vap->iv_flags & IEEE80211_F_WPA) == 0 && + ni->ni_authmode != IEEE80211_AUTH_8021X) ? + vap->iv_def_txkey : IEEE80211_KEYIX_NONE); + } +#endif /* IEEE80211_SUPPORT_SUPERG */ + if (vap->iv_appie_assocreq != NULL) + frm = add_appie(frm, vap->iv_appie_assocreq); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + + ieee80211_add_callback(m, ieee80211_tx_mgt_cb, + (void *) vap->iv_state); + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + /* + * asresp frame format + * [2] capability information + * [2] status + * [2] association ID + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] HT capabilities (standard, if STA enabled) + * [tlv] HT information (standard, if STA enabled) + * [tlv] WME (if configured and STA enabled) + * [tlv] HT capabilities (vendor OUI, if STA enabled) + * [tlv] HT information (vendor OUI, if STA enabled) + * [tlv] Atheros capabilities (if STA enabled) + * [tlv] AppIE's (optional) + */ + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t) + + sizeof(uint16_t) + + sizeof(uint16_t) + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + sizeof(struct ieee80211_ie_htcap) + 4 + + sizeof(struct ieee80211_ie_htinfo) + 4 + + sizeof(struct ieee80211_wme_param) +#ifdef IEEE80211_SUPPORT_SUPERG + + sizeof(struct ieee80211_ath_ie) +#endif + + (vap->iv_appie_assocresp != NULL ? + vap->iv_appie_assocresp->ie_len : 0) + ); + if (m == NULL) + senderr(ENOMEM, is_tx_nobuf); + + capinfo = ieee80211_getcapinfo(vap, bss->ni_chan); + *(uint16_t *)frm = htole16(capinfo); + frm += 2; + + *(uint16_t *)frm = htole16(arg); /* status */ + frm += 2; + + if (arg == IEEE80211_STATUS_SUCCESS) { + *(uint16_t *)frm = htole16(ni->ni_associd); + IEEE80211_NODE_STAT(ni, tx_assoc); + } else + IEEE80211_NODE_STAT(ni, tx_assoc_fail); + frm += 2; + + frm = ieee80211_add_rates(frm, &ni->ni_rates); + frm = ieee80211_add_xrates(frm, &ni->ni_rates); + /* NB: respond according to what we received */ + if ((ni->ni_flags & HTFLAGS) == IEEE80211_NODE_HT) { + frm = ieee80211_add_htcap(frm, ni); + frm = ieee80211_add_htinfo(frm, ni); + } + if ((vap->iv_flags & IEEE80211_F_WME) && + ni->ni_ies.wme_ie != NULL) + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + if ((ni->ni_flags & HTFLAGS) == HTFLAGS) { + frm = ieee80211_add_htcap_vendor(frm, ni); + frm = ieee80211_add_htinfo_vendor(frm, ni); + } +#ifdef IEEE80211_SUPPORT_SUPERG + if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) + frm = ieee80211_add_ath(frm, + IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), + ((vap->iv_flags & IEEE80211_F_WPA) == 0 && + ni->ni_authmode != IEEE80211_AUTH_8021X) ? + vap->iv_def_txkey : IEEE80211_KEYIX_NONE); +#endif /* IEEE80211_SUPPORT_SUPERG */ + if (vap->iv_appie_assocresp != NULL) + frm = add_appie(frm, vap->iv_appie_assocresp); + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + break; + + case IEEE80211_FC0_SUBTYPE_DISASSOC: + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, + "send station disassociate (reason %d)", arg); + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + sizeof(uint16_t)); + if (m == NULL) + senderr(ENOMEM, is_tx_nobuf); + *(uint16_t *)frm = htole16(arg); /* reason */ + m->m_pkthdr.len = m->m_len = sizeof(uint16_t); + + IEEE80211_NODE_STAT(ni, tx_disassoc); + IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg); + break; + + default: + IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, + "invalid mgmt frame type %u", type); + senderr(EINVAL, is_tx_unknownmgt); + /* NOTREACHED */ + } + + /* NB: force non-ProbeResp frames to the highest queue */ + params.ibp_pri = WME_AC_VO; + params.ibp_rate0 = bss->ni_txparms->mgmtrate; + /* NB: we know all frames are unicast */ + params.ibp_try0 = bss->ni_txparms->maxretry; + params.ibp_power = bss->ni_txpower; + return ieee80211_mgmt_output(ni, m, type, ¶ms); +bad: + ieee80211_free_node(ni); + return ret; +#undef senderr +#undef HTFLAGS +} + +/* + * Return an mbuf with a probe response frame in it. + * Space is left to prepend and 802.11 header at the + * front but it's left to the caller to fill in. + */ +struct mbuf * +ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) +{ + struct ieee80211vap *vap = bss->ni_vap; + struct ieee80211com *ic = bss->ni_ic; + const struct ieee80211_rateset *rs; + struct mbuf *m; + uint16_t capinfo; + uint8_t *frm; + + /* + * probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] parameter set (FH/DS) + * [tlv] parameter set (IBSS) + * [tlv] country (optional) + * [3] power control (optional) + * [5] channel switch announcement (CSA) (optional) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] RSN (optional) + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] WPA (optional) + * [tlv] WME (optional) + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Vendor OUI HT information (optional) + * [tlv] Atheros capabilities + * [tlv] AppIE's (optional) + * [tlv] Mesh ID (MBSS) + * [tlv] Mesh Conf (MBSS) + */ + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), + 8 + + sizeof(uint16_t) + + sizeof(uint16_t) + + 2 + IEEE80211_NWID_LEN + + 2 + IEEE80211_RATE_SIZE + + 7 /* max(7,3) */ + + IEEE80211_COUNTRY_MAX_SIZE + + 3 + + sizeof(struct ieee80211_csa_ie) + + 3 + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + sizeof(struct ieee80211_ie_wpa) + + sizeof(struct ieee80211_ie_htcap) + + sizeof(struct ieee80211_ie_htinfo) + + sizeof(struct ieee80211_ie_wpa) + + sizeof(struct ieee80211_wme_param) + + 4 + sizeof(struct ieee80211_ie_htcap) + + 4 + sizeof(struct ieee80211_ie_htinfo) +#ifdef IEEE80211_SUPPORT_SUPERG + + sizeof(struct ieee80211_ath_ie) +#endif +#ifdef IEEE80211_SUPPORT_MESH + + 2 + IEEE80211_MESHID_LEN + + sizeof(struct ieee80211_meshconf_ie) +#endif + + (vap->iv_appie_proberesp != NULL ? + vap->iv_appie_proberesp->ie_len : 0) + ); + if (m == NULL) { + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + + memset(frm, 0, 8); /* timestamp should be filled later */ + frm += 8; + *(uint16_t *)frm = htole16(bss->ni_intval); + frm += 2; + capinfo = ieee80211_getcapinfo(vap, bss->ni_chan); + *(uint16_t *)frm = htole16(capinfo); + frm += 2; + + frm = ieee80211_add_ssid(frm, bss->ni_essid, bss->ni_esslen); + rs = ieee80211_get_suprates(ic, bss->ni_chan); + frm = ieee80211_add_rates(frm, rs); + + if (IEEE80211_IS_CHAN_FHSS(bss->ni_chan)) { + *frm++ = IEEE80211_ELEMID_FHPARMS; + *frm++ = 5; + *frm++ = bss->ni_fhdwell & 0x00ff; + *frm++ = (bss->ni_fhdwell >> 8) & 0x00ff; + *frm++ = IEEE80211_FH_CHANSET( + ieee80211_chan2ieee(ic, bss->ni_chan)); + *frm++ = IEEE80211_FH_CHANPAT( + ieee80211_chan2ieee(ic, bss->ni_chan)); + *frm++ = bss->ni_fhindex; + } else { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = ieee80211_chan2ieee(ic, bss->ni_chan); + } + + if (vap->iv_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + } + if ((vap->iv_flags & IEEE80211_F_DOTH) || + (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) + frm = ieee80211_add_countryie(frm, ic); + if (vap->iv_flags & IEEE80211_F_DOTH) { + if (IEEE80211_IS_CHAN_5GHZ(bss->ni_chan)) + frm = ieee80211_add_powerconstraint(frm, vap); + if (ic->ic_flags & IEEE80211_F_CSAPENDING) + frm = ieee80211_add_csa(frm, vap); + } + if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan)) + frm = ieee80211_add_erp(frm, ic); + frm = ieee80211_add_xrates(frm, rs); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain? */ + } + /* + * NB: legacy 11b clients do not get certain ie's. + * The caller identifies such clients by passing + * a token in legacy to us. Could expand this to be + * any legacy client for stuff like HT ie's. + */ + if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && + legacy != IEEE80211_SEND_LEGACY_11B) { + frm = ieee80211_add_htcap(frm, bss); + frm = ieee80211_add_htinfo(frm, bss); + } + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain? */ + } + if (vap->iv_flags & IEEE80211_F_WME) + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && + (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) && + legacy != IEEE80211_SEND_LEGACY_11B) { + frm = ieee80211_add_htcap_vendor(frm, bss); + frm = ieee80211_add_htinfo_vendor(frm, bss); + } +#ifdef IEEE80211_SUPPORT_SUPERG + if ((vap->iv_flags & IEEE80211_F_ATHEROS) && + legacy != IEEE80211_SEND_LEGACY_11B) + frm = ieee80211_add_athcaps(frm, bss); +#endif + if (vap->iv_appie_proberesp != NULL) + frm = add_appie(frm, vap->iv_appie_proberesp); +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode == IEEE80211_M_MBSS) { + frm = ieee80211_add_meshid(frm, vap); + frm = ieee80211_add_meshconf(frm, vap); + } +#endif + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); + + return m; +} + +/* + * Send a probe response frame to the specified mac address. + * This does not go through the normal mgt frame api so we + * can specify the destination address and re-use the bss node + * for the sta reference. + */ +int +ieee80211_send_proberesp(struct ieee80211vap *vap, + const uint8_t da[IEEE80211_ADDR_LEN], int legacy) +{ + struct ieee80211_node *bss = vap->iv_bss; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_frame *wh; + struct mbuf *m; + + if (vap->iv_state == IEEE80211_S_CAC) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss, + "block %s frame in CAC state", "probe response"); + vap->iv_stats.is_tx_badstate++; + return EIO; /* XXX */ + } + + /* + * Hold a reference on the node so it doesn't go away until after + * the xmit is complete all the way in the driver. On error we + * will remove our reference. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", + __func__, __LINE__, bss, ether_sprintf(bss->ni_macaddr), + ieee80211_node_refcnt(bss)+1); + ieee80211_ref_node(bss); + + m = ieee80211_alloc_proberesp(bss, legacy); + if (m == NULL) { + ieee80211_free_node(bss); + return ENOMEM; + } + + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + KASSERT(m != NULL, ("no room for header")); + + wh = mtod(m, struct ieee80211_frame *); + ieee80211_send_setup(bss, m, + IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP, + IEEE80211_NONQOS_TID, vap->iv_myaddr, da, bss->ni_bssid); + /* XXX power management? */ + m->m_flags |= M_ENCAP; /* mark encapsulated */ + + M_WME_SETAC(m, WME_AC_BE); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, + "send probe resp on channel %u to %s%s\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(da), + legacy ? " <legacy>" : ""); + IEEE80211_NODE_STAT(bss, tx_mgmt); + + return ic->ic_raw_xmit(bss, m, NULL); +} + +/* + * Allocate and build a RTS (Request To Send) control frame. + */ +struct mbuf * +ieee80211_alloc_rts(struct ieee80211com *ic, + const uint8_t ra[IEEE80211_ADDR_LEN], + const uint8_t ta[IEEE80211_ADDR_LEN], + uint16_t dur) +{ + struct ieee80211_frame_rts *rts; + struct mbuf *m; + + /* XXX honor ic_headroom */ + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m != NULL) { + rts = mtod(m, struct ieee80211_frame_rts *); + rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | + IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_RTS; + rts->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)rts->i_dur = htole16(dur); + IEEE80211_ADDR_COPY(rts->i_ra, ra); + IEEE80211_ADDR_COPY(rts->i_ta, ta); + + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts); + } + return m; +} + +/* + * Allocate and build a CTS (Clear To Send) control frame. + */ +struct mbuf * +ieee80211_alloc_cts(struct ieee80211com *ic, + const uint8_t ra[IEEE80211_ADDR_LEN], uint16_t dur) +{ + struct ieee80211_frame_cts *cts; + struct mbuf *m; + + /* XXX honor ic_headroom */ + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m != NULL) { + cts = mtod(m, struct ieee80211_frame_cts *); + cts->i_fc[0] = IEEE80211_FC0_VERSION_0 | + IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_CTS; + cts->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)cts->i_dur = htole16(dur); + IEEE80211_ADDR_COPY(cts->i_ra, ra); + + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts); + } + return m; +} + +static void +ieee80211_tx_mgt_timeout(void *arg) +{ + struct ieee80211_node *ni = arg; + struct ieee80211vap *vap = ni->ni_vap; + + if (vap->iv_state != IEEE80211_S_INIT && + (vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0) { + /* + * NB: it's safe to specify a timeout as the reason here; + * it'll only be used in the right state. + */ + ieee80211_new_state(vap, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_TIMEOUT); + } +} + +static void +ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) +{ + struct ieee80211vap *vap = ni->ni_vap; + enum ieee80211_state ostate = (enum ieee80211_state) arg; + + /* + * Frame transmit completed; arrange timer callback. If + * transmit was successfuly we wait for response. Otherwise + * we arrange an immediate callback instead of doing the + * callback directly since we don't know what state the driver + * is in (e.g. what locks it is holding). This work should + * not be too time-critical and not happen too often so the + * added overhead is acceptable. + * + * XXX what happens if !acked but response shows up before callback? + */ + if (vap->iv_state == ostate) + callout_reset(&vap->iv_mgtsend, + status == 0 ? IEEE80211_TRANS_WAIT*hz : 0, + ieee80211_tx_mgt_timeout, ni); +} + +static void +ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, + struct ieee80211_beacon_offsets *bo, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_rateset *rs = &ni->ni_rates; + uint16_t capinfo; + + /* + * beacon frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [3] parameter set (DS) + * [8] CF parameter set (optional) + * [tlv] parameter set (IBSS/TIM) + * [tlv] country (optional) + * [3] power control (optional) + * [5] channel switch announcement (CSA) (optional) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] RSN parameters + * [tlv] HT capabilities + * [tlv] HT information + * XXX Vendor-specific OIDs (e.g. Atheros) + * [tlv] WPA parameters + * [tlv] WME parameters + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Vendor OUI HT information (optional) + * [tlv] Atheros capabilities (optional) + * [tlv] TDMA parameters (optional) + * [tlv] Mesh ID (MBSS) + * [tlv] Mesh Conf (MBSS) + * [tlv] application data (optional) + */ + + memset(bo, 0, sizeof(*bo)); + + memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */ + frm += 8; + *(uint16_t *)frm = htole16(ni->ni_intval); + frm += 2; + capinfo = ieee80211_getcapinfo(vap, ni->ni_chan); + bo->bo_caps = (uint16_t *)frm; + *(uint16_t *)frm = htole16(capinfo); + frm += 2; + *frm++ = IEEE80211_ELEMID_SSID; + if ((vap->iv_flags & IEEE80211_F_HIDESSID) == 0) { + *frm++ = ni->ni_esslen; + memcpy(frm, ni->ni_essid, ni->ni_esslen); + frm += ni->ni_esslen; + } else + *frm++ = 0; + frm = ieee80211_add_rates(frm, rs); + if (!IEEE80211_IS_CHAN_FHSS(ni->ni_chan)) { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + } + if (ic->ic_flags & IEEE80211_F_PCF) { + bo->bo_cfp = frm; + frm = ieee80211_add_cfparms(frm, ic); + } + bo->bo_tim = frm; + if (vap->iv_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + bo->bo_tim_len = 0; + } else if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_MBSS) { + /* TIM IE is the same for Mesh and Hostap */ + struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm; + + tie->tim_ie = IEEE80211_ELEMID_TIM; + tie->tim_len = 4; /* length */ + tie->tim_count = 0; /* DTIM count */ + tie->tim_period = vap->iv_dtim_period; /* DTIM period */ + tie->tim_bitctl = 0; /* bitmap control */ + tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */ + frm += sizeof(struct ieee80211_tim_ie); + bo->bo_tim_len = 1; + } + bo->bo_tim_trailer = frm; + if ((vap->iv_flags & IEEE80211_F_DOTH) || + (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) + frm = ieee80211_add_countryie(frm, ic); + if (vap->iv_flags & IEEE80211_F_DOTH) { + if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) + frm = ieee80211_add_powerconstraint(frm, vap); + bo->bo_csa = frm; + if (ic->ic_flags & IEEE80211_F_CSAPENDING) + frm = ieee80211_add_csa(frm, vap); + } else + bo->bo_csa = frm; + if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) { + bo->bo_erp = frm; + frm = ieee80211_add_erp(frm, ic); + } + frm = ieee80211_add_xrates(frm, rs); + if (vap->iv_flags & IEEE80211_F_WPA2) { + if (vap->iv_rsn_ie != NULL) + frm = add_ie(frm, vap->iv_rsn_ie); + /* XXX else complain */ + } + if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { + frm = ieee80211_add_htcap(frm, ni); + bo->bo_htinfo = frm; + frm = ieee80211_add_htinfo(frm, ni); + } + if (vap->iv_flags & IEEE80211_F_WPA1) { + if (vap->iv_wpa_ie != NULL) + frm = add_ie(frm, vap->iv_wpa_ie); + /* XXX else complain */ + } + if (vap->iv_flags & IEEE80211_F_WME) { + bo->bo_wme = frm; + frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + } + if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && + (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT)) { + frm = ieee80211_add_htcap_vendor(frm, ni); + frm = ieee80211_add_htinfo_vendor(frm, ni); + } +#ifdef IEEE80211_SUPPORT_SUPERG + if (vap->iv_flags & IEEE80211_F_ATHEROS) { + bo->bo_ath = frm; + frm = ieee80211_add_athcaps(frm, ni); + } +#endif +#ifdef IEEE80211_SUPPORT_TDMA + if (vap->iv_caps & IEEE80211_C_TDMA) { + bo->bo_tdma = frm; + frm = ieee80211_add_tdma(frm, vap); + } +#endif + if (vap->iv_appie_beacon != NULL) { + bo->bo_appie = frm; + bo->bo_appie_len = vap->iv_appie_beacon->ie_len; + frm = add_appie(frm, vap->iv_appie_beacon); + } +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode == IEEE80211_M_MBSS) { + frm = ieee80211_add_meshid(frm, vap); + bo->bo_meshconf = frm; + frm = ieee80211_add_meshconf(frm, vap); + } +#endif + bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer; + bo->bo_csa_trailer_len = frm - bo->bo_csa; + m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); +} + +/* + * Allocate a beacon frame and fillin the appropriate bits. + */ +struct mbuf * +ieee80211_beacon_alloc(struct ieee80211_node *ni, + struct ieee80211_beacon_offsets *bo) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_frame *wh; + struct mbuf *m; + int pktlen; + uint8_t *frm; + + /* + * beacon frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [3] parameter set (DS) + * [8] CF parameter set (optional) + * [tlv] parameter set (IBSS/TIM) + * [tlv] country (optional) + * [3] power control (optional) + * [5] channel switch announcement (CSA) (optional) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] RSN parameters + * [tlv] HT capabilities + * [tlv] HT information + * [tlv] Vendor OUI HT capabilities (optional) + * [tlv] Vendor OUI HT information (optional) + * XXX Vendor-specific OIDs (e.g. Atheros) + * [tlv] WPA parameters + * [tlv] WME parameters + * [tlv] TDMA parameters (optional) + * [tlv] Mesh ID (MBSS) + * [tlv] Mesh Conf (MBSS) + * [tlv] application data (optional) + * NB: we allocate the max space required for the TIM bitmap. + * XXX how big is this? + */ + pktlen = 8 /* time stamp */ + + sizeof(uint16_t) /* beacon interval */ + + sizeof(uint16_t) /* capabilities */ + + 2 + ni->ni_esslen /* ssid */ + + 2 + IEEE80211_RATE_SIZE /* supported rates */ + + 2 + 1 /* DS parameters */ + + 2 + 6 /* CF parameters */ + + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */ + + IEEE80211_COUNTRY_MAX_SIZE /* country */ + + 2 + 1 /* power control */ + + sizeof(struct ieee80211_csa_ie) /* CSA */ + + 2 + 1 /* ERP */ + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ + 2*sizeof(struct ieee80211_ie_wpa) : 0) + /* XXX conditional? */ + + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ + + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ + + (vap->iv_caps & IEEE80211_C_WME ? /* WME */ + sizeof(struct ieee80211_wme_param) : 0) +#ifdef IEEE80211_SUPPORT_SUPERG + + sizeof(struct ieee80211_ath_ie) /* ATH */ +#endif +#ifdef IEEE80211_SUPPORT_TDMA + + (vap->iv_caps & IEEE80211_C_TDMA ? /* TDMA */ + sizeof(struct ieee80211_tdma_param) : 0) +#endif +#ifdef IEEE80211_SUPPORT_MESH + + 2 + ni->ni_meshidlen + + sizeof(struct ieee80211_meshconf_ie) +#endif + + IEEE80211_MAX_APPIE + ; + m = ieee80211_getmgtframe(&frm, + ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen); + if (m == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, + "%s: cannot get buf; size %u\n", __func__, pktlen); + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + ieee80211_beacon_construct(m, frm, bo, ni); + + M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + KASSERT(m != NULL, ("no space for 802.11 header?")); + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_BEACON; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(uint16_t *)wh->i_dur = 0; + IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + *(uint16_t *)wh->i_seq = 0; + + return m; +} + +/* + * Update the dynamic parts of a beacon frame based on the current state. + */ +int +ieee80211_beacon_update(struct ieee80211_node *ni, + struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + int len_changed = 0; + uint16_t capinfo; + + IEEE80211_LOCK(ic); + /* + * Handle 11h channel change when we've reached the count. + * We must recalculate the beacon frame contents to account + * for the new channel. Note we do this only for the first + * vap that reaches this point; subsequent vaps just update + * their beacon state to reflect the recalculated channel. + */ + if (isset(bo->bo_flags, IEEE80211_BEACON_CSA) && + vap->iv_csa_count == ic->ic_csa_count) { + vap->iv_csa_count = 0; + /* + * Effect channel change before reconstructing the beacon + * frame contents as many places reference ni_chan. + */ + if (ic->ic_csa_newchan != NULL) + ieee80211_csa_completeswitch(ic); + /* + * NB: ieee80211_beacon_construct clears all pending + * updates in bo_flags so we don't need to explicitly + * clear IEEE80211_BEACON_CSA. + */ + ieee80211_beacon_construct(m, + mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), bo, ni); + + /* XXX do WME aggressive mode processing? */ + IEEE80211_UNLOCK(ic); + return 1; /* just assume length changed */ + } + + /* XXX faster to recalculate entirely or just changes? */ + capinfo = ieee80211_getcapinfo(vap, ni->ni_chan); + *bo->bo_caps = htole16(capinfo); + + if (vap->iv_flags & IEEE80211_F_WME) { + struct ieee80211_wme_state *wme = &ic->ic_wme; + + /* + * Check for agressive mode change. When there is + * significant high priority traffic in the BSS + * throttle back BE traffic by using conservative + * parameters. Otherwise BE uses agressive params + * to optimize performance of legacy/non-QoS traffic. + */ + if (wme->wme_flags & WME_F_AGGRMODE) { + if (wme->wme_hipri_traffic > + wme->wme_hipri_switch_thresh) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, + "%s: traffic %u, disable aggressive mode\n", + __func__, wme->wme_hipri_traffic); + wme->wme_flags &= ~WME_F_AGGRMODE; + ieee80211_wme_updateparams_locked(vap); + wme->wme_hipri_traffic = + wme->wme_hipri_switch_hysteresis; + } else + wme->wme_hipri_traffic = 0; + } else { + if (wme->wme_hipri_traffic <= + wme->wme_hipri_switch_thresh) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, + "%s: traffic %u, enable aggressive mode\n", + __func__, wme->wme_hipri_traffic); + wme->wme_flags |= WME_F_AGGRMODE; + ieee80211_wme_updateparams_locked(vap); + wme->wme_hipri_traffic = 0; + } else + wme->wme_hipri_traffic = + wme->wme_hipri_switch_hysteresis; + } + if (isset(bo->bo_flags, IEEE80211_BEACON_WME)) { + (void) ieee80211_add_wme_param(bo->bo_wme, wme); + clrbit(bo->bo_flags, IEEE80211_BEACON_WME); + } + } + + if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) { + ieee80211_ht_update_beacon(vap, bo); + clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO); + } +#ifdef IEEE80211_SUPPORT_TDMA + if (vap->iv_caps & IEEE80211_C_TDMA) { + /* + * NB: the beacon is potentially updated every TBTT. + */ + ieee80211_tdma_update_beacon(vap, bo); + } +#endif +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode == IEEE80211_M_MBSS) + ieee80211_mesh_update_beacon(vap, bo); +#endif + + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_MBSS) { /* NB: no IBSS support*/ + struct ieee80211_tim_ie *tie = + (struct ieee80211_tim_ie *) bo->bo_tim; + if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) { + u_int timlen, timoff, i; + /* + * ATIM/DTIM needs updating. If it fits in the + * current space allocated then just copy in the + * new bits. Otherwise we need to move any trailing + * data to make room. Note that we know there is + * contiguous space because ieee80211_beacon_allocate + * insures there is space in the mbuf to write a + * maximal-size virtual bitmap (based on iv_max_aid). + */ + /* + * Calculate the bitmap size and offset, copy any + * trailer out of the way, and then copy in the + * new bitmap and update the information element. + * Note that the tim bitmap must contain at least + * one byte and any offset must be even. + */ + if (vap->iv_ps_pending != 0) { + timoff = 128; /* impossibly large */ + for (i = 0; i < vap->iv_tim_len; i++) + if (vap->iv_tim_bitmap[i]) { + timoff = i &~ 1; + break; + } + KASSERT(timoff != 128, ("tim bitmap empty!")); + for (i = vap->iv_tim_len-1; i >= timoff; i--) + if (vap->iv_tim_bitmap[i]) + break; + timlen = 1 + (i - timoff); + } else { + timoff = 0; + timlen = 1; + } + if (timlen != bo->bo_tim_len) { + /* copy up/down trailer */ + int adjust = tie->tim_bitmap+timlen + - bo->bo_tim_trailer; + ovbcopy(bo->bo_tim_trailer, + bo->bo_tim_trailer+adjust, + bo->bo_tim_trailer_len); + bo->bo_tim_trailer += adjust; + bo->bo_erp += adjust; + bo->bo_htinfo += adjust; +#ifdef IEEE80211_SUPERG_SUPPORT + bo->bo_ath += adjust; +#endif +#ifdef IEEE80211_TDMA_SUPPORT + bo->bo_tdma += adjust; +#endif +#ifdef IEEE80211_MESH_SUPPORT + bo->bo_meshconf += adjust; +#endif + bo->bo_appie += adjust; + bo->bo_wme += adjust; + bo->bo_csa += adjust; + bo->bo_tim_len = timlen; + + /* update information element */ + tie->tim_len = 3 + timlen; + tie->tim_bitctl = timoff; + len_changed = 1; + } + memcpy(tie->tim_bitmap, vap->iv_tim_bitmap + timoff, + bo->bo_tim_len); + + clrbit(bo->bo_flags, IEEE80211_BEACON_TIM); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, + "%s: TIM updated, pending %u, off %u, len %u\n", + __func__, vap->iv_ps_pending, timoff, timlen); + } + /* count down DTIM period */ + if (tie->tim_count == 0) + tie->tim_count = tie->tim_period - 1; + else + tie->tim_count--; + /* update state for buffered multicast frames on DTIM */ + if (mcast && tie->tim_count == 0) + tie->tim_bitctl |= 1; + else + tie->tim_bitctl &= ~1; + if (isset(bo->bo_flags, IEEE80211_BEACON_CSA)) { + struct ieee80211_csa_ie *csa = + (struct ieee80211_csa_ie *) bo->bo_csa; + + /* + * Insert or update CSA ie. If we're just starting + * to count down to the channel switch then we need + * to insert the CSA ie. Otherwise we just need to + * drop the count. The actual change happens above + * when the vap's count reaches the target count. + */ + if (vap->iv_csa_count == 0) { + memmove(&csa[1], csa, bo->bo_csa_trailer_len); + bo->bo_erp += sizeof(*csa); + bo->bo_htinfo += sizeof(*csa); + bo->bo_wme += sizeof(*csa); +#ifdef IEEE80211_SUPERG_SUPPORT + bo->bo_ath += sizeof(*csa); +#endif +#ifdef IEEE80211_TDMA_SUPPORT + bo->bo_tdma += sizeof(*csa); +#endif +#ifdef IEEE80211_MESH_SUPPORT + bo->bo_meshconf += sizeof(*csa); +#endif + bo->bo_appie += sizeof(*csa); + bo->bo_csa_trailer_len += sizeof(*csa); + bo->bo_tim_trailer_len += sizeof(*csa); + m->m_len += sizeof(*csa); + m->m_pkthdr.len += sizeof(*csa); + + ieee80211_add_csa(bo->bo_csa, vap); + } else + csa->csa_count--; + vap->iv_csa_count++; + /* NB: don't clear IEEE80211_BEACON_CSA */ + } + if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) { + /* + * ERP element needs updating. + */ + (void) ieee80211_add_erp(bo->bo_erp, ic); + clrbit(bo->bo_flags, IEEE80211_BEACON_ERP); + } +#ifdef IEEE80211_SUPPORT_SUPERG + if (isset(bo->bo_flags, IEEE80211_BEACON_ATH)) { + ieee80211_add_athcaps(bo->bo_ath, ni); + clrbit(bo->bo_flags, IEEE80211_BEACON_ATH); + } +#endif + } + if (isset(bo->bo_flags, IEEE80211_BEACON_APPIE)) { + const struct ieee80211_appie *aie = vap->iv_appie_beacon; + int aielen; + uint8_t *frm; + + aielen = 0; + if (aie != NULL) + aielen += aie->ie_len; + if (aielen != bo->bo_appie_len) { + /* copy up/down trailer */ + int adjust = aielen - bo->bo_appie_len; + ovbcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust, + bo->bo_tim_trailer_len); + bo->bo_tim_trailer += adjust; + bo->bo_appie += adjust; + bo->bo_appie_len = aielen; + + len_changed = 1; + } + frm = bo->bo_appie; + if (aie != NULL) + frm = add_appie(frm, aie); + clrbit(bo->bo_flags, IEEE80211_BEACON_APPIE); + } + IEEE80211_UNLOCK(ic); + + return len_changed; +} diff --git a/freebsd/sys/net80211/ieee80211_phy.c b/freebsd/sys/net80211/ieee80211_phy.c new file mode 100644 index 00000000..5c5a2f55 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_phy.c @@ -0,0 +1,467 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 PHY-related support. + */ + +#include <freebsd/local/opt_inet.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/systm.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/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 */ +static 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; +} diff --git a/freebsd/sys/net80211/ieee80211_phy.h b/freebsd/sys/net80211/ieee80211_phy.h new file mode 100644 index 00000000..af76e666 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_phy.h @@ -0,0 +1,155 @@ +/*- + * 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 _NET80211_IEEE80211_PHY_HH_ +#define _NET80211_IEEE80211_PHY_HH_ + +#ifdef _KERNEL +/* + * IEEE 802.11 PHY-related definitions. + */ + +/* + * 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 /* !_NET80211_IEEE80211_PHY_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_power.c b/freebsd/sys/net80211/ieee80211_power.c new file mode 100644 index 00000000..f56a641f --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_power.c @@ -0,0 +1,529 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 power save support. + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net80211/ieee80211_var.h> + +#include <freebsd/net/bpf.h> + +static void ieee80211_update_ps(struct ieee80211vap *, int); +static int ieee80211_set_tim(struct ieee80211_node *, int); + +MALLOC_DEFINE(M_80211_POWER, "80211power", "802.11 power save state"); + +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)); + } +} diff --git a/freebsd/sys/net80211/ieee80211_power.h b/freebsd/sys/net80211/ieee80211_power.h new file mode 100644 index 00000000..6cb0eab8 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_power.h @@ -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 _NET80211_IEEE80211_POWER_HH_ +#define _NET80211_IEEE80211_POWER_HH_ + +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 /* _NET80211_IEEE80211_POWER_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_proto.c b/freebsd/sys/net80211/ieee80211_proto.c new file mode 100644 index 00000000..70c5f299 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_proto.c @@ -0,0 +1,1888 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 protocol support. + */ + +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/systm.h> + +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> /* XXX for ether_sprintf */ + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_adhoc.h> +#include <freebsd/net80211/ieee80211_sta.h> +#include <freebsd/net80211/ieee80211_hostap.h> +#include <freebsd/net80211/ieee80211_wds.h> +#ifdef IEEE80211_SUPPORT_MESH +#include <freebsd/net80211/ieee80211_mesh.h> +#endif +#include <freebsd/net80211/ieee80211_monitor.h> +#include <freebsd/net80211/ieee80211_input.h> + +/* XXX tunables */ +#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ +#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ + +const char *ieee80211_mgt_subtype_name[] = { + "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", + "probe_req", "probe_resp", "reserved#6", "reserved#7", + "beacon", "atim", "disassoc", "auth", + "deauth", "action", "reserved#14", "reserved#15" +}; +const char *ieee80211_ctl_subtype_name[] = { + "reserved#0", "reserved#1", "reserved#2", "reserved#3", + "reserved#3", "reserved#5", "reserved#6", "reserved#7", + "reserved#8", "reserved#9", "ps_poll", "rts", + "cts", "ack", "cf_end", "cf_end_ack" +}; +const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { + "IBSS", /* IEEE80211_M_IBSS */ + "STA", /* IEEE80211_M_STA */ + "WDS", /* IEEE80211_M_WDS */ + "AHDEMO", /* IEEE80211_M_AHDEMO */ + "HOSTAP", /* IEEE80211_M_HOSTAP */ + "MONITOR", /* IEEE80211_M_MONITOR */ + "MBSS" /* IEEE80211_M_MBSS */ +}; +const char *ieee80211_state_name[IEEE80211_S_MAX] = { + "INIT", /* IEEE80211_S_INIT */ + "SCAN", /* IEEE80211_S_SCAN */ + "AUTH", /* IEEE80211_S_AUTH */ + "ASSOC", /* IEEE80211_S_ASSOC */ + "CAC", /* IEEE80211_S_CAC */ + "RUN", /* IEEE80211_S_RUN */ + "CSA", /* IEEE80211_S_CSA */ + "SLEEP", /* IEEE80211_S_SLEEP */ +}; +const char *ieee80211_wme_acnames[] = { + "WME_AC_BE", + "WME_AC_BK", + "WME_AC_VI", + "WME_AC_VO", + "WME_UPSD", +}; + +static void beacon_miss(void *, int); +static void beacon_swmiss(void *, int); +static void parent_updown(void *, int); +static void update_mcast(void *, int); +static void update_promisc(void *, int); +static void update_channel(void *, int); +static void ieee80211_newstate_cb(void *, int); +static int ieee80211_new_state_locked(struct ieee80211vap *, + enum ieee80211_state, int); + +static int +null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ifnet *ifp = ni->ni_ic->ic_ifp; + + if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n"); + m_freem(m); + return ENETDOWN; +} + +void +ieee80211_proto_attach(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + + /* override the 802.3 setting */ + ifp->if_hdrlen = ic->ic_headroom + + sizeof(struct ieee80211_qosframe_addr4) + + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN; + /* XXX no way to recalculate on ifdetach */ + if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { + /* XXX sanity check... */ + max_linkhdr = ALIGN(ifp->if_hdrlen); + max_hdr = max_linkhdr + max_protohdr; + max_datalen = MHLEN - max_hdr; + } + ic->ic_protmode = IEEE80211_PROT_CTSONLY; + + TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp); + TASK_INIT(&ic->ic_mcast_task, 0, update_mcast, ic); + TASK_INIT(&ic->ic_promisc_task, 0, update_promisc, ic); + TASK_INIT(&ic->ic_chan_task, 0, update_channel, ic); + TASK_INIT(&ic->ic_bmiss_task, 0, beacon_miss, ic); + + ic->ic_wme.wme_hipri_switch_hysteresis = + AGGRESSIVE_MODE_SWITCH_HYSTERESIS; + + /* initialize management frame handlers */ + ic->ic_send_mgmt = ieee80211_send_mgmt; + ic->ic_raw_xmit = null_raw_xmit; + + ieee80211_adhoc_attach(ic); + ieee80211_sta_attach(ic); + ieee80211_wds_attach(ic); + ieee80211_hostap_attach(ic); +#ifdef IEEE80211_SUPPORT_MESH + ieee80211_mesh_attach(ic); +#endif + ieee80211_monitor_attach(ic); +} + +void +ieee80211_proto_detach(struct ieee80211com *ic) +{ + ieee80211_monitor_detach(ic); +#ifdef IEEE80211_SUPPORT_MESH + ieee80211_mesh_detach(ic); +#endif + ieee80211_hostap_detach(ic); + ieee80211_wds_detach(ic); + ieee80211_adhoc_detach(ic); + ieee80211_sta_detach(ic); +} + +static void +null_update_beacon(struct ieee80211vap *vap, int item) +{ +} + +void +ieee80211_proto_vattach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = vap->iv_ifp; + int i; + + /* override the 802.3 setting */ + ifp->if_hdrlen = ic->ic_ifp->if_hdrlen; + + vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT; + vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT; + vap->iv_bmiss_max = IEEE80211_BMISS_MAX; + callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE); + callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE); + TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap); + TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap); + /* + * Install default tx rate handling: no fixed rate, lowest + * supported rate for mgmt and multicast frames. Default + * max retry count. These settings can be changed by the + * driver and/or user applications. + */ + for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) { + const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i]; + + vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; + if (i == IEEE80211_MODE_11NA || i == IEEE80211_MODE_11NG) { + vap->iv_txparms[i].mgmtrate = 0 | IEEE80211_RATE_MCS; + vap->iv_txparms[i].mcastrate = 0 | IEEE80211_RATE_MCS; + } else { + vap->iv_txparms[i].mgmtrate = + rs->rs_rates[0] & IEEE80211_RATE_VAL; + vap->iv_txparms[i].mcastrate = + rs->rs_rates[0] & IEEE80211_RATE_VAL; + } + vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT; + } + vap->iv_roaming = IEEE80211_ROAMING_AUTO; + + vap->iv_update_beacon = null_update_beacon; + vap->iv_deliver_data = ieee80211_deliver_data; + + /* attach support for operating mode */ + ic->ic_vattach[vap->iv_opmode](vap); +} + +void +ieee80211_proto_vdetach(struct ieee80211vap *vap) +{ +#define FREEAPPIE(ie) do { \ + if (ie != NULL) \ + free(ie, M_80211_NODE_IE); \ +} while (0) + /* + * Detach operating mode module. + */ + if (vap->iv_opdetach != NULL) + vap->iv_opdetach(vap); + /* + * This should not be needed as we detach when reseting + * the state but be conservative here since the + * authenticator may do things like spawn kernel threads. + */ + if (vap->iv_auth->ia_detach != NULL) + vap->iv_auth->ia_detach(vap); + /* + * Detach any ACL'ator. + */ + if (vap->iv_acl != NULL) + vap->iv_acl->iac_detach(vap); + + FREEAPPIE(vap->iv_appie_beacon); + FREEAPPIE(vap->iv_appie_probereq); + FREEAPPIE(vap->iv_appie_proberesp); + FREEAPPIE(vap->iv_appie_assocreq); + FREEAPPIE(vap->iv_appie_assocresp); + FREEAPPIE(vap->iv_appie_wpa); +#undef FREEAPPIE +} + +/* + * Simple-minded authenticator module support. + */ + +#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) +/* XXX well-known names */ +static const char *auth_modnames[IEEE80211_AUTH_MAX] = { + "wlan_internal", /* IEEE80211_AUTH_NONE */ + "wlan_internal", /* IEEE80211_AUTH_OPEN */ + "wlan_internal", /* IEEE80211_AUTH_SHARED */ + "wlan_xauth", /* IEEE80211_AUTH_8021X */ + "wlan_internal", /* IEEE80211_AUTH_AUTO */ + "wlan_xauth", /* IEEE80211_AUTH_WPA */ +}; +static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; + +static const struct ieee80211_authenticator auth_internal = { + .ia_name = "wlan_internal", + .ia_attach = NULL, + .ia_detach = NULL, + .ia_node_join = NULL, + .ia_node_leave = NULL, +}; + +/* + * Setup internal authenticators once; they are never unregistered. + */ +static void +ieee80211_auth_setup(void) +{ + ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); + ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); + ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); +} +SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); + +const struct ieee80211_authenticator * +ieee80211_authenticator_get(int auth) +{ + if (auth >= IEEE80211_AUTH_MAX) + return NULL; + if (authenticators[auth] == NULL) + ieee80211_load_module(auth_modnames[auth]); + return authenticators[auth]; +} + +void +ieee80211_authenticator_register(int type, + const struct ieee80211_authenticator *auth) +{ + if (type >= IEEE80211_AUTH_MAX) + return; + authenticators[type] = auth; +} + +void +ieee80211_authenticator_unregister(int type) +{ + + if (type >= IEEE80211_AUTH_MAX) + return; + authenticators[type] = NULL; +} + +/* + * Very simple-minded ACL module support. + */ +/* XXX just one for now */ +static const struct ieee80211_aclator *acl = NULL; + +void +ieee80211_aclator_register(const struct ieee80211_aclator *iac) +{ + printf("wlan: %s acl policy registered\n", iac->iac_name); + acl = iac; +} + +void +ieee80211_aclator_unregister(const struct ieee80211_aclator *iac) +{ + if (acl == iac) + acl = NULL; + printf("wlan: %s acl policy unregistered\n", iac->iac_name); +} + +const struct ieee80211_aclator * +ieee80211_aclator_get(const char *name) +{ + if (acl == NULL) + ieee80211_load_module("wlan_acl"); + return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; +} + +void +ieee80211_print_essid(const uint8_t *essid, int len) +{ + const uint8_t *p; + int i; + + if (len > IEEE80211_NWID_LEN) + len = IEEE80211_NWID_LEN; + /* determine printable or not */ + for (i = 0, p = essid; i < len; i++, p++) { + if (*p < ' ' || *p > 0x7e) + break; + } + if (i == len) { + printf("\""); + for (i = 0, p = essid; i < len; i++, p++) + printf("%c", *p); + printf("\""); + } else { + printf("0x"); + for (i = 0, p = essid; i < len; i++, p++) + printf("%02x", *p); + } +} + +void +ieee80211_dump_pkt(struct ieee80211com *ic, + const uint8_t *buf, int len, int rate, int rssi) +{ + const struct ieee80211_frame *wh; + int i; + + wh = (const struct ieee80211_frame *)buf; + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + printf("NODS %s", ether_sprintf(wh->i_addr2)); + printf("->%s", ether_sprintf(wh->i_addr1)); + printf("(%s)", ether_sprintf(wh->i_addr3)); + break; + case IEEE80211_FC1_DIR_TODS: + printf("TODS %s", ether_sprintf(wh->i_addr2)); + printf("->%s", ether_sprintf(wh->i_addr3)); + printf("(%s)", ether_sprintf(wh->i_addr1)); + break; + case IEEE80211_FC1_DIR_FROMDS: + printf("FRDS %s", ether_sprintf(wh->i_addr3)); + printf("->%s", ether_sprintf(wh->i_addr1)); + printf("(%s)", ether_sprintf(wh->i_addr2)); + break; + case IEEE80211_FC1_DIR_DSTODS: + printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1])); + printf("->%s", ether_sprintf(wh->i_addr3)); + printf("(%s", ether_sprintf(wh->i_addr2)); + printf("->%s)", ether_sprintf(wh->i_addr1)); + break; + } + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_DATA: + printf(" data"); + break; + case IEEE80211_FC0_TYPE_MGT: + printf(" %s", ieee80211_mgt_subtype_name[ + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) + >> IEEE80211_FC0_SUBTYPE_SHIFT]); + break; + default: + printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); + break; + } + if (IEEE80211_QOS_HAS_SEQ(wh)) { + const struct ieee80211_qosframe *qwh = + (const struct ieee80211_qosframe *)buf; + printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, + qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); + } + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + int off; + + off = ieee80211_anyhdrspace(ic, wh); + printf(" WEP [IV %.02x %.02x %.02x", + buf[off+0], buf[off+1], buf[off+2]); + if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) + printf(" %.02x %.02x %.02x", + buf[off+4], buf[off+5], buf[off+6]); + printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); + } + if (rate >= 0) + printf(" %dM", rate / 2); + if (rssi >= 0) + printf(" +%d", rssi); + printf("\n"); + if (len > 0) { + for (i = 0; i < len; i++) { + if ((i & 1) == 0) + printf(" "); + printf("%02x", buf[i]); + } + printf("\n"); + } +} + +static __inline int +findrix(const struct ieee80211_rateset *rs, int r) +{ + int i; + + for (i = 0; i < rs->rs_nrates; i++) + if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) + return i; + return -1; +} + +int +ieee80211_fix_rate(struct ieee80211_node *ni, + struct ieee80211_rateset *nrs, int flags) +{ +#define RV(v) ((v) & IEEE80211_RATE_VAL) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + int i, j, rix, error; + int okrate, badrate, fixedrate, ucastrate; + const struct ieee80211_rateset *srs; + uint8_t r; + + error = 0; + okrate = badrate = 0; + ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate; + if (ucastrate != IEEE80211_FIXED_RATE_NONE) { + /* + * Workaround awkwardness with fixed rate. We are called + * to check both the legacy rate set and the HT rate set + * but we must apply any legacy fixed rate check only to the + * legacy rate set and vice versa. We cannot tell what type + * of rate set we've been given (legacy or HT) but we can + * distinguish the fixed rate type (MCS have 0x80 set). + * So to deal with this the caller communicates whether to + * check MCS or legacy rate using the flags and we use the + * type of any fixed rate to avoid applying an MCS to a + * legacy rate and vice versa. + */ + if (ucastrate & 0x80) { + if (flags & IEEE80211_F_DOFRATE) + flags &= ~IEEE80211_F_DOFRATE; + } else if ((ucastrate & 0x80) == 0) { + if (flags & IEEE80211_F_DOFMCS) + flags &= ~IEEE80211_F_DOFMCS; + } + /* NB: required to make MCS match below work */ + ucastrate &= IEEE80211_RATE_VAL; + } + fixedrate = IEEE80211_FIXED_RATE_NONE; + /* + * XXX we are called to process both MCS and legacy rates; + * we must use the appropriate basic rate set or chaos will + * ensue; for now callers that want MCS must supply + * IEEE80211_F_DOBRS; at some point we'll need to split this + * function so there are two variants, one for MCS and one + * for legacy rates. + */ + if (flags & IEEE80211_F_DOBRS) + srs = (const struct ieee80211_rateset *) + ieee80211_get_suphtrates(ic, ni->ni_chan); + else + srs = ieee80211_get_suprates(ic, ni->ni_chan); + for (i = 0; i < nrs->rs_nrates; ) { + if (flags & IEEE80211_F_DOSORT) { + /* + * Sort rates. + */ + for (j = i + 1; j < nrs->rs_nrates; j++) { + if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { + r = nrs->rs_rates[i]; + nrs->rs_rates[i] = nrs->rs_rates[j]; + nrs->rs_rates[j] = r; + } + } + } + r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; + badrate = r; + /* + * Check for fixed rate. + */ + if (r == ucastrate) + fixedrate = r; + /* + * Check against supported rates. + */ + rix = findrix(srs, r); + if (flags & IEEE80211_F_DONEGO) { + if (rix < 0) { + /* + * A rate in the node's rate set is not + * supported. If this is a basic rate and we + * are operating as a STA then this is an error. + * Otherwise we just discard/ignore the rate. + */ + if ((flags & IEEE80211_F_JOIN) && + (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) + error++; + } else if ((flags & IEEE80211_F_JOIN) == 0) { + /* + * Overwrite with the supported rate + * value so any basic rate bit is set. + */ + nrs->rs_rates[i] = srs->rs_rates[rix]; + } + } + if ((flags & IEEE80211_F_DODEL) && rix < 0) { + /* + * Delete unacceptable rates. + */ + nrs->rs_nrates--; + for (j = i; j < nrs->rs_nrates; j++) + nrs->rs_rates[j] = nrs->rs_rates[j + 1]; + nrs->rs_rates[j] = 0; + continue; + } + if (rix >= 0) + okrate = nrs->rs_rates[i]; + i++; + } + if (okrate == 0 || error != 0 || + ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) && + fixedrate != ucastrate)) { + IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, + "%s: flags 0x%x okrate %d error %d fixedrate 0x%x " + "ucastrate %x\n", __func__, fixedrate, ucastrate, flags); + return badrate | IEEE80211_RATE_BASIC; + } else + return RV(okrate); +#undef RV +} + +/* + * Reset 11g-related state. + */ +void +ieee80211_reset_erp(struct ieee80211com *ic) +{ + ic->ic_flags &= ~IEEE80211_F_USEPROT; + ic->ic_nonerpsta = 0; + ic->ic_longslotsta = 0; + /* + * Short slot time is enabled only when operating in 11g + * and not in an IBSS. We must also honor whether or not + * the driver is capable of doing it. + */ + ieee80211_set_shortslottime(ic, + IEEE80211_IS_CHAN_A(ic->ic_curchan) || + IEEE80211_IS_CHAN_HT(ic->ic_curchan) || + (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && + ic->ic_opmode == IEEE80211_M_HOSTAP && + (ic->ic_caps & IEEE80211_C_SHSLOT))); + /* + * Set short preamble and ERP barker-preamble flags. + */ + if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || + (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } else { + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + ic->ic_flags |= IEEE80211_F_USEBARKER; + } +} + +/* + * Set the short slot time state and notify the driver. + */ +void +ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) +{ + if (onoff) + ic->ic_flags |= IEEE80211_F_SHSLOT; + else + ic->ic_flags &= ~IEEE80211_F_SHSLOT; + /* notify driver */ + if (ic->ic_updateslot != NULL) + ic->ic_updateslot(ic->ic_ifp); +} + +/* + * Check if the specified rate set supports ERP. + * NB: the rate set is assumed to be sorted. + */ +int +ieee80211_iserp_rateset(const struct ieee80211_rateset *rs) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; + int i, j; + + if (rs->rs_nrates < N(rates)) + return 0; + for (i = 0; i < N(rates); i++) { + for (j = 0; j < rs->rs_nrates; j++) { + int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; + if (rates[i] == r) + goto next; + if (r > rates[i]) + return 0; + } + return 0; + next: + ; + } + return 1; +#undef N +} + +/* + * Mark the basic rates for the rate table based on the + * operating mode. For real 11g we mark all the 11b rates + * and 6, 12, and 24 OFDM. For 11b compatibility we mark only + * 11b rates. There's also a pseudo 11a-mode used to mark only + * the basic OFDM rates. + */ +static void +setbasicrates(struct ieee80211_rateset *rs, + enum ieee80211_phymode mode, int add) +{ + static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_11A] = { 3, { 12, 24, 48 } }, + [IEEE80211_MODE_11B] = { 2, { 2, 4 } }, + /* NB: mixed b/g */ + [IEEE80211_MODE_11G] = { 4, { 2, 4, 11, 22 } }, + [IEEE80211_MODE_TURBO_A] = { 3, { 12, 24, 48 } }, + [IEEE80211_MODE_TURBO_G] = { 4, { 2, 4, 11, 22 } }, + [IEEE80211_MODE_STURBO_A] = { 3, { 12, 24, 48 } }, + [IEEE80211_MODE_HALF] = { 3, { 6, 12, 24 } }, + [IEEE80211_MODE_QUARTER] = { 3, { 3, 6, 12 } }, + [IEEE80211_MODE_11NA] = { 3, { 12, 24, 48 } }, + /* NB: mixed b/g */ + [IEEE80211_MODE_11NG] = { 4, { 2, 4, 11, 22 } }, + }; + int i, j; + + for (i = 0; i < rs->rs_nrates; i++) { + if (!add) + rs->rs_rates[i] &= IEEE80211_RATE_VAL; + for (j = 0; j < basic[mode].rs_nrates; j++) + if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { + rs->rs_rates[i] |= IEEE80211_RATE_BASIC; + break; + } + } +} + +/* + * Set the basic rates in a rate set. + */ +void +ieee80211_setbasicrates(struct ieee80211_rateset *rs, + enum ieee80211_phymode mode) +{ + setbasicrates(rs, mode, 0); +} + +/* + * Add basic rates to a rate set. + */ +void +ieee80211_addbasicrates(struct ieee80211_rateset *rs, + enum ieee80211_phymode mode) +{ + setbasicrates(rs, mode, 1); +} + +/* + * WME protocol support. + * + * The default 11a/b/g/n parameters come from the WiFi Alliance WMM + * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n + * Draft 2.0 Test Plan (Appendix D). + * + * Static/Dynamic Turbo mode settings come from Atheros. + */ +typedef struct phyParamType { + uint8_t aifsn; + uint8_t logcwmin; + uint8_t logcwmax; + uint16_t txopLimit; + uint8_t acm; +} paramType; + +static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = { 3, 4, 6, 0, 0 }, + [IEEE80211_MODE_11A] = { 3, 4, 6, 0, 0 }, + [IEEE80211_MODE_11B] = { 3, 4, 6, 0, 0 }, + [IEEE80211_MODE_11G] = { 3, 4, 6, 0, 0 }, + [IEEE80211_MODE_FH] = { 3, 4, 6, 0, 0 }, + [IEEE80211_MODE_TURBO_A]= { 2, 3, 5, 0, 0 }, + [IEEE80211_MODE_TURBO_G]= { 2, 3, 5, 0, 0 }, + [IEEE80211_MODE_STURBO_A]={ 2, 3, 5, 0, 0 }, + [IEEE80211_MODE_HALF] = { 3, 4, 6, 0, 0 }, + [IEEE80211_MODE_QUARTER]= { 3, 4, 6, 0, 0 }, + [IEEE80211_MODE_11NA] = { 3, 4, 6, 0, 0 }, + [IEEE80211_MODE_11NG] = { 3, 4, 6, 0, 0 }, +}; +static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = { 7, 4, 10, 0, 0 }, + [IEEE80211_MODE_11A] = { 7, 4, 10, 0, 0 }, + [IEEE80211_MODE_11B] = { 7, 4, 10, 0, 0 }, + [IEEE80211_MODE_11G] = { 7, 4, 10, 0, 0 }, + [IEEE80211_MODE_FH] = { 7, 4, 10, 0, 0 }, + [IEEE80211_MODE_TURBO_A]= { 7, 3, 10, 0, 0 }, + [IEEE80211_MODE_TURBO_G]= { 7, 3, 10, 0, 0 }, + [IEEE80211_MODE_STURBO_A]={ 7, 3, 10, 0, 0 }, + [IEEE80211_MODE_HALF] = { 7, 4, 10, 0, 0 }, + [IEEE80211_MODE_QUARTER]= { 7, 4, 10, 0, 0 }, + [IEEE80211_MODE_11NA] = { 7, 4, 10, 0, 0 }, + [IEEE80211_MODE_11NG] = { 7, 4, 10, 0, 0 }, +}; +static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = { 1, 3, 4, 94, 0 }, + [IEEE80211_MODE_11A] = { 1, 3, 4, 94, 0 }, + [IEEE80211_MODE_11B] = { 1, 3, 4, 188, 0 }, + [IEEE80211_MODE_11G] = { 1, 3, 4, 94, 0 }, + [IEEE80211_MODE_FH] = { 1, 3, 4, 188, 0 }, + [IEEE80211_MODE_TURBO_A]= { 1, 2, 3, 94, 0 }, + [IEEE80211_MODE_TURBO_G]= { 1, 2, 3, 94, 0 }, + [IEEE80211_MODE_STURBO_A]={ 1, 2, 3, 94, 0 }, + [IEEE80211_MODE_HALF] = { 1, 3, 4, 94, 0 }, + [IEEE80211_MODE_QUARTER]= { 1, 3, 4, 94, 0 }, + [IEEE80211_MODE_11NA] = { 1, 3, 4, 94, 0 }, + [IEEE80211_MODE_11NG] = { 1, 3, 4, 94, 0 }, +}; +static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = { 1, 2, 3, 47, 0 }, + [IEEE80211_MODE_11A] = { 1, 2, 3, 47, 0 }, + [IEEE80211_MODE_11B] = { 1, 2, 3, 102, 0 }, + [IEEE80211_MODE_11G] = { 1, 2, 3, 47, 0 }, + [IEEE80211_MODE_FH] = { 1, 2, 3, 102, 0 }, + [IEEE80211_MODE_TURBO_A]= { 1, 2, 2, 47, 0 }, + [IEEE80211_MODE_TURBO_G]= { 1, 2, 2, 47, 0 }, + [IEEE80211_MODE_STURBO_A]={ 1, 2, 2, 47, 0 }, + [IEEE80211_MODE_HALF] = { 1, 2, 3, 47, 0 }, + [IEEE80211_MODE_QUARTER]= { 1, 2, 3, 47, 0 }, + [IEEE80211_MODE_11NA] = { 1, 2, 3, 47, 0 }, + [IEEE80211_MODE_11NG] = { 1, 2, 3, 47, 0 }, +}; + +static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = { 3, 4, 10, 0, 0 }, + [IEEE80211_MODE_11A] = { 3, 4, 10, 0, 0 }, + [IEEE80211_MODE_11B] = { 3, 4, 10, 0, 0 }, + [IEEE80211_MODE_11G] = { 3, 4, 10, 0, 0 }, + [IEEE80211_MODE_FH] = { 3, 4, 10, 0, 0 }, + [IEEE80211_MODE_TURBO_A]= { 2, 3, 10, 0, 0 }, + [IEEE80211_MODE_TURBO_G]= { 2, 3, 10, 0, 0 }, + [IEEE80211_MODE_STURBO_A]={ 2, 3, 10, 0, 0 }, + [IEEE80211_MODE_HALF] = { 3, 4, 10, 0, 0 }, + [IEEE80211_MODE_QUARTER]= { 3, 4, 10, 0, 0 }, + [IEEE80211_MODE_11NA] = { 3, 4, 10, 0, 0 }, + [IEEE80211_MODE_11NG] = { 3, 4, 10, 0, 0 }, +}; +static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = { 2, 3, 4, 94, 0 }, + [IEEE80211_MODE_11A] = { 2, 3, 4, 94, 0 }, + [IEEE80211_MODE_11B] = { 2, 3, 4, 188, 0 }, + [IEEE80211_MODE_11G] = { 2, 3, 4, 94, 0 }, + [IEEE80211_MODE_FH] = { 2, 3, 4, 188, 0 }, + [IEEE80211_MODE_TURBO_A]= { 2, 2, 3, 94, 0 }, + [IEEE80211_MODE_TURBO_G]= { 2, 2, 3, 94, 0 }, + [IEEE80211_MODE_STURBO_A]={ 2, 2, 3, 94, 0 }, + [IEEE80211_MODE_HALF] = { 2, 3, 4, 94, 0 }, + [IEEE80211_MODE_QUARTER]= { 2, 3, 4, 94, 0 }, + [IEEE80211_MODE_11NA] = { 2, 3, 4, 94, 0 }, + [IEEE80211_MODE_11NG] = { 2, 3, 4, 94, 0 }, +}; +static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = { 2, 2, 3, 47, 0 }, + [IEEE80211_MODE_11A] = { 2, 2, 3, 47, 0 }, + [IEEE80211_MODE_11B] = { 2, 2, 3, 102, 0 }, + [IEEE80211_MODE_11G] = { 2, 2, 3, 47, 0 }, + [IEEE80211_MODE_FH] = { 2, 2, 3, 102, 0 }, + [IEEE80211_MODE_TURBO_A]= { 1, 2, 2, 47, 0 }, + [IEEE80211_MODE_TURBO_G]= { 1, 2, 2, 47, 0 }, + [IEEE80211_MODE_STURBO_A]={ 1, 2, 2, 47, 0 }, + [IEEE80211_MODE_HALF] = { 2, 2, 3, 47, 0 }, + [IEEE80211_MODE_QUARTER]= { 2, 2, 3, 47, 0 }, + [IEEE80211_MODE_11NA] = { 2, 2, 3, 47, 0 }, + [IEEE80211_MODE_11NG] = { 2, 2, 3, 47, 0 }, +}; + +static void +_setifsparams(struct wmeParams *wmep, const paramType *phy) +{ + wmep->wmep_aifsn = phy->aifsn; + wmep->wmep_logcwmin = phy->logcwmin; + wmep->wmep_logcwmax = phy->logcwmax; + wmep->wmep_txopLimit = phy->txopLimit; +} + +static void +setwmeparams(struct ieee80211vap *vap, const char *type, int ac, + struct wmeParams *wmep, const paramType *phy) +{ + wmep->wmep_acm = phy->acm; + _setifsparams(wmep, phy); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, + "set %s (%s) [acm %u aifsn %u logcwmin %u logcwmax %u txop %u]\n", + ieee80211_wme_acnames[ac], type, + wmep->wmep_acm, wmep->wmep_aifsn, wmep->wmep_logcwmin, + wmep->wmep_logcwmax, wmep->wmep_txopLimit); +} + +static void +ieee80211_wme_initparams_locked(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_wme_state *wme = &ic->ic_wme; + const paramType *pPhyParam, *pBssPhyParam; + struct wmeParams *wmep; + enum ieee80211_phymode mode; + int i; + + IEEE80211_LOCK_ASSERT(ic); + + if ((ic->ic_caps & IEEE80211_C_WME) == 0 || ic->ic_nrunning > 1) + return; + + /* + * Select mode; we can be called early in which case we + * always use auto mode. We know we'll be called when + * entering the RUN state with bsschan setup properly + * so state will eventually get set correctly + */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) + mode = ieee80211_chan2mode(ic->ic_bsschan); + else + mode = IEEE80211_MODE_AUTO; + for (i = 0; i < WME_NUM_AC; i++) { + switch (i) { + case WME_AC_BK: + pPhyParam = &phyParamForAC_BK[mode]; + pBssPhyParam = &phyParamForAC_BK[mode]; + break; + case WME_AC_VI: + pPhyParam = &phyParamForAC_VI[mode]; + pBssPhyParam = &bssPhyParamForAC_VI[mode]; + break; + case WME_AC_VO: + pPhyParam = &phyParamForAC_VO[mode]; + pBssPhyParam = &bssPhyParamForAC_VO[mode]; + break; + case WME_AC_BE: + default: + pPhyParam = &phyParamForAC_BE[mode]; + pBssPhyParam = &bssPhyParamForAC_BE[mode]; + break; + } + wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + setwmeparams(vap, "chan", i, wmep, pPhyParam); + } else { + setwmeparams(vap, "chan", i, wmep, pBssPhyParam); + } + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; + setwmeparams(vap, "bss ", i, wmep, pBssPhyParam); + } + /* NB: check ic_bss to avoid NULL deref on initial attach */ + if (vap->iv_bss != NULL) { + /* + * Calculate agressive mode switching threshold based + * on beacon interval. This doesn't need locking since + * we're only called before entering the RUN state at + * which point we start sending beacon frames. + */ + wme->wme_hipri_switch_thresh = + (HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100; + wme->wme_flags &= ~WME_F_AGGRMODE; + ieee80211_wme_updateparams(vap); + } +} + +void +ieee80211_wme_initparams(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + ieee80211_wme_initparams_locked(vap); + IEEE80211_UNLOCK(ic); +} + +/* + * Update WME parameters for ourself and the BSS. + */ +void +ieee80211_wme_updateparams_locked(struct ieee80211vap *vap) +{ + static const paramType aggrParam[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = { 2, 4, 10, 64, 0 }, + [IEEE80211_MODE_11A] = { 2, 4, 10, 64, 0 }, + [IEEE80211_MODE_11B] = { 2, 5, 10, 64, 0 }, + [IEEE80211_MODE_11G] = { 2, 4, 10, 64, 0 }, + [IEEE80211_MODE_FH] = { 2, 5, 10, 64, 0 }, + [IEEE80211_MODE_TURBO_A] = { 1, 3, 10, 64, 0 }, + [IEEE80211_MODE_TURBO_G] = { 1, 3, 10, 64, 0 }, + [IEEE80211_MODE_STURBO_A] = { 1, 3, 10, 64, 0 }, + [IEEE80211_MODE_HALF] = { 2, 4, 10, 64, 0 }, + [IEEE80211_MODE_QUARTER] = { 2, 4, 10, 64, 0 }, + [IEEE80211_MODE_11NA] = { 2, 4, 10, 64, 0 }, /* XXXcheck*/ + [IEEE80211_MODE_11NG] = { 2, 4, 10, 64, 0 }, /* XXXcheck*/ + }; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_wme_state *wme = &ic->ic_wme; + const struct wmeParams *wmep; + struct wmeParams *chanp, *bssp; + enum ieee80211_phymode mode; + int i; + + /* + * Set up the channel access parameters for the physical + * device. First populate the configured settings. + */ + for (i = 0; i < WME_NUM_AC; i++) { + chanp = &wme->wme_chanParams.cap_wmeParams[i]; + wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; + chanp->wmep_aifsn = wmep->wmep_aifsn; + chanp->wmep_logcwmin = wmep->wmep_logcwmin; + chanp->wmep_logcwmax = wmep->wmep_logcwmax; + chanp->wmep_txopLimit = wmep->wmep_txopLimit; + + chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; + wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; + chanp->wmep_aifsn = wmep->wmep_aifsn; + chanp->wmep_logcwmin = wmep->wmep_logcwmin; + chanp->wmep_logcwmax = wmep->wmep_logcwmax; + chanp->wmep_txopLimit = wmep->wmep_txopLimit; + } + + /* + * Select mode; we can be called early in which case we + * always use auto mode. We know we'll be called when + * entering the RUN state with bsschan setup properly + * so state will eventually get set correctly + */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) + mode = ieee80211_chan2mode(ic->ic_bsschan); + else + mode = IEEE80211_MODE_AUTO; + + /* + * This implements agressive mode as found in certain + * vendors' AP's. When there is significant high + * priority (VI/VO) traffic in the BSS throttle back BE + * traffic by using conservative parameters. Otherwise + * BE uses agressive params to optimize performance of + * legacy/non-QoS traffic. + */ + if ((vap->iv_opmode == IEEE80211_M_HOSTAP && + (wme->wme_flags & WME_F_AGGRMODE) != 0) || + (vap->iv_opmode == IEEE80211_M_STA && + (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || + (vap->iv_flags & IEEE80211_F_WME) == 0) { + chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; + bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; + + chanp->wmep_aifsn = bssp->wmep_aifsn = aggrParam[mode].aifsn; + chanp->wmep_logcwmin = bssp->wmep_logcwmin = + aggrParam[mode].logcwmin; + chanp->wmep_logcwmax = bssp->wmep_logcwmax = + aggrParam[mode].logcwmax; + chanp->wmep_txopLimit = bssp->wmep_txopLimit = + (vap->iv_flags & IEEE80211_F_BURST) ? + aggrParam[mode].txopLimit : 0; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, + "update %s (chan+bss) [acm %u aifsn %u logcwmin %u " + "logcwmax %u txop %u]\n", ieee80211_wme_acnames[WME_AC_BE], + chanp->wmep_acm, chanp->wmep_aifsn, chanp->wmep_logcwmin, + chanp->wmep_logcwmax, chanp->wmep_txopLimit); + } + + if (vap->iv_opmode == IEEE80211_M_HOSTAP && + ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { + static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = 3, + [IEEE80211_MODE_11A] = 3, + [IEEE80211_MODE_11B] = 4, + [IEEE80211_MODE_11G] = 3, + [IEEE80211_MODE_FH] = 4, + [IEEE80211_MODE_TURBO_A] = 3, + [IEEE80211_MODE_TURBO_G] = 3, + [IEEE80211_MODE_STURBO_A] = 3, + [IEEE80211_MODE_HALF] = 3, + [IEEE80211_MODE_QUARTER] = 3, + [IEEE80211_MODE_11NA] = 3, + [IEEE80211_MODE_11NG] = 3, + }; + chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; + bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; + + chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, + "update %s (chan+bss) logcwmin %u\n", + ieee80211_wme_acnames[WME_AC_BE], chanp->wmep_logcwmin); + } + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ + /* + * Arrange for a beacon update and bump the parameter + * set number so associated stations load the new values. + */ + wme->wme_bssChanParams.cap_info = + (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; + ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME); + } + + wme->wme_update(ic); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, + "%s: WME params updated, cap_info 0x%x\n", __func__, + vap->iv_opmode == IEEE80211_M_STA ? + wme->wme_wmeChanParams.cap_info : + wme->wme_bssChanParams.cap_info); +} + +void +ieee80211_wme_updateparams(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + if (ic->ic_caps & IEEE80211_C_WME) { + IEEE80211_LOCK(ic); + ieee80211_wme_updateparams_locked(vap); + IEEE80211_UNLOCK(ic); + } +} + +static void +parent_updown(void *arg, int npending) +{ + struct ifnet *parent = arg; + + parent->if_ioctl(parent, SIOCSIFFLAGS, NULL); +} + +static void +update_mcast(void *arg, int npending) +{ + struct ieee80211com *ic = arg; + struct ifnet *parent = ic->ic_ifp; + + ic->ic_update_mcast(parent); +} + +static void +update_promisc(void *arg, int npending) +{ + struct ieee80211com *ic = arg; + struct ifnet *parent = ic->ic_ifp; + + ic->ic_update_promisc(parent); +} + +static void +update_channel(void *arg, int npending) +{ + struct ieee80211com *ic = arg; + + ic->ic_set_channel(ic); + ieee80211_radiotap_chan_change(ic); +} + +/* + * Block until the parent is in a known state. This is + * used after any operations that dispatch a task (e.g. + * to auto-configure the parent device up/down). + */ +void +ieee80211_waitfor_parent(struct ieee80211com *ic) +{ + taskqueue_block(ic->ic_tq); + ieee80211_draintask(ic, &ic->ic_parent_task); + ieee80211_draintask(ic, &ic->ic_mcast_task); + ieee80211_draintask(ic, &ic->ic_promisc_task); + ieee80211_draintask(ic, &ic->ic_chan_task); + ieee80211_draintask(ic, &ic->ic_bmiss_task); + taskqueue_unblock(ic->ic_tq); +} + +/* + * Start a vap running. If this is the first vap to be + * set running on the underlying device then we + * automatically bring the device up. + */ +void +ieee80211_start_locked(struct ieee80211vap *vap) +{ + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *parent = ic->ic_ifp; + + IEEE80211_LOCK_ASSERT(ic); + + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "start running, %d vaps running\n", ic->ic_nrunning); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + /* + * Mark us running. Note that it's ok to do this first; + * if we need to bring the parent device up we defer that + * to avoid dropping the com lock. We expect the device + * to respond to being marked up by calling back into us + * through ieee80211_start_all at which point we'll come + * back in here and complete the work. + */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + /* + * We are not running; if this we are the first vap + * to be brought up auto-up the parent if necessary. + */ + if (ic->ic_nrunning++ == 0 && + (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "%s: up parent %s\n", __func__, parent->if_xname); + parent->if_flags |= IFF_UP; + ieee80211_runtask(ic, &ic->ic_parent_task); + return; + } + } + /* + * If the parent is up and running, then kick the + * 802.11 state machine as appropriate. + */ + if ((parent->if_drv_flags & IFF_DRV_RUNNING) && + vap->iv_roaming != IEEE80211_ROAMING_MANUAL) { + if (vap->iv_opmode == IEEE80211_M_STA) { +#if 0 + /* XXX bypasses scan too easily; disable for now */ + /* + * Try to be intelligent about clocking the state + * machine. If we're currently in RUN state then + * we should be able to apply any new state/parameters + * simply by re-associating. Otherwise we need to + * re-scan to select an appropriate ap. + */ + if (vap->iv_state >= IEEE80211_S_RUN) + ieee80211_new_state_locked(vap, + IEEE80211_S_ASSOC, 1); + else +#endif + ieee80211_new_state_locked(vap, + IEEE80211_S_SCAN, 0); + } else { + /* + * For monitor+wds mode there's nothing to do but + * start running. Otherwise if this is the first + * vap to be brought up, start a scan which may be + * preempted if the station is locked to a particular + * channel. + */ + vap->iv_flags_ext |= IEEE80211_FEXT_REINIT; + if (vap->iv_opmode == IEEE80211_M_MONITOR || + vap->iv_opmode == IEEE80211_M_WDS) + ieee80211_new_state_locked(vap, + IEEE80211_S_RUN, -1); + else + ieee80211_new_state_locked(vap, + IEEE80211_S_SCAN, 0); + } + } +} + +/* + * Start a single vap. + */ +void +ieee80211_init(void *arg) +{ + struct ieee80211vap *vap = arg; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "%s\n", __func__); + + IEEE80211_LOCK(vap->iv_ic); + ieee80211_start_locked(vap); + IEEE80211_UNLOCK(vap->iv_ic); +} + +/* + * Start all runnable vap's on a device. + */ +void +ieee80211_start_all(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK(ic); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ifnet *ifp = vap->iv_ifp; + if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ + ieee80211_start_locked(vap); + } + IEEE80211_UNLOCK(ic); +} + +/* + * Stop a vap. We force it down using the state machine + * then mark it's ifnet not running. If this is the last + * vap running on the underlying device then we close it + * too to insure it will be properly initialized when the + * next vap is brought up. + */ +void +ieee80211_stop_locked(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = vap->iv_ifp; + struct ifnet *parent = ic->ic_ifp; + + IEEE80211_LOCK_ASSERT(ic); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "stop running, %d vaps running\n", ic->ic_nrunning); + + ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* mark us stopped */ + if (--ic->ic_nrunning == 0 && + (parent->if_drv_flags & IFF_DRV_RUNNING)) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "down parent %s\n", parent->if_xname); + parent->if_flags &= ~IFF_UP; + ieee80211_runtask(ic, &ic->ic_parent_task); + } + } +} + +void +ieee80211_stop(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + IEEE80211_LOCK(ic); + ieee80211_stop_locked(vap); + IEEE80211_UNLOCK(ic); +} + +/* + * Stop all vap's running on a device. + */ +void +ieee80211_stop_all(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK(ic); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ifnet *ifp = vap->iv_ifp; + if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ + ieee80211_stop_locked(vap); + } + IEEE80211_UNLOCK(ic); + + ieee80211_waitfor_parent(ic); +} + +/* + * Stop all vap's running on a device and arrange + * for those that were running to be resumed. + */ +void +ieee80211_suspend_all(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK(ic); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ifnet *ifp = vap->iv_ifp; + if (IFNET_IS_UP_RUNNING(ifp)) { /* NB: avoid recursion */ + vap->iv_flags_ext |= IEEE80211_FEXT_RESUME; + ieee80211_stop_locked(vap); + } + } + IEEE80211_UNLOCK(ic); + + ieee80211_waitfor_parent(ic); +} + +/* + * Start all vap's marked for resume. + */ +void +ieee80211_resume_all(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK(ic); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ifnet *ifp = vap->iv_ifp; + if (!IFNET_IS_UP_RUNNING(ifp) && + (vap->iv_flags_ext & IEEE80211_FEXT_RESUME)) { + vap->iv_flags_ext &= ~IEEE80211_FEXT_RESUME; + ieee80211_start_locked(vap); + } + } + IEEE80211_UNLOCK(ic); +} + +void +ieee80211_beacon_miss(struct ieee80211com *ic) +{ + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + /* Process in a taskq, the handler may reenter the driver */ + ieee80211_runtask(ic, &ic->ic_bmiss_task); + } + IEEE80211_UNLOCK(ic); +} + +static void +beacon_miss(void *arg, int npending) +{ + struct ieee80211com *ic = arg; + struct ieee80211vap *vap; + + /* XXX locking */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + /* + * We only pass events through for sta vap's in RUN state; + * may be too restrictive but for now this saves all the + * handlers duplicating these checks. + */ + if (vap->iv_opmode == IEEE80211_M_STA && + vap->iv_state >= IEEE80211_S_RUN && + vap->iv_bmiss != NULL) + vap->iv_bmiss(vap); + } +} + +static void +beacon_swmiss(void *arg, int npending) +{ + struct ieee80211vap *vap = arg; + + if (vap->iv_state != IEEE80211_S_RUN) + return; + + /* XXX Call multiple times if npending > zero? */ + vap->iv_bmiss(vap); +} + +/* + * Software beacon miss handling. Check if any beacons + * were received in the last period. If not post a + * beacon miss; otherwise reset the counter. + */ +void +ieee80211_swbmiss(void *arg) +{ + struct ieee80211vap *vap = arg; + struct ieee80211com *ic = vap->iv_ic; + + /* XXX sleep state? */ + KASSERT(vap->iv_state == IEEE80211_S_RUN, + ("wrong state %d", vap->iv_state)); + + if (ic->ic_flags & IEEE80211_F_SCAN) { + /* + * If scanning just ignore and reset state. If we get a + * bmiss after coming out of scan because we haven't had + * time to receive a beacon then we should probe the AP + * before posting a real bmiss (unless iv_bmiss_max has + * been artifiically lowered). A cleaner solution might + * be to disable the timer on scan start/end but to handle + * case of multiple sta vap's we'd need to disable the + * timers of all affected vap's. + */ + vap->iv_swbmiss_count = 0; + } else if (vap->iv_swbmiss_count == 0) { + if (vap->iv_bmiss != NULL) + ieee80211_runtask(ic, &vap->iv_swbmiss_task); + } else + vap->iv_swbmiss_count = 0; + callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, + ieee80211_swbmiss, vap); +} + +/* + * Start an 802.11h channel switch. We record the parameters, + * mark the operation pending, notify each vap through the + * beacon update mechanism so it can update the beacon frame + * contents, and then switch vap's to CSA state to block outbound + * traffic. Devices that handle CSA directly can use the state + * switch to do the right thing so long as they call + * ieee80211_csa_completeswitch when it's time to complete the + * channel change. Devices that depend on the net80211 layer can + * use ieee80211_beacon_update to handle the countdown and the + * channel switch. + */ +void +ieee80211_csa_startswitch(struct ieee80211com *ic, + struct ieee80211_channel *c, int mode, int count) +{ + struct ieee80211vap *vap; + + IEEE80211_LOCK_ASSERT(ic); + + ic->ic_csa_newchan = c; + ic->ic_csa_mode = mode; + ic->ic_csa_count = count; + ic->ic_flags |= IEEE80211_F_CSAPENDING; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_MBSS) + ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA); + /* switch to CSA state to block outbound traffic */ + if (vap->iv_state == IEEE80211_S_RUN) + ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0); + } + ieee80211_notify_csa(ic, c, mode, count); +} + +static void +csa_completeswitch(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + + ic->ic_csa_newchan = NULL; + ic->ic_flags &= ~IEEE80211_F_CSAPENDING; + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_state == IEEE80211_S_CSA) + ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); +} + +/* + * Complete an 802.11h channel switch started by ieee80211_csa_startswitch. + * We clear state and move all vap's in CSA state to RUN state + * so they can again transmit. + */ +void +ieee80211_csa_completeswitch(struct ieee80211com *ic) +{ + IEEE80211_LOCK_ASSERT(ic); + + KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending")); + + ieee80211_setcurchan(ic, ic->ic_csa_newchan); + csa_completeswitch(ic); +} + +/* + * Cancel an 802.11h channel switch started by ieee80211_csa_startswitch. + * We clear state and move all vap's in CSA state to RUN state + * so they can again transmit. + */ +void +ieee80211_csa_cancelswitch(struct ieee80211com *ic) +{ + IEEE80211_LOCK_ASSERT(ic); + + csa_completeswitch(ic); +} + +/* + * Complete a DFS CAC started by ieee80211_dfs_cac_start. + * We clear state and move all vap's in CAC state to RUN state. + */ +void +ieee80211_cac_completeswitch(struct ieee80211vap *vap0) +{ + struct ieee80211com *ic = vap0->iv_ic; + struct ieee80211vap *vap; + + IEEE80211_LOCK(ic); + /* + * Complete CAC state change for lead vap first; then + * clock all the other vap's waiting. + */ + KASSERT(vap0->iv_state == IEEE80211_S_CAC, + ("wrong state %d", vap0->iv_state)); + ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0); + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_state == IEEE80211_S_CAC) + ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); + IEEE80211_UNLOCK(ic); +} + +/* + * Force all vap's other than the specified vap to the INIT state + * and mark them as waiting for a scan to complete. These vaps + * will be brought up when the scan completes and the scanning vap + * reaches RUN state by wakeupwaiting. + */ +static void +markwaiting(struct ieee80211vap *vap0) +{ + struct ieee80211com *ic = vap0->iv_ic; + struct ieee80211vap *vap; + + IEEE80211_LOCK_ASSERT(ic); + + /* + * A vap list entry can not disappear since we are running on the + * taskqueue and a vap destroy will queue and drain another state + * change task. + */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap == vap0) + continue; + if (vap->iv_state != IEEE80211_S_INIT) { + /* NB: iv_newstate may drop the lock */ + vap->iv_newstate(vap, IEEE80211_S_INIT, 0); + vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; + } + } +} + +/* + * Wakeup all vap's waiting for a scan to complete. This is the + * companion to markwaiting (above) and is used to coordinate + * multiple vaps scanning. + * This is called from the state taskqueue. + */ +static void +wakeupwaiting(struct ieee80211vap *vap0) +{ + struct ieee80211com *ic = vap0->iv_ic; + struct ieee80211vap *vap; + + IEEE80211_LOCK_ASSERT(ic); + + /* + * A vap list entry can not disappear since we are running on the + * taskqueue and a vap destroy will queue and drain another state + * change task. + */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap == vap0) + continue; + if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) { + vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; + /* NB: sta's cannot go INIT->RUN */ + /* NB: iv_newstate may drop the lock */ + vap->iv_newstate(vap, + vap->iv_opmode == IEEE80211_M_STA ? + IEEE80211_S_SCAN : IEEE80211_S_RUN, 0); + } + } +} + +/* + * Handle post state change work common to all operating modes. + */ +static void +ieee80211_newstate_cb(void *xvap, int npending) +{ + struct ieee80211vap *vap = xvap; + struct ieee80211com *ic = vap->iv_ic; + enum ieee80211_state nstate, ostate; + int arg, rc; + + IEEE80211_LOCK(ic); + nstate = vap->iv_nstate; + arg = vap->iv_nstate_arg; + + if (vap->iv_flags_ext & IEEE80211_FEXT_REINIT) { + /* + * We have been requested to drop back to the INIT before + * proceeding to the new state. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %s -> %s arg %d\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[IEEE80211_S_INIT], arg); + vap->iv_newstate(vap, IEEE80211_S_INIT, arg); + vap->iv_flags_ext &= ~IEEE80211_FEXT_REINIT; + } + + ostate = vap->iv_state; + if (nstate == IEEE80211_S_SCAN && ostate != IEEE80211_S_INIT) { + /* + * SCAN was forced; e.g. on beacon miss. Force other running + * vap's to INIT state and mark them as waiting for the scan to + * complete. This insures they don't interfere with our + * scanning. Since we are single threaded the vaps can not + * transition again while we are executing. + * + * XXX not always right, assumes ap follows sta + */ + markwaiting(vap); + } + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %s -> %s arg %d\n", __func__, + ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg); + + rc = vap->iv_newstate(vap, nstate, arg); + vap->iv_flags_ext &= ~IEEE80211_FEXT_STATEWAIT; + if (rc != 0) { + /* State transition failed */ + KASSERT(rc != EINPROGRESS, ("iv_newstate was deferred")); + KASSERT(nstate != IEEE80211_S_INIT, + ("INIT state change failed")); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %s returned error %d\n", __func__, + ieee80211_state_name[nstate], rc); + goto done; + } + + /* No actual transition, skip post processing */ + if (ostate == nstate) + goto done; + + if (nstate == IEEE80211_S_RUN) { + /* + * OACTIVE may be set on the vap if the upper layer + * tried to transmit (e.g. IPv6 NDP) before we reach + * RUN state. Clear it and restart xmit. + * + * Note this can also happen as a result of SLEEP->RUN + * (i.e. coming out of power save mode). + */ + vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + if_start(vap->iv_ifp); + + /* bring up any vaps waiting on us */ + wakeupwaiting(vap); + } else if (nstate == IEEE80211_S_INIT) { + /* + * Flush the scan cache if we did the last scan (XXX?) + * and flush any frames on send queues from this vap. + * Note the mgt q is used only for legacy drivers and + * will go away shortly. + */ + ieee80211_scan_flush(vap); + + /* XXX NB: cast for altq */ + ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap); + } +done: + IEEE80211_UNLOCK(ic); +} + +/* + * Public interface for initiating a state machine change. + * This routine single-threads the request and coordinates + * the scheduling of multiple vaps for the purpose of selecting + * an operating channel. Specifically the following scenarios + * are handled: + * o only one vap can be selecting a channel so on transition to + * SCAN state if another vap is already scanning then + * mark the caller for later processing and return without + * doing anything (XXX? expectations by caller of synchronous operation) + * o only one vap can be doing CAC of a channel so on transition to + * CAC state if another vap is already scanning for radar then + * mark the caller for later processing and return without + * doing anything (XXX? expectations by caller of synchronous operation) + * o if another vap is already running when a request is made + * to SCAN then an operating channel has been chosen; bypass + * the scan and just join the channel + * + * Note that the state change call is done through the iv_newstate + * method pointer so any driver routine gets invoked. The driver + * will normally call back into operating mode-specific + * ieee80211_newstate routines (below) unless it needs to completely + * bypass the state machine (e.g. because the firmware has it's + * own idea how things should work). Bypassing the net80211 layer + * is usually a mistake and indicates lack of proper integration + * with the net80211 layer. + */ +static int +ieee80211_new_state_locked(struct ieee80211vap *vap, + enum ieee80211_state nstate, int arg) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211vap *vp; + enum ieee80211_state ostate; + int nrunning, nscanning; + + IEEE80211_LOCK_ASSERT(ic); + + if (vap->iv_flags_ext & IEEE80211_FEXT_STATEWAIT) { + if (vap->iv_nstate == IEEE80211_S_INIT) { + /* + * XXX The vap is being stopped, do no allow any other + * state changes until this is completed. + */ + return -1; + } else if (vap->iv_state != vap->iv_nstate) { +#if 0 + /* Warn if the previous state hasn't completed. */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: pending %s -> %s transition lost\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[vap->iv_nstate]); +#else + /* XXX temporarily enable to identify issues */ + if_printf(vap->iv_ifp, + "%s: pending %s -> %s transition lost\n", + __func__, ieee80211_state_name[vap->iv_state], + ieee80211_state_name[vap->iv_nstate]); +#endif + } + } + + nrunning = nscanning = 0; + /* XXX can track this state instead of calculating */ + TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) { + if (vp != vap) { + if (vp->iv_state >= IEEE80211_S_RUN) + nrunning++; + /* XXX doesn't handle bg scan */ + /* NB: CAC+AUTH+ASSOC treated like SCAN */ + else if (vp->iv_state > IEEE80211_S_INIT) + nscanning++; + } + } + ostate = vap->iv_state; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__, + ieee80211_state_name[ostate], ieee80211_state_name[nstate], + nrunning, nscanning); + switch (nstate) { + case IEEE80211_S_SCAN: + if (ostate == IEEE80211_S_INIT) { + /* + * INIT -> SCAN happens on initial bringup. + */ + KASSERT(!(nscanning && nrunning), + ("%d scanning and %d running", nscanning, nrunning)); + if (nscanning) { + /* + * Someone is scanning, defer our state + * change until the work has completed. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: defer %s -> %s\n", + __func__, ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; + return 0; + } + if (nrunning) { + /* + * Someone is operating; just join the channel + * they have chosen. + */ + /* XXX kill arg? */ + /* XXX check each opmode, adhoc? */ + if (vap->iv_opmode == IEEE80211_M_STA) + nstate = IEEE80211_S_SCAN; + else + nstate = IEEE80211_S_RUN; +#ifdef IEEE80211_DEBUG + if (nstate != IEEE80211_S_SCAN) { + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_STATE, + "%s: override, now %s -> %s\n", + __func__, + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + } +#endif + } + } + break; + case IEEE80211_S_RUN: + if (vap->iv_opmode == IEEE80211_M_WDS && + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) && + nscanning) { + /* + * Legacy WDS with someone else scanning; don't + * go online until that completes as we should + * follow the other vap to the channel they choose. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: defer %s -> %s (legacy WDS)\n", __func__, + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; + return 0; + } + if (vap->iv_opmode == IEEE80211_M_HOSTAP && + IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && + (vap->iv_flags_ext & IEEE80211_FEXT_DFS) && + !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) { + /* + * This is a DFS channel, transition to CAC state + * instead of RUN. This allows us to initiate + * Channel Availability Check (CAC) as specified + * by 11h/DFS. + */ + nstate = IEEE80211_S_CAC; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, + "%s: override %s -> %s (DFS)\n", __func__, + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + } + break; + case IEEE80211_S_INIT: + /* cancel any scan in progress */ + ieee80211_cancel_scan(vap); + if (ostate == IEEE80211_S_INIT ) { + /* XXX don't believe this */ + /* INIT -> INIT. nothing to do */ + vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; + } + /* fall thru... */ + default: + break; + } + /* defer the state change to a thread */ + vap->iv_nstate = nstate; + vap->iv_nstate_arg = arg; + vap->iv_flags_ext |= IEEE80211_FEXT_STATEWAIT; + ieee80211_runtask(ic, &vap->iv_nstate_task); + return EINPROGRESS; +} + +int +ieee80211_new_state(struct ieee80211vap *vap, + enum ieee80211_state nstate, int arg) +{ + struct ieee80211com *ic = vap->iv_ic; + int rc; + + IEEE80211_LOCK(ic); + rc = ieee80211_new_state_locked(vap, nstate, arg); + IEEE80211_UNLOCK(ic); + return rc; +} diff --git a/freebsd/sys/net80211/ieee80211_proto.h b/freebsd/sys/net80211/ieee80211_proto.h new file mode 100644 index 00000000..f81a6433 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_proto.h @@ -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 _NET80211_IEEE80211_PROTO_HH_ +#define _NET80211_IEEE80211_PROTO_HH_ + +/* + * 802.11 protocol implementation definitions. + */ + +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_meshconf; /* start of MESHCONF element */ + uint8_t *bo_spare[3]; +}; +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 */ + IEEE80211_BEACON_MESHCONF = 11, /* Mesh Configuration */ +}; +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 /* _NET80211_IEEE80211_PROTO_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_radiotap.c b/freebsd/sys/net80211/ieee80211_radiotap.c new file mode 100644 index 00000000..f630ae99 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_radiotap.c @@ -0,0 +1,357 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 radiotap support. + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/bpf.h> +#include <freebsd/net/if.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/if_media.h> + +#include <freebsd/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; +} diff --git a/freebsd/sys/net80211/ieee80211_radiotap.h b/freebsd/sys/net80211/ieee80211_radiotap.h new file mode 100644 index 00000000..89eac4be --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_radiotap.h @@ -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 _NET80211_IEEE80211_RADIOTAP_HH_ +#define _NET80211_IEEE80211_RADIOTAP_HH_ + +/* 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 /* !_NET80211_IEEE80211_RADIOTAP_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_ratectl.c b/freebsd/sys/net80211/ieee80211_ratectl.c new file mode 100644 index 00000000..8eede3cb --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_ratectl.c @@ -0,0 +1,94 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org> + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_ratectl.h> + +static const struct ieee80211_ratectl *ratectls[IEEE80211_RATECTL_MAX]; + +static const char *ratectl_modnames[IEEE80211_RATECTL_MAX] = { + [IEEE80211_RATECTL_AMRR] = "wlan_amrr", + [IEEE80211_RATECTL_RSSADAPT] = "wlan_rssadapt", + [IEEE80211_RATECTL_ONOE] = "wlan_onoe", + [IEEE80211_RATECTL_SAMPLE] = "wlan_sample", + [IEEE80211_RATECTL_NONE] = "wlan_none", +}; + +MALLOC_DEFINE(M_80211_RATECTL, "80211ratectl", "802.11 rate control"); + +void +ieee80211_ratectl_register(int type, const struct ieee80211_ratectl *ratectl) +{ + if (type >= IEEE80211_RATECTL_MAX) + return; + ratectls[type] = ratectl; +} + +void +ieee80211_ratectl_unregister(int type) +{ + if (type >= IEEE80211_RATECTL_MAX) + return; + ratectls[type] = NULL; +} + +void +ieee80211_ratectl_init(struct ieee80211vap *vap) +{ + if (vap->iv_rate == ratectls[IEEE80211_RATECTL_NONE]) + ieee80211_ratectl_set(vap, IEEE80211_RATECTL_AMRR); + vap->iv_rate->ir_init(vap); +} + +void +ieee80211_ratectl_set(struct ieee80211vap *vap, int type) +{ + if (type >= IEEE80211_RATECTL_MAX) + return; + if (ratectls[type] == NULL) { + ieee80211_load_module(ratectl_modnames[type]); + if (ratectls[type] == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_RATECTL, + "%s: unable to load algo %u, module %s\n", + __func__, type, ratectl_modnames[type]); + vap->iv_rate = ratectls[IEEE80211_RATECTL_NONE]; + return; + } + } + vap->iv_rate = ratectls[type]; +} diff --git a/freebsd/sys/net80211/ieee80211_ratectl.h b/freebsd/sys/net80211/ieee80211_ratectl.h new file mode 100644 index 00000000..be81781c --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_ratectl.h @@ -0,0 +1,117 @@ +/*- + * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org> + * 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$ + */ + +enum ieee80211_ratealgs { + IEEE80211_RATECTL_AMRR = 0, + IEEE80211_RATECTL_RSSADAPT = 1, + IEEE80211_RATECTL_ONOE = 2, + IEEE80211_RATECTL_SAMPLE = 3, + IEEE80211_RATECTL_NONE = 4, + IEEE80211_RATECTL_MAX +}; + +#define IEEE80211_RATECTL_TX_SUCCESS 1 +#define IEEE80211_RATECTL_TX_FAILURE 0 + +struct ieee80211_ratectl { + const char *ir_name; + int (*ir_attach)(const struct ieee80211vap *); + void (*ir_detach)(const struct ieee80211vap *); + void (*ir_init)(struct ieee80211vap *); + void (*ir_deinit)(struct ieee80211vap *); + void (*ir_node_init)(struct ieee80211_node *); + void (*ir_node_deinit)(struct ieee80211_node *); + int (*ir_rate)(struct ieee80211_node *, void *, uint32_t); + void (*ir_tx_complete)(const struct ieee80211vap *, + const struct ieee80211_node *, int, + void *, void *); + void (*ir_tx_update)(const struct ieee80211vap *, + const struct ieee80211_node *, + void *, void *, void *); + void (*ir_setinterval)(const struct ieee80211vap *, int); +}; + +void ieee80211_ratectl_register(int, const struct ieee80211_ratectl *); +void ieee80211_ratectl_unregister(int); +void ieee80211_ratectl_init(struct ieee80211vap *); +void ieee80211_ratectl_set(struct ieee80211vap *, int); + +MALLOC_DECLARE(M_80211_RATECTL); + +static void __inline +ieee80211_ratectl_deinit(struct ieee80211vap *vap) +{ + vap->iv_rate->ir_deinit(vap); +} + +static void __inline +ieee80211_ratectl_node_init(struct ieee80211_node *ni) +{ + const struct ieee80211vap *vap = ni->ni_vap; + + vap->iv_rate->ir_node_init(ni); +} + +static void __inline +ieee80211_ratectl_node_deinit(struct ieee80211_node *ni) +{ + const struct ieee80211vap *vap = ni->ni_vap; + + vap->iv_rate->ir_node_deinit(ni); +} + +static int __inline +ieee80211_ratectl_rate(struct ieee80211_node *ni, void *arg, uint32_t iarg) +{ + const struct ieee80211vap *vap = ni->ni_vap; + + return vap->iv_rate->ir_rate(ni, arg, iarg); +} + +static void __inline +ieee80211_ratectl_tx_complete(const struct ieee80211vap *vap, + const struct ieee80211_node *ni, int status, void *arg1, void *arg2) +{ + vap->iv_rate->ir_tx_complete(vap, ni, status, arg1, arg2); +} + +static void __inline +ieee80211_ratectl_tx_update(const struct ieee80211vap *vap, + const struct ieee80211_node *ni, void *arg1, void *arg2, void *arg3) +{ + if (vap->iv_rate->ir_tx_update == NULL) + return; + vap->iv_rate->ir_tx_update(vap, ni, arg1, arg2, arg3); +} + +static void __inline +ieee80211_ratectl_setinterval(const struct ieee80211vap *vap, int msecs) +{ + if (vap->iv_rate->ir_setinterval == NULL) + return; + vap->iv_rate->ir_setinterval(vap, msecs); +} diff --git a/freebsd/sys/net80211/ieee80211_ratectl_none.c b/freebsd/sys/net80211/ieee80211_ratectl_none.c new file mode 100644 index 00000000..97f0749f --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_ratectl_none.c @@ -0,0 +1,116 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * Copyright (c) 2010 Bernhard Schmidt <bschmidt@FreeBSD.org> + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/module.h> +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> + +#ifdef INET +#include <freebsd/netinet/in.h> +#include <freebsd/netinet/if_ether.h> +#endif + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_ratectl.h> + +static void +none_init(struct ieee80211vap *vap) +{ +} + +static void +none_deinit(struct ieee80211vap *vap) +{ + free(vap->iv_rs, M_80211_RATECTL); +} + +static void +none_node_init(struct ieee80211_node *ni) +{ + ni->ni_txrate = ni->ni_rates.rs_rates[0] & IEEE80211_RATE_VAL; +} + +static void +none_node_deinit(struct ieee80211_node *ni) +{ +} + +static int +none_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) +{ + int rix = 0; + + ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL; + return rix; +} + +static void +none_tx_complete(const struct ieee80211vap *vap, + const struct ieee80211_node *ni, int ok, + void *arg1, void *arg2 __unused) +{ +} + +static void +none_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni, + void *arg1, void *arg2, void *arg3) +{ +} + +static void +none_setinterval(const struct ieee80211vap *vap, int msecs) +{ +} + +/* number of references from net80211 layer */ +static int nrefs = 0; + +static const struct ieee80211_ratectl none = { + .ir_name = "none", + .ir_attach = NULL, + .ir_detach = NULL, + .ir_init = none_init, + .ir_deinit = none_deinit, + .ir_node_init = none_node_init, + .ir_node_deinit = none_node_deinit, + .ir_rate = none_rate, + .ir_tx_complete = none_tx_complete, + .ir_tx_update = none_tx_update, + .ir_setinterval = none_setinterval, +}; +IEEE80211_RATECTL_MODULE(ratectl_none, 1); +IEEE80211_RATECTL_ALG(none, IEEE80211_RATECTL_NONE, none); diff --git a/freebsd/sys/net80211/ieee80211_regdomain.c b/freebsd/sys/net80211/ieee80211_regdomain.c new file mode 100644 index 00000000..e83132c8 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_regdomain.c @@ -0,0 +1,450 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 regdomain support. + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/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; +} diff --git a/freebsd/sys/net80211/ieee80211_regdomain.h b/freebsd/sys/net80211/ieee80211_regdomain.h new file mode 100644 index 00000000..f71c1093 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_regdomain.h @@ -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 _NET80211_IEEE80211_REGDOMAIN_HH_ +#define _NET80211_IEEE80211_REGDOMAIN_HH_ + +/* + * 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 /* _NET80211_IEEE80211_REGDOMAIN_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_rssadapt.c b/freebsd/sys/net80211/ieee80211_rssadapt.c new file mode 100644 index 00000000..b50d2e66 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_rssadapt.c @@ -0,0 +1,351 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/* $FreeBSD$ */ +/* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */ +/*- + * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org> + * 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 <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/module.h> +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_rssadapt.h> +#include <freebsd/net80211/ieee80211_ratectl.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_setinterval(const struct ieee80211vap *, int); +static void rssadapt_init(struct ieee80211vap *); +static void rssadapt_deinit(struct ieee80211vap *); +static void rssadapt_updatestats(struct ieee80211_rssadapt_node *); +static void rssadapt_node_init(struct ieee80211_node *); +static void rssadapt_node_deinit(struct ieee80211_node *); +static int rssadapt_rate(struct ieee80211_node *, void *, uint32_t); +static void rssadapt_lower_rate(struct ieee80211_rssadapt_node *, int, int); +static void rssadapt_raise_rate(struct ieee80211_rssadapt_node *, + int, int); +static void rssadapt_tx_complete(const struct ieee80211vap *, + const struct ieee80211_node *, int, + void *, void *); +static void rssadapt_sysctlattach(struct ieee80211vap *, + struct sysctl_ctx_list *, struct sysctl_oid *); + +/* number of references from net80211 layer */ +static int nrefs = 0; + +static const struct ieee80211_ratectl rssadapt = { + .ir_name = "rssadapt", + .ir_attach = NULL, + .ir_detach = NULL, + .ir_init = rssadapt_init, + .ir_deinit = rssadapt_deinit, + .ir_node_init = rssadapt_node_init, + .ir_node_deinit = rssadapt_node_deinit, + .ir_rate = rssadapt_rate, + .ir_tx_complete = rssadapt_tx_complete, + .ir_tx_update = NULL, + .ir_setinterval = rssadapt_setinterval, +}; +IEEE80211_RATECTL_MODULE(rssadapt, 1); +IEEE80211_RATECTL_ALG(rssadapt, IEEE80211_RATECTL_RSSADAPT, rssadapt); + +static void +rssadapt_setinterval(const struct ieee80211vap *vap, int msecs) +{ + struct ieee80211_rssadapt *rs = vap->iv_rs; + int t; + + if (msecs < 100) + msecs = 100; + t = msecs_to_ticks(msecs); + rs->interval = (t < 1) ? 1 : t; +} + +static void +rssadapt_init(struct ieee80211vap *vap) +{ + struct ieee80211_rssadapt *rs; + + KASSERT(vap->iv_rs == NULL, ("%s: iv_rs already initialized", + __func__)); + + vap->iv_rs = rs = malloc(sizeof(struct ieee80211_rssadapt), + M_80211_RATECTL, M_NOWAIT|M_ZERO); + if (rs == NULL) { + if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); + return; + } + rs->vap = vap; + rssadapt_setinterval(vap, 500 /* msecs */); + rssadapt_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid); +} + +static void +rssadapt_deinit(struct ieee80211vap *vap) +{ + free(vap->iv_rs, M_80211_RATECTL); +} + +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); +} + +static void +rssadapt_node_init(struct ieee80211_node *ni) +{ + struct ieee80211_rssadapt_node *ra; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_rssadapt *rsa = vap->iv_rs; + const struct ieee80211_rateset *rs = &ni->ni_rates; + + if (ni->ni_rctls == NULL) { + ni->ni_rctls = ra = + malloc(sizeof(struct ieee80211_rssadapt_node), + M_80211_RATECTL, M_NOWAIT|M_ZERO); + if (ra == NULL) { + if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " + "structure\n"); + return; + } + } else + ra = ni->ni_rctls; + 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 void +rssadapt_node_deinit(struct ieee80211_node *ni) +{ + + free(ni->ni_rctls, M_80211_RATECTL); +} + +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; +} + +static int +rssadapt_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg) +{ + struct ieee80211_rssadapt_node *ra = ni->ni_rctls; + u_int pktlen = iarg; + 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. + */ +static void +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); +} + +static void +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 void +rssadapt_tx_complete(const struct ieee80211vap *vap, + const struct ieee80211_node *ni, int success, void *arg1, void *arg2) +{ + struct ieee80211_rssadapt_node *ra = ni->ni_rctls; + int pktlen = *(int *)arg1, rssi = *(int *)arg2; + + if (success) { + ra->ra_nok++; + if ((ra->ra_rix + 1) < ra->ra_rates.rs_nrates && + (ticks - ra->ra_last_raise) >= ra->ra_raise_interval) + rssadapt_raise_rate(ra, pktlen, rssi); + } else { + ra->ra_nfail++; + rssadapt_lower_rate(ra, pktlen, rssi); + } +} + +static int +rssadapt_sysctl_interval(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211vap *vap = arg1; + struct ieee80211_rssadapt *rs = vap->iv_rs; + int msecs = ticks_to_msecs(rs->interval); + int error; + + error = sysctl_handle_int(oidp, &msecs, 0, req); + if (error || !req->newptr) + return error; + rssadapt_setinterval(vap, msecs); + return 0; +} + +static void +rssadapt_sysctlattach(struct ieee80211vap *vap, + 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, vap, + 0, rssadapt_sysctl_interval, "I", "rssadapt operation interval (ms)"); +} diff --git a/freebsd/sys/net80211/ieee80211_rssadapt.h b/freebsd/sys/net80211/ieee80211_rssadapt.h new file mode 100644 index 00000000..26211ece --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_rssadapt.h @@ -0,0 +1,71 @@ +/* $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 _NET80211_IEEE80211_RSSADAPT_HH_ +#define _NET80211_IEEE80211_RSSADAPT_HH_ + +/* 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 { + const 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]; +}; + +#define IEEE80211_RSSADAPT_SUCCESS 1 +#define IEEE80211_RSSADAPT_FAILURE 0 +#endif /* _NET80211_IEEE80211_RSSADAPT_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_scan.c b/freebsd/sys/net80211/ieee80211_scan.c new file mode 100644 index 00000000..bda486ec --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_scan.c @@ -0,0 +1,1240 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 scanning support. + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/condvar.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net80211/ieee80211_var.h> + +#include <freebsd/net/bpf.h> + +struct scan_state { + struct ieee80211_scan_state base; /* public state */ + + u_int ss_iflags; /* flags used internally */ +#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */ +#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */ +#define ISCAN_CANCEL 0x0004 /* cancel current scan */ +#define ISCAN_ABORT 0x0008 /* end the scan immediately */ + unsigned long ss_chanmindwell; /* min dwell on curchan */ + unsigned long ss_scanend; /* time scan must stop */ + u_int ss_duration; /* duration for next scan */ + struct task ss_scan_task; /* scan execution */ + struct cv ss_scan_cv; /* scan signal */ + struct callout ss_scan_timer; /* scan timer */ +}; +#define SCAN_PRIVATE(ss) ((struct scan_state *) ss) + +/* + * Amount of time to go off-channel during a background + * scan. This value should be large enough to catch most + * ap's but short enough that we can return on-channel + * before our listen interval expires. + * + * XXX tunable + * XXX check against configured listen interval + */ +#define IEEE80211_SCAN_OFFCHANNEL msecs_to_ticks(150) + +/* + * Roaming-related defaults. RSSI thresholds are as returned by the + * driver (.5dBm). Transmit rate thresholds are IEEE rate codes (i.e + * .5M units) or MCS. + */ +/* rssi thresholds */ +#define ROAM_RSSI_11A_DEFAULT 14 /* 11a bss */ +#define ROAM_RSSI_11B_DEFAULT 14 /* 11b bss */ +#define ROAM_RSSI_11BONLY_DEFAULT 14 /* 11b-only bss */ +/* transmit rate thresholds */ +#define ROAM_RATE_11A_DEFAULT 2*12 /* 11a bss */ +#define ROAM_RATE_11B_DEFAULT 2*5 /* 11b bss */ +#define ROAM_RATE_11BONLY_DEFAULT 2*1 /* 11b-only bss */ +#define ROAM_RATE_HALF_DEFAULT 2*6 /* half-width 11a/g bss */ +#define ROAM_RATE_QUARTER_DEFAULT 2*3 /* quarter-width 11a/g bss */ +#define ROAM_MCS_11N_DEFAULT (1 | IEEE80211_RATE_MCS) /* 11n bss */ + +static void scan_curchan(struct ieee80211_scan_state *, unsigned long); +static void scan_mindwell(struct ieee80211_scan_state *); +static void scan_signal(void *); +static void scan_task(void *, int); + +MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state"); + +void +ieee80211_scan_attach(struct ieee80211com *ic) +{ + struct scan_state *ss; + + ss = (struct scan_state *) malloc(sizeof(struct scan_state), + M_80211_SCAN, M_NOWAIT | M_ZERO); + if (ss == NULL) { + ic->ic_scan = NULL; + return; + } + callout_init_mtx(&ss->ss_scan_timer, IEEE80211_LOCK_OBJ(ic), 0); + cv_init(&ss->ss_scan_cv, "scan"); + TASK_INIT(&ss->ss_scan_task, 0, scan_task, ss); + ic->ic_scan = &ss->base; + ss->base.ss_ic = ic; + + ic->ic_scan_curchan = scan_curchan; + ic->ic_scan_mindwell = scan_mindwell; +} + +void +ieee80211_scan_detach(struct ieee80211com *ic) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + if (ss != NULL) { + IEEE80211_LOCK(ic); + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT; + scan_signal(ss); + IEEE80211_UNLOCK(ic); + ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); + callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer); + KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, + ("scan still running")); + if (ss->ss_ops != NULL) { + ss->ss_ops->scan_detach(ss); + ss->ss_ops = NULL; + } + ic->ic_scan = NULL; + free(SCAN_PRIVATE(ss), M_80211_SCAN); + } +} + +static const struct ieee80211_roamparam defroam[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_11A] = { .rssi = ROAM_RSSI_11A_DEFAULT, + .rate = ROAM_RATE_11A_DEFAULT }, + [IEEE80211_MODE_11G] = { .rssi = ROAM_RSSI_11B_DEFAULT, + .rate = ROAM_RATE_11B_DEFAULT }, + [IEEE80211_MODE_11B] = { .rssi = ROAM_RSSI_11BONLY_DEFAULT, + .rate = ROAM_RATE_11BONLY_DEFAULT }, + [IEEE80211_MODE_TURBO_A]= { .rssi = ROAM_RSSI_11A_DEFAULT, + .rate = ROAM_RATE_11A_DEFAULT }, + [IEEE80211_MODE_TURBO_G]= { .rssi = ROAM_RSSI_11A_DEFAULT, + .rate = ROAM_RATE_11A_DEFAULT }, + [IEEE80211_MODE_STURBO_A]={ .rssi = ROAM_RSSI_11A_DEFAULT, + .rate = ROAM_RATE_11A_DEFAULT }, + [IEEE80211_MODE_HALF] = { .rssi = ROAM_RSSI_11A_DEFAULT, + .rate = ROAM_RATE_HALF_DEFAULT }, + [IEEE80211_MODE_QUARTER]= { .rssi = ROAM_RSSI_11A_DEFAULT, + .rate = ROAM_RATE_QUARTER_DEFAULT }, + [IEEE80211_MODE_11NA] = { .rssi = ROAM_RSSI_11A_DEFAULT, + .rate = ROAM_MCS_11N_DEFAULT }, + [IEEE80211_MODE_11NG] = { .rssi = ROAM_RSSI_11B_DEFAULT, + .rate = ROAM_MCS_11N_DEFAULT }, +}; + +void +ieee80211_scan_vattach(struct ieee80211vap *vap) +{ + vap->iv_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz; + vap->iv_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz; + vap->iv_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz; + + vap->iv_roaming = IEEE80211_ROAMING_AUTO; + memcpy(vap->iv_roamparms, defroam, sizeof(defroam)); +} + +void +ieee80211_scan_vdetach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss; + + IEEE80211_LOCK(ic); + ss = ic->ic_scan; + if (ss != NULL && ss->ss_vap == vap) { + if (ic->ic_flags & IEEE80211_F_SCAN) { + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT; + scan_signal(ss); + } + if (ss->ss_ops != NULL) { + ss->ss_ops->scan_detach(ss); + ss->ss_ops = NULL; + } + ss->ss_vap = NULL; + } + IEEE80211_UNLOCK(ic); +} + +/* + * Simple-minded scanner module support. + */ +static const char *scan_modnames[IEEE80211_OPMODE_MAX] = { + "wlan_scan_sta", /* IEEE80211_M_IBSS */ + "wlan_scan_sta", /* IEEE80211_M_STA */ + "wlan_scan_wds", /* IEEE80211_M_WDS */ + "wlan_scan_sta", /* IEEE80211_M_AHDEMO */ + "wlan_scan_ap", /* IEEE80211_M_HOSTAP */ + "wlan_scan_monitor", /* IEEE80211_M_MONITOR */ + "wlan_scan_sta", /* IEEE80211_M_MBSS */ +}; +static const struct ieee80211_scanner *scanners[IEEE80211_OPMODE_MAX]; + +const struct ieee80211_scanner * +ieee80211_scanner_get(enum ieee80211_opmode mode) +{ + if (mode >= IEEE80211_OPMODE_MAX) + return NULL; + if (scanners[mode] == NULL) + ieee80211_load_module(scan_modnames[mode]); + return scanners[mode]; +} + +void +ieee80211_scanner_register(enum ieee80211_opmode mode, + const struct ieee80211_scanner *scan) +{ + if (mode >= IEEE80211_OPMODE_MAX) + return; + scanners[mode] = scan; +} + +void +ieee80211_scanner_unregister(enum ieee80211_opmode mode, + const struct ieee80211_scanner *scan) +{ + if (mode >= IEEE80211_OPMODE_MAX) + return; + if (scanners[mode] == scan) + scanners[mode] = NULL; +} + +void +ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan) +{ + int m; + + for (m = 0; m < IEEE80211_OPMODE_MAX; m++) + if (scanners[m] == scan) + scanners[m] = NULL; +} + +/* + * Update common scanner state to reflect the current + * operating mode. This is called when the state machine + * is transitioned to RUN state w/o scanning--e.g. when + * operating in monitor mode. The purpose of this is to + * ensure later callbacks find ss_ops set to properly + * reflect current operating mode. + */ +static void +scan_update_locked(struct ieee80211vap *vap, + const struct ieee80211_scanner *scan) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK_ASSERT(ic); + +#ifdef IEEE80211_DEBUG + if (ss->ss_vap != vap || ss->ss_ops != scan) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: current scanner is <%s:%s>, switch to <%s:%s>\n", + __func__, + ss->ss_vap != NULL ? + ss->ss_vap->iv_ifp->if_xname : "none", + ss->ss_vap != NULL ? + ieee80211_opmode_name[ss->ss_vap->iv_opmode] : "none", + vap->iv_ifp->if_xname, + ieee80211_opmode_name[vap->iv_opmode]); + } +#endif + ss->ss_vap = vap; + if (ss->ss_ops != scan) { + /* + * Switch scanners; detach old, attach new. Special + * case where a single scan module implements multiple + * policies by using different scan ops but a common + * core. We assume if the old and new attach methods + * are identical then it's ok to just change ss_ops + * and not flush the internal state of the module. + */ + if (scan == NULL || ss->ss_ops == NULL || + ss->ss_ops->scan_attach != scan->scan_attach) { + if (ss->ss_ops != NULL) + ss->ss_ops->scan_detach(ss); + if (scan != NULL && !scan->scan_attach(ss)) { + /* XXX attach failure */ + /* XXX stat+msg */ + scan = NULL; + } + } + ss->ss_ops = scan; + } +} + +static char +channel_type(const struct ieee80211_channel *c) +{ + if (IEEE80211_IS_CHAN_ST(c)) + return 'S'; + if (IEEE80211_IS_CHAN_108A(c)) + return 'T'; + if (IEEE80211_IS_CHAN_108G(c)) + return 'G'; + if (IEEE80211_IS_CHAN_HT(c)) + return 'n'; + if (IEEE80211_IS_CHAN_A(c)) + return 'a'; + if (IEEE80211_IS_CHAN_ANYG(c)) + return 'g'; + if (IEEE80211_IS_CHAN_B(c)) + return 'b'; + return 'f'; +} + +void +ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss) +{ + struct ieee80211com *ic = ss->ss_ic; + const char *sep; + int i; + + sep = ""; + for (i = ss->ss_next; i < ss->ss_last; i++) { + const struct ieee80211_channel *c = ss->ss_chans[i]; + + printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c), + channel_type(c)); + sep = ", "; + } +} + +#ifdef IEEE80211_DEBUG +static void +scan_dump(struct ieee80211_scan_state *ss) +{ + struct ieee80211vap *vap = ss->ss_vap; + + if_printf(vap->iv_ifp, "scan set "); + ieee80211_scan_dump_channels(ss); + printf(" dwell min %lums max %lums\n", + ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(ss->ss_maxdwell)); +} +#endif /* IEEE80211_DEBUG */ + +static void +copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss, + int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + if (nssid > IEEE80211_SCAN_MAX_SSID) { + /* XXX printf */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: too many ssid %d, ignoring all of them\n", + __func__, nssid); + return; + } + memcpy(ss->ss_ssid, ssids, nssid * sizeof(ssids[0])); + ss->ss_nssid = nssid; +} + +/* + * Start a scan unless one is already going. + */ +static int +start_scan_locked(const struct ieee80211_scanner *scan, + struct ieee80211vap *vap, int flags, u_int duration, + u_int mindwell, u_int maxdwell, + u_int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK_ASSERT(ic); + + if (ic->ic_flags & IEEE80211_F_CSAPENDING) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: scan inhibited by pending channel change\n", __func__); + } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n" + , __func__ + , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" + , duration, mindwell, maxdwell + , ieee80211_phymode_name[vap->iv_des_mode] + , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" + , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" + , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" + , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : "" + , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" + , flags & IEEE80211_SCAN_ONCE ? ", once" : "" + ); + + scan_update_locked(vap, scan); + if (ss->ss_ops != NULL) { + if ((flags & IEEE80211_SCAN_NOSSID) == 0) + copy_ssid(vap, ss, nssid, ssids); + + /* NB: top 4 bits for internal use */ + ss->ss_flags = flags & 0xfff; + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + vap->iv_stats.is_scan_active++; + else + vap->iv_stats.is_scan_passive++; + if (flags & IEEE80211_SCAN_FLUSH) + ss->ss_ops->scan_flush(ss); + + /* NB: flush frames rx'd before 1st channel change */ + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; + SCAN_PRIVATE(ss)->ss_duration = duration; + ss->ss_next = 0; + ss->ss_mindwell = mindwell; + ss->ss_maxdwell = maxdwell; + /* NB: scan_start must be before the scan runtask */ + ss->ss_ops->scan_start(ss, vap); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(vap)) + scan_dump(ss); +#endif /* IEEE80211_DEBUG */ + ic->ic_flags |= IEEE80211_F_SCAN; + ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); + } + } else { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan already in progress\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); + } + return (ic->ic_flags & IEEE80211_F_SCAN); +} + +/* + * Start a scan unless one is already going. + */ +int +ieee80211_start_scan(struct ieee80211vap *vap, int flags, + u_int duration, u_int mindwell, u_int maxdwell, + u_int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + struct ieee80211com *ic = vap->iv_ic; + const struct ieee80211_scanner *scan; + int result; + + scan = ieee80211_scanner_get(vap->iv_opmode); + if (scan == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no scanner support for %s mode\n", + __func__, ieee80211_opmode_name[vap->iv_opmode]); + /* XXX stat */ + return 0; + } + + IEEE80211_LOCK(ic); + result = start_scan_locked(scan, vap, flags, duration, + mindwell, maxdwell, nssid, ssids); + IEEE80211_UNLOCK(ic); + + return result; +} + +/* + * Check the scan cache for an ap/channel to use; if that + * fails then kick off a new scan. + */ +int +ieee80211_check_scan(struct ieee80211vap *vap, int flags, + u_int duration, u_int mindwell, u_int maxdwell, + u_int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + const struct ieee80211_scanner *scan; + int result; + + scan = ieee80211_scanner_get(vap->iv_opmode); + if (scan == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no scanner support for %s mode\n", + __func__, vap->iv_opmode); + /* XXX stat */ + return 0; + } + + /* + * Check if there's a list of scan candidates already. + * XXX want more than the ap we're currently associated with + */ + + IEEE80211_LOCK(ic); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan, %s%s%s%s%s\n" + , __func__ + , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" + , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" + , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" + , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" + , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" + , flags & IEEE80211_SCAN_ONCE ? ", once" : "" + ); + + if (ss->ss_ops != scan) { + /* XXX re-use cache contents? e.g. adhoc<->sta */ + flags |= IEEE80211_SCAN_FLUSH; + } + scan_update_locked(vap, scan); + if (ss->ss_ops != NULL) { + /* XXX verify ss_ops matches vap->iv_opmode */ + if ((flags & IEEE80211_SCAN_NOSSID) == 0) { + /* + * Update the ssid list and mark flags so if + * we call start_scan it doesn't duplicate work. + */ + copy_ssid(vap, ss, nssid, ssids); + flags |= IEEE80211_SCAN_NOSSID; + } + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && + (flags & IEEE80211_SCAN_FLUSH) == 0 && + time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { + /* + * We're not currently scanning and the cache is + * deemed hot enough to consult. Lock out others + * by marking IEEE80211_F_SCAN while we decide if + * something is already in the scan cache we can + * use. Also discard any frames that might come + * in while temporarily marked as scanning. + */ + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; + ic->ic_flags |= IEEE80211_F_SCAN; + + /* NB: need to use supplied flags in check */ + ss->ss_flags = flags & 0xff; + result = ss->ss_ops->scan_end(ss, vap); + + ic->ic_flags &= ~IEEE80211_F_SCAN; + SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD; + if (result) { + ieee80211_notify_scan_done(vap); + IEEE80211_UNLOCK(ic); + return 1; + } + } + } + result = start_scan_locked(scan, vap, flags, duration, + mindwell, maxdwell, nssid, ssids); + IEEE80211_UNLOCK(ic); + + return result; +} + +/* + * Check the scan cache for an ap/channel to use; if that fails + * then kick off a scan using the current settings. + */ +int +ieee80211_check_scan_current(struct ieee80211vap *vap) +{ + return ieee80211_check_scan(vap, + IEEE80211_SCAN_ACTIVE, + IEEE80211_SCAN_FOREVER, 0, 0, + vap->iv_des_nssid, vap->iv_des_ssid); +} + +/* + * Restart a previous scan. If the previous scan completed + * then we start again using the existing channel list. + */ +int +ieee80211_bg_scan(struct ieee80211vap *vap, int flags) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + const struct ieee80211_scanner *scan; + + scan = ieee80211_scanner_get(vap->iv_opmode); + if (scan == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no scanner support for %s mode\n", + __func__, vap->iv_opmode); + /* XXX stat */ + return 0; + } + + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + u_int duration; + /* + * Go off-channel for a fixed interval that is large + * enough to catch most ap's but short enough that + * we can return on-channel before our listen interval + * expires. + */ + duration = IEEE80211_SCAN_OFFCHANNEL; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan, ticks %u duration %lu\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", + ticks, duration); + + scan_update_locked(vap, scan); + if (ss->ss_ops != NULL) { + ss->ss_vap = vap; + /* + * A background scan does not select a new sta; it + * just refreshes the scan cache. Also, indicate + * the scan logic should follow the beacon schedule: + * we go off-channel and scan for a while, then + * return to the bss channel to receive a beacon, + * then go off-channel again. All during this time + * we notify the ap we're in power save mode. When + * the scan is complete we leave power save mode. + * If any beacon indicates there are frames pending + * for us then we drop out of power save mode + * (and background scan) automatically by way of the + * usual sta power save logic. + */ + ss->ss_flags |= IEEE80211_SCAN_NOPICK + | IEEE80211_SCAN_BGSCAN + | flags + ; + /* if previous scan completed, restart */ + if (ss->ss_next >= ss->ss_last) { + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + vap->iv_stats.is_scan_active++; + else + vap->iv_stats.is_scan_passive++; + /* + * NB: beware of the scan cache being flushed; + * if the channel list is empty use the + * scan_start method to populate it. + */ + ss->ss_next = 0; + if (ss->ss_last != 0) + ss->ss_ops->scan_restart(ss, vap); + else { + ss->ss_ops->scan_start(ss, vap); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(vap)) + scan_dump(ss); +#endif /* IEEE80211_DEBUG */ + } + } + /* NB: flush frames rx'd before 1st channel change */ + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; + SCAN_PRIVATE(ss)->ss_duration = duration; + ss->ss_maxdwell = duration; + ic->ic_flags |= IEEE80211_F_SCAN; + ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; + ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); + } else { + /* XXX msg+stat */ + } + } else { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan already in progress\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); + } + IEEE80211_UNLOCK(ic); + + /* NB: racey, does it matter? */ + return (ic->ic_flags & IEEE80211_F_SCAN); +} + +/* + * Cancel any scan currently going on for the specified vap. + */ +void +ieee80211_cancel_scan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) && + ss->ss_vap == vap && + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: cancel %s scan\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? + "active" : "passive"); + + /* clear bg scan NOPICK and mark cancel request */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; + /* wake up the scan task */ + scan_signal(ss); + } + IEEE80211_UNLOCK(ic); +} + +/* + * Cancel any scan currently going on. + */ +void +ieee80211_cancel_anyscan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) && + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: cancel %s scan\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? + "active" : "passive"); + + /* clear bg scan NOPICK and mark cancel request */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; + /* wake up the scan task */ + scan_signal(ss); + } + IEEE80211_UNLOCK(ic); +} + +/* + * Public access to scan_next for drivers that manage + * scanning themselves (e.g. for firmware-based devices). + */ +void +ieee80211_scan_next(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + /* wake up the scan task */ + IEEE80211_LOCK(ic); + scan_signal(ss); + IEEE80211_UNLOCK(ic); +} + +/* + * Public access to scan_next for drivers that are not able to scan single + * channels (e.g. for firmware-based devices). + */ +void +ieee80211_scan_done(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss; + + IEEE80211_LOCK(ic); + ss = ic->ic_scan; + ss->ss_next = ss->ss_last; /* all channels are complete */ + scan_signal(ss); + IEEE80211_UNLOCK(ic); +} + +/* + * Probe the curent channel, if allowed, while scanning. + * If the channel is not marked passive-only then send + * a probe request immediately. Otherwise mark state and + * listen for beacons on the channel; if we receive something + * then we'll transmit a probe request. + */ +void +ieee80211_probe_curchan(struct ieee80211vap *vap, int force) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + struct ifnet *ifp = vap->iv_ifp; + int i; + + if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) && !force) { + ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN; + return; + } + /* + * Send directed probe requests followed by any + * broadcast probe request. + * XXX remove dependence on ic/vap->iv_bss + */ + for (i = 0; i < ss->ss_nssid; i++) + ieee80211_send_probereq(vap->iv_bss, + vap->iv_myaddr, ifp->if_broadcastaddr, + ifp->if_broadcastaddr, + ss->ss_ssid[i].ssid, ss->ss_ssid[i].len); + if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0) + ieee80211_send_probereq(vap->iv_bss, + vap->iv_myaddr, ifp->if_broadcastaddr, + ifp->if_broadcastaddr, + "", 0); +} + +/* + * Scan curchan. If this is an active scan and the channel + * is not marked passive then send probe request frame(s). + * Arrange for the channel change after maxdwell ticks. + */ +static void +scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) +{ + struct ieee80211vap *vap = ss->ss_vap; + + IEEE80211_LOCK(vap->iv_ic); + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + ieee80211_probe_curchan(vap, 0); + callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, + maxdwell, scan_signal, ss); + IEEE80211_UNLOCK(vap->iv_ic); +} + +static void +scan_signal(void *arg) +{ + struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; + + IEEE80211_LOCK_ASSERT(ss->ss_ic); + + cv_signal(&SCAN_PRIVATE(ss)->ss_scan_cv); +} + +/* + * Handle mindwell requirements completed; initiate a channel + * change to the next channel asap. + */ +static void +scan_mindwell(struct ieee80211_scan_state *ss) +{ + struct ieee80211com *ic = ss->ss_ic; + + IEEE80211_LOCK(ic); + scan_signal(ss); + IEEE80211_UNLOCK(ic); +} + +static void +scan_task(void *arg, int pending) +{ +#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD) + struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; + struct ieee80211vap *vap = ss->ss_vap; + struct ieee80211com *ic = ss->ss_ic; + struct ieee80211_channel *chan; + unsigned long maxdwell, scanend; + int scandone = 0; + + IEEE80211_LOCK(ic); + if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 || + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) { + /* Cancelled before we started */ + goto done; + } + + if (ss->ss_next == ss->ss_last) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no channels to scan\n", __func__); + goto done; + } + + if (vap->iv_opmode == IEEE80211_M_STA && + vap->iv_state == IEEE80211_S_RUN) { + if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { + /* Enable station power save mode */ + ieee80211_sta_pwrsave(vap, 1); + /* + * Use an 1ms delay so the null data frame has a chance + * to go out. + * XXX Should use M_TXCB mechanism to eliminate this. + */ + cv_timedwait(&SCAN_PRIVATE(ss)->ss_scan_cv, + IEEE80211_LOCK_OBJ(ic), hz / 1000); + if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) + goto done; + } + } + + scanend = ticks + SCAN_PRIVATE(ss)->ss_duration; + IEEE80211_UNLOCK(ic); + ic->ic_scan_start(ic); /* notify driver */ + IEEE80211_LOCK(ic); + + for (;;) { + scandone = (ss->ss_next >= ss->ss_last) || + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0; + if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) || + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) || + time_after(ticks + ss->ss_mindwell, scanend)) + break; + + chan = ss->ss_chans[ss->ss_next++]; + + /* + * Watch for truncation due to the scan end time. + */ + if (time_after(ticks + ss->ss_maxdwell, scanend)) + maxdwell = scanend - ticks; + else + maxdwell = ss->ss_maxdwell; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n", + __func__, + ieee80211_chan2ieee(ic, ic->ic_curchan), + channel_type(ic->ic_curchan), + ieee80211_chan2ieee(ic, chan), channel_type(chan), + (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && + (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? + "active" : "passive", + ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell)); + + /* + * Potentially change channel and phy mode. + */ + ic->ic_curchan = chan; + ic->ic_rt = ieee80211_get_ratetable(chan); + IEEE80211_UNLOCK(ic); + /* + * Perform the channel change and scan unlocked so the driver + * may sleep. Once set_channel returns the hardware has + * completed the channel change. + */ + ic->ic_set_channel(ic); + ieee80211_radiotap_chan_change(ic); + + /* + * Scan curchan. Drivers for "intelligent hardware" + * override ic_scan_curchan to tell the device to do + * the work. Otherwise we manage the work outselves; + * sending a probe request (as needed), and arming the + * timeout to switch channels after maxdwell ticks. + * + * scan_curchan should only pause for the time required to + * prepare/initiate the hardware for the scan (if at all), the + * below condvar is used to sleep for the channels dwell time + * and allows it to be signalled for abort. + */ + ic->ic_scan_curchan(ss, maxdwell); + IEEE80211_LOCK(ic); + + SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell; + /* clear mindwell lock and initial channel change flush */ + SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; + + if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT))) + continue; + + /* Wait to be signalled to scan the next channel */ + cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic)); + } + if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) + goto done; + + IEEE80211_UNLOCK(ic); + ic->ic_scan_end(ic); /* notify driver */ + IEEE80211_LOCK(ic); + + /* + * Record scan complete time. Note that we also do + * this when canceled so any background scan will + * not be restarted for a while. + */ + if (scandone) + ic->ic_lastscan = ticks; + /* return to the bss channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + ic->ic_curchan != ic->ic_bsschan) { + ieee80211_setupcurchan(ic, ic->ic_bsschan); + IEEE80211_UNLOCK(ic); + ic->ic_set_channel(ic); + ieee80211_radiotap_chan_change(ic); + IEEE80211_LOCK(ic); + } + /* clear internal flags and any indication of a pick */ + SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; + ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; + + /* + * If not canceled and scan completed, do post-processing. + * If the callback function returns 0, then it wants to + * continue/restart scanning. Unfortunately we needed to + * notify the driver to end the scan above to avoid having + * rx frames alter the scan candidate list. + */ + if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 && + !ss->ss_ops->scan_end(ss, vap) && + (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && + time_before(ticks + ss->ss_mindwell, scanend)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: done, restart " + "[ticks %u, dwell min %lu scanend %lu]\n", + __func__, + ticks, ss->ss_mindwell, scanend); + ss->ss_next = 0; /* reset to begining */ + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + vap->iv_stats.is_scan_active++; + else + vap->iv_stats.is_scan_passive++; + + ss->ss_ops->scan_restart(ss, vap); /* XXX? */ + ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); + IEEE80211_UNLOCK(ic); + return; + } + + /* past here, scandone is ``true'' if not in bg mode */ + if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) + scandone = 1; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n", + __func__, scandone ? "done" : "stopped", + ticks, ss->ss_mindwell, scanend); + + /* + * Clear the SCAN bit first in case frames are + * pending on the station power save queue. If + * we defer this then the dispatch of the frames + * may generate a request to cancel scanning. + */ +done: + ic->ic_flags &= ~IEEE80211_F_SCAN; + /* + * Drop out of power save mode when a scan has + * completed. If this scan was prematurely terminated + * because it is a background scan then don't notify + * the ap; we'll either return to scanning after we + * receive the beacon frame or we'll drop out of power + * save mode because the beacon indicates we have frames + * waiting for us. + */ + if (scandone) { + ieee80211_sta_pwrsave(vap, 0); + if (ss->ss_next >= ss->ss_last) { + ieee80211_notify_scan_done(vap); + ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; + } + } + SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT); + ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST); + IEEE80211_UNLOCK(ic); +#undef ISCAN_REP +} + +#ifdef IEEE80211_DEBUG +static void +dump_country(const uint8_t *ie) +{ + const struct ieee80211_country_ie *cie = + (const struct ieee80211_country_ie *) ie; + int i, nbands, schan, nchan; + + if (cie->len < 3) { + printf(" <bogus country ie, len %d>", cie->len); + return; + } + printf(" country [%c%c%c", cie->cc[0], cie->cc[1], cie->cc[2]); + nbands = (cie->len - 3) / sizeof(cie->band[0]); + for (i = 0; i < nbands; i++) { + schan = cie->band[i].schan; + nchan = cie->band[i].nchan; + if (nchan != 1) + printf(" %u-%u,%u", schan, schan + nchan-1, + cie->band[i].maxtxpwr); + else + printf(" %u,%u", schan, cie->band[i].maxtxpwr); + } + printf("]"); +} + +static void +dump_probe_beacon(uint8_t subtype, int isnew, + const uint8_t mac[IEEE80211_ADDR_LEN], + const struct ieee80211_scanparams *sp, int rssi) +{ + + printf("[%s] %s%s on chan %u (bss chan %u) ", + ether_sprintf(mac), isnew ? "new " : "", + ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], + sp->chan, sp->bchan); + ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]); + printf(" rssi %d\n", rssi); + + if (isnew) { + printf("[%s] caps 0x%x bintval %u erp 0x%x", + ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp); + if (sp->country != NULL) + dump_country(sp->country); + printf("\n"); + } +} +#endif /* IEEE80211_DEBUG */ + +/* + * Process a beacon or probe response frame. + */ +void +ieee80211_add_scan(struct ieee80211vap *vap, + const struct ieee80211_scanparams *sp, + const struct ieee80211_frame *wh, + int subtype, int rssi, int noise) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + /* XXX locking */ + /* + * Frames received during startup are discarded to avoid + * using scan state setup on the initial entry to the timer + * callback. This can occur because the device may enable + * rx prior to our doing the initial channel change in the + * timer routine. + */ + if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD) + return; +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN)) + dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi); +#endif + if (ss->ss_ops != NULL && + ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise)) { + /* + * If we've reached the min dwell time terminate + * the timer so we'll switch to the next channel. + */ + if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 && + time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: chan %3d%c min dwell met (%u > %lu)\n", + __func__, + ieee80211_chan2ieee(ic, ic->ic_curchan), + channel_type(ic->ic_curchan), + ticks, SCAN_PRIVATE(ss)->ss_chanmindwell); + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL; + /* + * NB: trigger at next clock tick or wait for the + * hardware. + */ + ic->ic_scan_mindwell(ss); + } + } +} + +/* + * Timeout/age scan cache entries; called from sta timeout + * timer (XXX should be self-contained). + */ +void +ieee80211_scan_timeout(struct ieee80211com *ic) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + if (ss->ss_ops != NULL) + ss->ss_ops->scan_age(ss); +} + +/* + * Mark a scan cache entry after a successful associate. + */ +void +ieee80211_scan_assoc_success(struct ieee80211vap *vap, const uint8_t mac[]) +{ + struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; + + if (ss->ss_ops != NULL) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, + mac, "%s", __func__); + ss->ss_ops->scan_assoc_success(ss, mac); + } +} + +/* + * Demerit a scan cache entry after failing to associate. + */ +void +ieee80211_scan_assoc_fail(struct ieee80211vap *vap, + const uint8_t mac[], int reason) +{ + struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; + + if (ss->ss_ops != NULL) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, mac, + "%s: reason %u", __func__, reason); + ss->ss_ops->scan_assoc_fail(ss, mac, reason); + } +} + +/* + * Iterate over the contents of the scan cache. + */ +void +ieee80211_scan_iterate(struct ieee80211vap *vap, + ieee80211_scan_iter_func *f, void *arg) +{ + struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; + + if (ss->ss_ops != NULL) + ss->ss_ops->scan_iterate(ss, f, arg); +} + +/* + * Flush the contents of the scan cache. + */ +void +ieee80211_scan_flush(struct ieee80211vap *vap) +{ + struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; + + if (ss->ss_ops != NULL && ss->ss_vap == vap) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", __func__); + ss->ss_ops->scan_flush(ss); + } +} + +/* + * Check the scan cache for an ap/channel to use; if that + * fails then kick off a new scan. + */ +struct ieee80211_channel * +ieee80211_scan_pickchannel(struct ieee80211com *ic, int flags) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK_ASSERT(ic); + + if (ss == NULL || ss->ss_ops == NULL || ss->ss_vap == NULL) { + /* XXX printf? */ + return NULL; + } + if (ss->ss_ops->scan_pickchan == NULL) { + IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, + "%s: scan module does not support picking a channel, " + "opmode %s\n", __func__, ss->ss_vap->iv_opmode); + return NULL; + } + return ss->ss_ops->scan_pickchan(ss, flags); +} diff --git a/freebsd/sys/net80211/ieee80211_scan.h b/freebsd/sys/net80211/ieee80211_scan.h new file mode 100644 index 00000000..6273902d --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_scan.h @@ -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 _NET80211_IEEE80211_SCAN_HH_ +#define _NET80211_IEEE80211_SCAN_HH_ + +/* + * 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_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) */ +}; +MALLOC_DECLARE(M_80211_SCAN); + +/* + * 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 /* _NET80211_IEEE80211_SCAN_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_scan_sta.c b/freebsd/sys/net80211/ieee80211_scan_sta.c new file mode 100644 index 00000000..aa9d67ec --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_scan_sta.c @@ -0,0 +1,1928 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 station scanning support. + */ +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/module.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_input.h> +#include <freebsd/net80211/ieee80211_regdomain.h> +#ifdef IEEE80211_SUPPORT_TDMA +#include <freebsd/net80211/ieee80211_tdma.h> +#endif +#ifdef IEEE80211_SUPPORT_MESH +#include <freebsd/net80211/ieee80211_mesh.h> +#endif + +#include <freebsd/net/bpf.h> + +/* + * Parameters for managing cache entries: + * + * o a station with STA_FAILS_MAX failures is not considered + * when picking a candidate + * o a station that hasn't had an update in STA_PURGE_SCANS + * (background) scans is discarded + * o after STA_FAILS_AGE seconds we clear the failure count + */ +#define STA_FAILS_MAX 2 /* assoc failures before ignored */ +#define STA_FAILS_AGE (2*60) /* time before clearing fails (secs) */ +#define STA_PURGE_SCANS 2 /* age for purging entries (scans) */ + +/* XXX tunable */ +#define STA_RSSI_MIN 8 /* min acceptable rssi */ +#define STA_RSSI_MAX 40 /* max rssi for comparison */ + +struct sta_entry { + struct ieee80211_scan_entry base; + TAILQ_ENTRY(sta_entry) se_list; + LIST_ENTRY(sta_entry) se_hash; + uint8_t se_fails; /* failure to associate count */ + uint8_t se_seen; /* seen during current scan */ + uint8_t se_notseen; /* not seen in previous scans */ + uint8_t se_flags; +#define STA_DEMOTE11B 0x01 /* match w/ demoted 11b chan */ + uint32_t se_avgrssi; /* LPF rssi state */ + unsigned long se_lastupdate; /* time of last update */ + unsigned long se_lastfail; /* time of last failure */ + unsigned long se_lastassoc; /* time of last association */ + u_int se_scangen; /* iterator scan gen# */ + u_int se_countrygen; /* gen# of last cc notify */ +}; + +#define STA_HASHSIZE 32 +/* simple hash is enough for variation of macaddr */ +#define STA_HASH(addr) \ + (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % STA_HASHSIZE) + +#define MAX_IEEE_CHAN 256 /* max acceptable IEEE chan # */ +CTASSERT(MAX_IEEE_CHAN >= 256); + +struct sta_table { + ieee80211_scan_table_lock_t st_lock; /* on scan table */ + TAILQ_HEAD(, sta_entry) st_entry; /* all entries */ + LIST_HEAD(, sta_entry) st_hash[STA_HASHSIZE]; + struct mtx st_scanlock; /* on st_scaniter */ + u_int st_scaniter; /* gen# for iterator */ + u_int st_scangen; /* scan generation # */ + int st_newscan; + /* ap-related state */ + int st_maxrssi[MAX_IEEE_CHAN]; +}; + +static void sta_flush_table(struct sta_table *); +/* + * match_bss returns a bitmask describing if an entry is suitable + * for use. If non-zero the entry was deemed not suitable and it's + * contents explains why. The following flags are or'd to to this + * mask and can be used to figure out why the entry was rejected. + */ +#define MATCH_CHANNEL 0x00001 /* channel mismatch */ +#define MATCH_CAPINFO 0x00002 /* capabilities mismatch, e.g. no ess */ +#define MATCH_PRIVACY 0x00004 /* privacy mismatch */ +#define MATCH_RATE 0x00008 /* rate set mismatch */ +#define MATCH_SSID 0x00010 /* ssid mismatch */ +#define MATCH_BSSID 0x00020 /* bssid mismatch */ +#define MATCH_FAILS 0x00040 /* too many failed auth attempts */ +#define MATCH_NOTSEEN 0x00080 /* not seen in recent scans */ +#define MATCH_RSSI 0x00100 /* rssi deemed too low to use */ +#define MATCH_CC 0x00200 /* country code mismatch */ +#define MATCH_TDMA_NOIE 0x00400 /* no TDMA ie */ +#define MATCH_TDMA_NOTMASTER 0x00800 /* not TDMA master */ +#define MATCH_TDMA_NOSLOT 0x01000 /* all TDMA slots occupied */ +#define MATCH_TDMA_LOCAL 0x02000 /* local address */ +#define MATCH_TDMA_VERSION 0x04000 /* protocol version mismatch */ +#define MATCH_MESH_NOID 0x10000 /* no MESHID ie */ +#define MATCH_MESHID 0x20000 /* meshid mismatch */ +static int match_bss(struct ieee80211vap *, + const struct ieee80211_scan_state *, struct sta_entry *, int); +static void adhoc_age(struct ieee80211_scan_state *); + +static __inline int +isocmp(const uint8_t cc1[], const uint8_t cc2[]) +{ + return (cc1[0] == cc2[0] && cc1[1] == cc2[1]); +} + +/* number of references from net80211 layer */ +static int nrefs = 0; +/* + * Module glue. + */ +IEEE80211_SCANNER_MODULE(sta, 1); + +/* + * Attach prior to any scanning work. + */ +static int +sta_attach(struct ieee80211_scan_state *ss) +{ + struct sta_table *st; + + st = (struct sta_table *) malloc(sizeof(struct sta_table), + M_80211_SCAN, M_NOWAIT | M_ZERO); + if (st == NULL) + return 0; + IEEE80211_SCAN_TABLE_LOCK_INIT(st, "scantable"); + mtx_init(&st->st_scanlock, "scangen", "802.11 scangen", MTX_DEF); + TAILQ_INIT(&st->st_entry); + ss->ss_priv = st; + nrefs++; /* NB: we assume caller locking */ + return 1; +} + +/* + * Cleanup any private state. + */ +static int +sta_detach(struct ieee80211_scan_state *ss) +{ + struct sta_table *st = ss->ss_priv; + + if (st != NULL) { + sta_flush_table(st); + IEEE80211_SCAN_TABLE_LOCK_DESTROY(st); + mtx_destroy(&st->st_scanlock); + free(st, M_80211_SCAN); + KASSERT(nrefs > 0, ("imbalanced attach/detach")); + nrefs--; /* NB: we assume caller locking */ + } + return 1; +} + +/* + * Flush all per-scan state. + */ +static int +sta_flush(struct ieee80211_scan_state *ss) +{ + struct sta_table *st = ss->ss_priv; + + IEEE80211_SCAN_TABLE_LOCK(st); + sta_flush_table(st); + IEEE80211_SCAN_TABLE_UNLOCK(st); + ss->ss_last = 0; + return 0; +} + +/* + * Flush all entries in the scan cache. + */ +static void +sta_flush_table(struct sta_table *st) +{ + struct sta_entry *se, *next; + + TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) { + TAILQ_REMOVE(&st->st_entry, se, se_list); + LIST_REMOVE(se, se_hash); + ieee80211_ies_cleanup(&se->base.se_ies); + free(se, M_80211_SCAN); + } + memset(st->st_maxrssi, 0, sizeof(st->st_maxrssi)); +} + +/* + * Process a beacon or probe response frame; create an + * entry in the scan cache or update any previous entry. + */ +static int +sta_add(struct ieee80211_scan_state *ss, + const struct ieee80211_scanparams *sp, + const struct ieee80211_frame *wh, + int subtype, int rssi, int noise) +{ +#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) +#define PICK1ST(_ss) \ + ((ss->ss_flags & (IEEE80211_SCAN_PICK1ST | IEEE80211_SCAN_GOTPICK)) == \ + IEEE80211_SCAN_PICK1ST) + struct sta_table *st = ss->ss_priv; + const uint8_t *macaddr = wh->i_addr2; + struct ieee80211vap *vap = ss->ss_vap; + struct ieee80211com *ic = vap->iv_ic; + struct sta_entry *se; + struct ieee80211_scan_entry *ise; + int hash; + + hash = STA_HASH(macaddr); + + IEEE80211_SCAN_TABLE_LOCK(st); + LIST_FOREACH(se, &st->st_hash[hash], se_hash) + if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr)) + goto found; + se = (struct sta_entry *) malloc(sizeof(struct sta_entry), + M_80211_SCAN, M_NOWAIT | M_ZERO); + if (se == NULL) { + IEEE80211_SCAN_TABLE_UNLOCK(st); + return 0; + } + se->se_scangen = st->st_scaniter-1; + se->se_avgrssi = IEEE80211_RSSI_DUMMY_MARKER; + IEEE80211_ADDR_COPY(se->base.se_macaddr, macaddr); + TAILQ_INSERT_TAIL(&st->st_entry, se, se_list); + LIST_INSERT_HEAD(&st->st_hash[hash], se, se_hash); +found: + ise = &se->base; + /* XXX ap beaconing multiple ssid w/ same bssid */ + if (sp->ssid[1] != 0 && + (ISPROBE(subtype) || ise->se_ssid[1] == 0)) + memcpy(ise->se_ssid, sp->ssid, 2+sp->ssid[1]); + KASSERT(sp->rates[1] <= IEEE80211_RATE_MAXSIZE, + ("rate set too large: %u", sp->rates[1])); + memcpy(ise->se_rates, sp->rates, 2+sp->rates[1]); + if (sp->xrates != NULL) { + /* XXX validate xrates[1] */ + KASSERT(sp->xrates[1] <= IEEE80211_RATE_MAXSIZE, + ("xrate set too large: %u", sp->xrates[1])); + memcpy(ise->se_xrates, sp->xrates, 2+sp->xrates[1]); + } else + ise->se_xrates[1] = 0; + IEEE80211_ADDR_COPY(ise->se_bssid, wh->i_addr3); + if ((sp->status & IEEE80211_BPARSE_OFFCHAN) == 0) { + /* + * Record rssi data using extended precision LPF filter. + * + * NB: use only on-channel data to insure we get a good + * estimate of the signal we'll see when associated. + */ + IEEE80211_RSSI_LPF(se->se_avgrssi, rssi); + ise->se_rssi = IEEE80211_RSSI_GET(se->se_avgrssi); + ise->se_noise = noise; + } + memcpy(ise->se_tstamp.data, sp->tstamp, sizeof(ise->se_tstamp)); + ise->se_intval = sp->bintval; + ise->se_capinfo = sp->capinfo; +#ifdef IEEE80211_SUPPORT_MESH + if (sp->meshid != NULL && sp->meshid[1] != 0) + memcpy(ise->se_meshid, sp->meshid, 2+sp->meshid[1]); +#endif + /* + * Beware of overriding se_chan for frames seen + * off-channel; this can cause us to attempt an + * association on the wrong channel. + */ + if (sp->status & IEEE80211_BPARSE_OFFCHAN) { + struct ieee80211_channel *c; + /* + * Off-channel, locate the home/bss channel for the sta + * using the value broadcast in the DSPARMS ie. We know + * sp->chan has this value because it's used to calculate + * IEEE80211_BPARSE_OFFCHAN. + */ + c = ieee80211_find_channel_byieee(ic, sp->chan, + ic->ic_curchan->ic_flags); + if (c != NULL) { + ise->se_chan = c; + } else if (ise->se_chan == NULL) { + /* should not happen, pick something */ + ise->se_chan = ic->ic_curchan; + } + } else + ise->se_chan = ic->ic_curchan; + ise->se_fhdwell = sp->fhdwell; + ise->se_fhindex = sp->fhindex; + ise->se_erp = sp->erp; + ise->se_timoff = sp->timoff; + if (sp->tim != NULL) { + const struct ieee80211_tim_ie *tim = + (const struct ieee80211_tim_ie *) sp->tim; + ise->se_dtimperiod = tim->tim_period; + } + if (sp->country != NULL) { + const struct ieee80211_country_ie *cie = + (const struct ieee80211_country_ie *) sp->country; + /* + * If 11d is enabled and we're attempting to join a bss + * that advertises it's country code then compare our + * current settings to what we fetched from the country ie. + * If our country code is unspecified or different then + * dispatch an event to user space that identifies the + * country code so our regdomain config can be changed. + */ + /* XXX only for STA mode? */ + if ((IEEE80211_IS_CHAN_11D(ise->se_chan) || + (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) && + (ic->ic_regdomain.country == CTRY_DEFAULT || + !isocmp(cie->cc, ic->ic_regdomain.isocc))) { + /* only issue one notify event per scan */ + if (se->se_countrygen != st->st_scangen) { + ieee80211_notify_country(vap, ise->se_bssid, + cie->cc); + se->se_countrygen = st->st_scangen; + } + } + ise->se_cc[0] = cie->cc[0]; + ise->se_cc[1] = cie->cc[1]; + } + /* NB: no need to setup ie ptrs; they are not (currently) used */ + (void) ieee80211_ies_init(&ise->se_ies, sp->ies, sp->ies_len); + + /* clear failure count after STA_FAIL_AGE passes */ + if (se->se_fails && (ticks - se->se_lastfail) > STA_FAILS_AGE*hz) { + se->se_fails = 0; + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, macaddr, + "%s: fails %u", __func__, se->se_fails); + } + + se->se_lastupdate = ticks; /* update time */ + se->se_seen = 1; + se->se_notseen = 0; + + KASSERT(sizeof(sp->bchan) == 1, ("bchan size")); + if (rssi > st->st_maxrssi[sp->bchan]) + st->st_maxrssi[sp->bchan] = rssi; + + IEEE80211_SCAN_TABLE_UNLOCK(st); + + /* + * If looking for a quick choice and nothing's + * been found check here. + */ + if (PICK1ST(ss) && match_bss(vap, ss, se, IEEE80211_MSG_SCAN) == 0) + ss->ss_flags |= IEEE80211_SCAN_GOTPICK; + + return 1; +#undef PICK1ST +#undef ISPROBE +} + +/* + * Check if a channel is excluded by user request. + */ +static int +isexcluded(struct ieee80211vap *vap, const struct ieee80211_channel *c) +{ + return (isclr(vap->iv_ic->ic_chan_active, c->ic_ieee) || + (vap->iv_des_chan != IEEE80211_CHAN_ANYC && + c->ic_freq != vap->iv_des_chan->ic_freq)); +} + +static struct ieee80211_channel * +find11gchannel(struct ieee80211com *ic, int i, int freq) +{ + struct ieee80211_channel *c; + int j; + + /* + * The normal ordering in the channel list is b channel + * immediately followed by g so optimize the search for + * this. We'll still do a full search just in case. + */ + for (j = i+1; j < ic->ic_nchans; j++) { + c = &ic->ic_channels[j]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_G(c)) + return c; + } + for (j = 0; j < i; j++) { + c = &ic->ic_channels[j]; + if (c->ic_freq == freq && IEEE80211_IS_CHAN_G(c)) + return c; + } + return NULL; +} + +static const u_int chanflags[IEEE80211_MODE_MAX] = { + [IEEE80211_MODE_AUTO] = IEEE80211_CHAN_B, + [IEEE80211_MODE_11A] = IEEE80211_CHAN_A, + [IEEE80211_MODE_11B] = IEEE80211_CHAN_B, + [IEEE80211_MODE_11G] = IEEE80211_CHAN_G, + [IEEE80211_MODE_FH] = IEEE80211_CHAN_FHSS, + /* check base channel */ + [IEEE80211_MODE_TURBO_A] = IEEE80211_CHAN_A, + [IEEE80211_MODE_TURBO_G] = IEEE80211_CHAN_G, + [IEEE80211_MODE_STURBO_A] = IEEE80211_CHAN_ST, + [IEEE80211_MODE_HALF] = IEEE80211_CHAN_HALF, + [IEEE80211_MODE_QUARTER] = IEEE80211_CHAN_QUARTER, + /* check legacy */ + [IEEE80211_MODE_11NA] = IEEE80211_CHAN_A, + [IEEE80211_MODE_11NG] = IEEE80211_CHAN_G, +}; + +static void +add_channels(struct ieee80211vap *vap, + struct ieee80211_scan_state *ss, + enum ieee80211_phymode mode, const uint16_t freq[], int nfreq) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel *c, *cg; + u_int modeflags; + int i; + + KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); + modeflags = chanflags[mode]; + for (i = 0; i < nfreq; i++) { + if (ss->ss_last >= IEEE80211_SCAN_MAX) + break; + + c = ieee80211_find_channel(ic, freq[i], modeflags); + if (c == NULL || isexcluded(vap, c)) + continue; + if (mode == IEEE80211_MODE_AUTO) { + /* + * XXX special-case 11b/g channels so we select + * the g channel if both are present. + */ + if (IEEE80211_IS_CHAN_B(c) && + (cg = find11gchannel(ic, i, c->ic_freq)) != NULL) + c = cg; + } + ss->ss_chans[ss->ss_last++] = c; + } +#undef N +} + +struct scanlist { + uint16_t mode; + uint16_t count; + const uint16_t *list; +}; + +static int +checktable(const struct scanlist *scan, const struct ieee80211_channel *c) +{ + int i; + + for (; scan->list != NULL; scan++) { + for (i = 0; i < scan->count; i++) + if (scan->list[i] == c->ic_freq) + return 1; + } + return 0; +} + +static int +onscanlist(const struct ieee80211_scan_state *ss, + const struct ieee80211_channel *c) +{ + int i; + + for (i = 0; i < ss->ss_last; i++) + if (ss->ss_chans[i] == c) + return 1; + return 0; +} + +static void +sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, + const struct scanlist table[]) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel *c; + int i; + + for (i = 0; i < ic->ic_nchans; i++) { + if (ss->ss_last >= IEEE80211_SCAN_MAX) + break; + + c = &ic->ic_channels[i]; + /* + * Ignore dynamic turbo channels; we scan them + * in normal mode (i.e. not boosted). Likewise + * for HT channels, they get scanned using + * legacy rates. + */ + if (IEEE80211_IS_CHAN_DTURBO(c) || IEEE80211_IS_CHAN_HT(c)) + continue; + + /* + * If a desired mode was specified, scan only + * channels that satisfy that constraint. + */ + if (vap->iv_des_mode != IEEE80211_MODE_AUTO && + vap->iv_des_mode != ieee80211_chan2mode(c)) + continue; + + /* + * Skip channels excluded by user request. + */ + if (isexcluded(vap, c)) + continue; + + /* + * Add the channel unless it is listed in the + * fixed scan order tables. This insures we + * don't sweep back in channels we filtered out + * above. + */ + if (checktable(table, c)) + continue; + + /* Add channel to scanning list. */ + ss->ss_chans[ss->ss_last++] = c; + } + /* + * Explicitly add any desired channel if: + * - not already on the scan list + * - allowed by any desired mode constraint + * - there is space in the scan list + * This allows the channel to be used when the filtering + * mechanisms would otherwise elide it (e.g HT, turbo). + */ + c = vap->iv_des_chan; + if (c != IEEE80211_CHAN_ANYC && + !onscanlist(ss, c) && + (vap->iv_des_mode == IEEE80211_MODE_AUTO || + vap->iv_des_mode == ieee80211_chan2mode(c)) && + ss->ss_last < IEEE80211_SCAN_MAX) + ss->ss_chans[ss->ss_last++] = c; +} + +static void +makescanlist(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, + const struct scanlist table[]) +{ + const struct scanlist *scan; + enum ieee80211_phymode mode; + + ss->ss_last = 0; + /* + * Use the table of ordered channels to construct the list + * of channels for scanning. Any channels in the ordered + * list not in the master list will be discarded. + */ + for (scan = table; scan->list != NULL; scan++) { + mode = scan->mode; + if (vap->iv_des_mode != IEEE80211_MODE_AUTO) { + /* + * If a desired mode was specified, scan only + * channels that satisfy that constraint. + */ + if (vap->iv_des_mode != mode) { + /* + * The scan table marks 2.4Ghz channels as b + * so if the desired mode is 11g, then use + * the 11b channel list but upgrade the mode. + */ + if (vap->iv_des_mode != IEEE80211_MODE_11G || + mode != IEEE80211_MODE_11B) + continue; + mode = IEEE80211_MODE_11G; /* upgrade */ + } + } else { + /* + * This lets add_channels upgrade an 11b channel + * to 11g if available. + */ + if (mode == IEEE80211_MODE_11B) + mode = IEEE80211_MODE_AUTO; + } +#ifdef IEEE80211_F_XR + /* XR does not operate on turbo channels */ + if ((vap->iv_flags & IEEE80211_F_XR) && + (mode == IEEE80211_MODE_TURBO_A || + mode == IEEE80211_MODE_TURBO_G || + mode == IEEE80211_MODE_STURBO_A)) + continue; +#endif + /* + * Add the list of the channels; any that are not + * in the master channel list will be discarded. + */ + add_channels(vap, ss, mode, scan->list, scan->count); + } + + /* + * Add the channels from the ic that are not present + * in the table. + */ + sweepchannels(ss, vap, table); +} + +static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */ +{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 }; +static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */ +{ 5170, 5190, 5210, 5230 }; +static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */ +{ 2412, 2437, 2462, 2442, 2472 }; +static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */ +{ 5745, 5765, 5785, 5805, 5825 }; +static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */ +{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 }; +static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */ +{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 }; +static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */ +{ 2484 }; +static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */ +{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 }; +static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */ +{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 }; +#ifdef ATH_TURBO_SCAN +static const uint16_t rcl5[] = /* 3 static turbo channels */ +{ 5210, 5250, 5290 }; +static const uint16_t rcl6[] = /* 2 static turbo channels */ +{ 5760, 5800 }; +static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */ +{ 5540, 5580, 5620, 5660 }; +static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */ +{ 2437 }; +static const uint16_t rcl13[] = /* dynamic Turbo channels */ +{ 5200, 5240, 5280, 5765, 5805 }; +#endif /* ATH_TURBO_SCAN */ + +#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a + +static const struct scanlist staScanTable[] = { + { IEEE80211_MODE_11B, X(rcl3) }, + { IEEE80211_MODE_11A, X(rcl1) }, + { IEEE80211_MODE_11A, X(rcl2) }, + { IEEE80211_MODE_11B, X(rcl8) }, + { IEEE80211_MODE_11B, X(rcl9) }, + { IEEE80211_MODE_11A, X(rcl4) }, +#ifdef ATH_TURBO_SCAN + { IEEE80211_MODE_STURBO_A, X(rcl5) }, + { IEEE80211_MODE_STURBO_A, X(rcl6) }, + { IEEE80211_MODE_TURBO_A, X(rcl6x) }, + { IEEE80211_MODE_TURBO_A, X(rcl13) }, +#endif /* ATH_TURBO_SCAN */ + { IEEE80211_MODE_11A, X(rcl7) }, + { IEEE80211_MODE_11B, X(rcl10) }, + { IEEE80211_MODE_11A, X(rcl11) }, +#ifdef ATH_TURBO_SCAN + { IEEE80211_MODE_TURBO_G, X(rcl12) }, +#endif /* ATH_TURBO_SCAN */ + { .list = NULL } +}; + +/* + * Start a station-mode scan by populating the channel list. + */ +static int +sta_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct sta_table *st = ss->ss_priv; + + makescanlist(ss, vap, staScanTable); + + if (ss->ss_mindwell == 0) + ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */ + if (ss->ss_maxdwell == 0) + ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */ + + st->st_scangen++; + st->st_newscan = 1; + + return 0; +} + +/* + * Restart a scan, typically a bg scan but can + * also be a fg scan that came up empty. + */ +static int +sta_restart(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct sta_table *st = ss->ss_priv; + + st->st_newscan = 1; + return 0; +} + +/* + * Cancel an ongoing scan. + */ +static int +sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + return 0; +} + +/* unalligned little endian access */ +#define LE_READ_2(p) \ + ((uint16_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_t *)(p))[1] << 8))) + +/* + * Demote any supplied 11g channel to 11b. There should + * always be an 11b channel but we check anyway... + */ +static struct ieee80211_channel * +demote11b(struct ieee80211vap *vap, struct ieee80211_channel *chan) +{ + struct ieee80211_channel *c; + + if (IEEE80211_IS_CHAN_ANYG(chan) && + vap->iv_des_mode == IEEE80211_MODE_AUTO) { + c = ieee80211_find_channel(vap->iv_ic, chan->ic_freq, + (chan->ic_flags &~ (IEEE80211_CHAN_PUREG | IEEE80211_CHAN_G)) | + IEEE80211_CHAN_B); + if (c != NULL) + chan = c; + } + return chan; +} + +static int +maxrate(const struct ieee80211_scan_entry *se) +{ + const struct ieee80211_ie_htcap *htcap = + (const struct ieee80211_ie_htcap *) se->se_ies.htcap_ie; + int rmax, r, i; + uint16_t caps; + + rmax = 0; + if (htcap != NULL) { + /* + * HT station; inspect supported MCS and then adjust + * rate by channel width. Could also include short GI + * in this if we want to be extra accurate. + */ + /* XXX assumes MCS15 is max */ + for (i = 15; i >= 0 && isclr(htcap->hc_mcsset, i); i--) + ; + if (i >= 0) { + caps = LE_READ_2(&htcap->hc_cap); + /* XXX short/long GI */ + if (caps & IEEE80211_HTCAP_CHWIDTH40) + rmax = ieee80211_htrates[i].ht40_rate_400ns; + else + rmax = ieee80211_htrates[i].ht40_rate_800ns; + } + } + for (i = 0; i < se->se_rates[1]; i++) { + r = se->se_rates[2+i] & IEEE80211_RATE_VAL; + if (r > rmax) + rmax = r; + } + for (i = 0; i < se->se_xrates[1]; i++) { + r = se->se_xrates[2+i] & IEEE80211_RATE_VAL; + if (r > rmax) + rmax = r; + } + return rmax; +} + +/* + * Compare the capabilities of two entries and decide which is + * more desirable (return >0 if a is considered better). Note + * that we assume compatibility/usability has already been checked + * so we don't need to (e.g. validate whether privacy is supported). + * Used to select the best scan candidate for association in a BSS. + */ +static int +sta_compare(const struct sta_entry *a, const struct sta_entry *b) +{ +#define PREFER(_a,_b,_what) do { \ + if (((_a) ^ (_b)) & (_what)) \ + return ((_a) & (_what)) ? 1 : -1; \ +} while (0) + int maxa, maxb; + int8_t rssia, rssib; + int weight; + + /* privacy support */ + PREFER(a->base.se_capinfo, b->base.se_capinfo, + IEEE80211_CAPINFO_PRIVACY); + + /* compare count of previous failures */ + weight = b->se_fails - a->se_fails; + if (abs(weight) > 1) + return weight; + + /* + * Compare rssi. If the two are considered equivalent + * then fallback to other criteria. We threshold the + * comparisons to avoid selecting an ap purely by rssi + * when both values may be good but one ap is otherwise + * more desirable (e.g. an 11b-only ap with stronger + * signal than an 11g ap). + */ + rssia = MIN(a->base.se_rssi, STA_RSSI_MAX); + rssib = MIN(b->base.se_rssi, STA_RSSI_MAX); + if (abs(rssib - rssia) < 5) { + /* best/max rate preferred if signal level close enough XXX */ + maxa = maxrate(&a->base); + maxb = maxrate(&b->base); + if (maxa != maxb) + return maxa - maxb; + /* XXX use freq for channel preference */ + /* for now just prefer 5Ghz band to all other bands */ + PREFER(IEEE80211_IS_CHAN_5GHZ(a->base.se_chan), + IEEE80211_IS_CHAN_5GHZ(b->base.se_chan), 1); + } + /* all things being equal, use signal level */ + return a->base.se_rssi - b->base.se_rssi; +#undef PREFER +} + +/* + * Check rate set suitability and return the best supported rate. + * XXX inspect MCS for HT + */ +static int +check_rate(struct ieee80211vap *vap, const struct ieee80211_channel *chan, + const struct ieee80211_scan_entry *se) +{ +#define RV(v) ((v) & IEEE80211_RATE_VAL) + const struct ieee80211_rateset *srs; + int i, j, nrs, r, okrate, badrate, fixedrate, ucastrate; + const uint8_t *rs; + + okrate = badrate = 0; + + srs = ieee80211_get_suprates(vap->iv_ic, chan); + nrs = se->se_rates[1]; + rs = se->se_rates+2; + /* XXX MCS */ + ucastrate = vap->iv_txparms[ieee80211_chan2mode(chan)].ucastrate; + fixedrate = IEEE80211_FIXED_RATE_NONE; +again: + for (i = 0; i < nrs; i++) { + r = RV(rs[i]); + badrate = r; + /* + * Check any fixed rate is included. + */ + if (r == ucastrate) + fixedrate = r; + /* + * Check against our supported rates. + */ + for (j = 0; j < srs->rs_nrates; j++) + if (r == RV(srs->rs_rates[j])) { + if (r > okrate) /* NB: track max */ + okrate = r; + break; + } + + if (j == srs->rs_nrates && (rs[i] & IEEE80211_RATE_BASIC)) { + /* + * Don't try joining a BSS, if we don't support + * one of its basic rates. + */ + okrate = 0; + goto back; + } + } + if (rs == se->se_rates+2) { + /* scan xrates too; sort of an algol68-style for loop */ + nrs = se->se_xrates[1]; + rs = se->se_xrates+2; + goto again; + } + +back: + if (okrate == 0 || ucastrate != fixedrate) + return badrate | IEEE80211_RATE_BASIC; + else + return RV(okrate); +#undef RV +} + +static __inline int +match_id(const uint8_t *ie, const uint8_t *val, int len) +{ + return (ie[1] == len && memcmp(ie+2, val, len) == 0); +} + +static int +match_ssid(const uint8_t *ie, + int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + int i; + + for (i = 0; i < nssid; i++) { + if (match_id(ie, ssids[i].ssid, ssids[i].len)) + return 1; + } + return 0; +} + +#ifdef IEEE80211_SUPPORT_TDMA +static int +tdma_isfull(const struct ieee80211_tdma_param *tdma) +{ + int slot, slotcnt; + + slotcnt = tdma->tdma_slotcnt; + for (slot = slotcnt-1; slot >= 0; slot--) + if (isclr(tdma->tdma_inuse, slot)) + return 0; + return 1; +} +#endif /* IEEE80211_SUPPORT_TDMA */ + +/* + * Test a scan candidate for suitability/compatibility. + */ +static int +match_bss(struct ieee80211vap *vap, + const struct ieee80211_scan_state *ss, struct sta_entry *se0, + int debug) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_entry *se = &se0->base; + uint8_t rate; + int fail; + + fail = 0; + if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan))) + fail |= MATCH_CHANNEL; + /* + * NB: normally the desired mode is used to construct + * the channel list, but it's possible for the scan + * cache to include entries for stations outside this + * list so we check the desired mode here to weed them + * out. + */ + if (vap->iv_des_mode != IEEE80211_MODE_AUTO && + (se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) != + chanflags[vap->iv_des_mode]) + fail |= MATCH_CHANNEL; + if (vap->iv_opmode == IEEE80211_M_IBSS) { + if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0) + fail |= MATCH_CAPINFO; +#ifdef IEEE80211_SUPPORT_TDMA + } else if (vap->iv_opmode == IEEE80211_M_AHDEMO) { + /* + * Adhoc demo network setup shouldn't really be scanning + * but just in case skip stations operating in IBSS or + * BSS mode. + */ + if (se->se_capinfo & (IEEE80211_CAPINFO_IBSS|IEEE80211_CAPINFO_ESS)) + fail |= MATCH_CAPINFO; + /* + * TDMA operation cannot coexist with a normal 802.11 network; + * skip if IBSS or ESS capabilities are marked and require + * the beacon have a TDMA ie present. + */ + if (vap->iv_caps & IEEE80211_C_TDMA) { + const struct ieee80211_tdma_param *tdma = + (const struct ieee80211_tdma_param *)se->se_ies.tdma_ie; + const struct ieee80211_tdma_state *ts = vap->iv_tdma; + + if (tdma == NULL) + fail |= MATCH_TDMA_NOIE; + else if (tdma->tdma_version != ts->tdma_version) + fail |= MATCH_TDMA_VERSION; + else if (tdma->tdma_slot != 0) + fail |= MATCH_TDMA_NOTMASTER; + else if (tdma_isfull(tdma)) + fail |= MATCH_TDMA_NOSLOT; +#if 0 + else if (ieee80211_local_address(se->se_macaddr)) + fail |= MATCH_TDMA_LOCAL; +#endif + } +#endif /* IEEE80211_SUPPORT_TDMA */ +#ifdef IEEE80211_SUPPORT_MESH + } else if (vap->iv_opmode == IEEE80211_M_MBSS) { + const struct ieee80211_mesh_state *ms = vap->iv_mesh; + /* + * Mesh nodes have IBSS & ESS bits in capinfo turned off + * and two special ie's that must be present. + */ + if (se->se_capinfo & (IEEE80211_CAPINFO_IBSS|IEEE80211_CAPINFO_ESS)) + fail |= MATCH_CAPINFO; + else if (se->se_meshid[0] != IEEE80211_ELEMID_MESHID) + fail |= MATCH_MESH_NOID; + else if (ms->ms_idlen != 0 && + match_id(se->se_meshid, ms->ms_id, ms->ms_idlen)) + fail |= MATCH_MESHID; +#endif + } else { + if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0) + fail |= MATCH_CAPINFO; + /* + * If 11d is enabled and we're attempting to join a bss + * that advertises it's country code then compare our + * current settings to what we fetched from the country ie. + * If our country code is unspecified or different then do + * not attempt to join the bss. We should have already + * dispatched an event to user space that identifies the + * new country code so our regdomain config should match. + */ + if ((IEEE80211_IS_CHAN_11D(se->se_chan) || + (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) && + se->se_cc[0] != 0 && + (ic->ic_regdomain.country == CTRY_DEFAULT || + !isocmp(se->se_cc, ic->ic_regdomain.isocc))) + fail |= MATCH_CC; + } + if (vap->iv_flags & IEEE80211_F_PRIVACY) { + if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) + fail |= MATCH_PRIVACY; + } else { + /* XXX does this mean privacy is supported or required? */ + if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) + fail |= MATCH_PRIVACY; + } + se0->se_flags &= ~STA_DEMOTE11B; + rate = check_rate(vap, se->se_chan, se); + if (rate & IEEE80211_RATE_BASIC) { + fail |= MATCH_RATE; + /* + * An 11b-only ap will give a rate mismatch if there is an + * OFDM fixed tx rate for 11g. Try downgrading the channel + * in the scan list to 11b and retry the rate check. + */ + if (IEEE80211_IS_CHAN_ANYG(se->se_chan)) { + rate = check_rate(vap, demote11b(vap, se->se_chan), se); + if ((rate & IEEE80211_RATE_BASIC) == 0) { + fail &= ~MATCH_RATE; + se0->se_flags |= STA_DEMOTE11B; + } + } + } else if (rate < 2*24) { + /* + * This is an 11b-only ap. Check the desired mode in + * case that needs to be honored (mode 11g filters out + * 11b-only ap's). Otherwise force any 11g channel used + * in scanning to be demoted. + * + * NB: we cheat a bit here by looking at the max rate; + * we could/should check the rates. + */ + if (!(vap->iv_des_mode == IEEE80211_MODE_AUTO || + vap->iv_des_mode == IEEE80211_MODE_11B)) + fail |= MATCH_RATE; + else + se0->se_flags |= STA_DEMOTE11B; + } + if (ss->ss_nssid != 0 && + !match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid)) + fail |= MATCH_SSID; + if ((vap->iv_flags & IEEE80211_F_DESBSSID) && + !IEEE80211_ADDR_EQ(vap->iv_des_bssid, se->se_bssid)) + fail |= MATCH_BSSID; + if (se0->se_fails >= STA_FAILS_MAX) + fail |= MATCH_FAILS; + if (se0->se_notseen >= STA_PURGE_SCANS) + fail |= MATCH_NOTSEEN; + if (se->se_rssi < STA_RSSI_MIN) + fail |= MATCH_RSSI; +#ifdef IEEE80211_DEBUG + if (ieee80211_msg(vap, debug)) { + printf(" %c %s", + fail & MATCH_FAILS ? '=' : + fail & MATCH_NOTSEEN ? '^' : + fail & MATCH_CC ? '$' : +#ifdef IEEE80211_SUPPORT_TDMA + fail & MATCH_TDMA_NOIE ? '&' : + fail & MATCH_TDMA_VERSION ? 'v' : + fail & MATCH_TDMA_NOTMASTER ? 's' : + fail & MATCH_TDMA_NOSLOT ? 'f' : + fail & MATCH_TDMA_LOCAL ? 'l' : +#endif + fail & MATCH_MESH_NOID ? 'm' : + fail ? '-' : '+', ether_sprintf(se->se_macaddr)); + printf(" %s%c", ether_sprintf(se->se_bssid), + fail & MATCH_BSSID ? '!' : ' '); + printf(" %3d%c", ieee80211_chan2ieee(ic, se->se_chan), + fail & MATCH_CHANNEL ? '!' : ' '); + printf(" %+4d%c", se->se_rssi, fail & MATCH_RSSI ? '!' : ' '); + printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, + fail & MATCH_RATE ? '!' : ' '); + printf(" %4s%c", + (se->se_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : + (se->se_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : "", + fail & MATCH_CAPINFO ? '!' : ' '); + printf(" %3s%c ", + (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) ? + "wep" : "no", + fail & MATCH_PRIVACY ? '!' : ' '); + ieee80211_print_essid(se->se_ssid+2, se->se_ssid[1]); + printf("%s\n", fail & (MATCH_SSID | MATCH_MESHID) ? "!" : ""); + } +#endif + return fail; +} + +static void +sta_update_notseen(struct sta_table *st) +{ + struct sta_entry *se; + + IEEE80211_SCAN_TABLE_LOCK(st); + TAILQ_FOREACH(se, &st->st_entry, se_list) { + /* + * If seen the reset and don't bump the count; + * otherwise bump the ``not seen'' count. Note + * that this insures that stations for which we + * see frames while not scanning but not during + * this scan will not be penalized. + */ + if (se->se_seen) + se->se_seen = 0; + else + se->se_notseen++; + } + IEEE80211_SCAN_TABLE_UNLOCK(st); +} + +static void +sta_dec_fails(struct sta_table *st) +{ + struct sta_entry *se; + + IEEE80211_SCAN_TABLE_LOCK(st); + TAILQ_FOREACH(se, &st->st_entry, se_list) + if (se->se_fails) + se->se_fails--; + IEEE80211_SCAN_TABLE_UNLOCK(st); +} + +static struct sta_entry * +select_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, int debug) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se, *selbs = NULL; + + IEEE80211_DPRINTF(vap, debug, " %s\n", + "macaddr bssid chan rssi rate flag wep essid"); + IEEE80211_SCAN_TABLE_LOCK(st); + TAILQ_FOREACH(se, &st->st_entry, se_list) { + ieee80211_ies_expand(&se->base.se_ies); + if (match_bss(vap, ss, se, debug) == 0) { + if (selbs == NULL) + selbs = se; + else if (sta_compare(se, selbs) > 0) + selbs = se; + } + } + IEEE80211_SCAN_TABLE_UNLOCK(st); + + return selbs; +} + +/* + * Pick an ap or ibss network to join or find a channel + * to use to start an ibss network. + */ +static int +sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *selbs; + struct ieee80211_channel *chan; + + KASSERT(vap->iv_opmode == IEEE80211_M_STA, + ("wrong mode %u", vap->iv_opmode)); + + if (st->st_newscan) { + sta_update_notseen(st); + st->st_newscan = 0; + } + if (ss->ss_flags & IEEE80211_SCAN_NOPICK) { + /* + * Manual/background scan, don't select+join the + * bss, just return. The scanning framework will + * handle notification that this has completed. + */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + return 1; + } + /* + * Automatic sequencing; look for a candidate and + * if found join the network. + */ + /* NB: unlocked read should be ok */ + if (TAILQ_FIRST(&st->st_entry) == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no scan candidate\n", __func__); + if (ss->ss_flags & IEEE80211_SCAN_NOJOIN) + return 0; +notfound: + /* + * If nothing suitable was found decrement + * the failure counts so entries will be + * reconsidered the next time around. We + * really want to do this only for sta's + * where we've previously had some success. + */ + sta_dec_fails(st); + st->st_newscan = 1; + return 0; /* restart scan */ + } + selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN); + if (ss->ss_flags & IEEE80211_SCAN_NOJOIN) + return (selbs != NULL); + if (selbs == NULL) + goto notfound; + chan = selbs->base.se_chan; + if (selbs->se_flags & STA_DEMOTE11B) + chan = demote11b(vap, chan); + if (!ieee80211_sta_join(vap, chan, &selbs->base)) + goto notfound; + return 1; /* terminate scan */ +} + +/* + * Lookup an entry in the scan cache. We assume we're + * called from the bottom half or such that we don't need + * to block the bottom half so that it's safe to return + * a reference to an entry w/o holding the lock on the table. + */ +static struct sta_entry * +sta_lookup(struct sta_table *st, const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct sta_entry *se; + int hash = STA_HASH(macaddr); + + IEEE80211_SCAN_TABLE_LOCK(st); + LIST_FOREACH(se, &st->st_hash[hash], se_hash) + if (IEEE80211_ADDR_EQ(se->base.se_macaddr, macaddr)) + break; + IEEE80211_SCAN_TABLE_UNLOCK(st); + + return se; /* NB: unlocked */ +} + +static void +sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni = vap->iv_bss; + struct sta_table *st = ss->ss_priv; + enum ieee80211_phymode mode; + struct sta_entry *se, *selbs; + uint8_t roamRate, curRate, ucastRate; + int8_t roamRssi, curRssi; + + se = sta_lookup(st, ni->ni_macaddr); + if (se == NULL) { + /* XXX something is wrong */ + return; + } + + mode = ieee80211_chan2mode(ic->ic_bsschan); + roamRate = vap->iv_roamparms[mode].rate; + roamRssi = vap->iv_roamparms[mode].rssi; + ucastRate = vap->iv_txparms[mode].ucastrate; + /* NB: the most up to date rssi is in the node, not the scan cache */ + curRssi = ic->ic_node_getrssi(ni); + if (ucastRate == IEEE80211_FIXED_RATE_NONE) { + curRate = ni->ni_txrate; + roamRate &= IEEE80211_RATE_VAL; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, + "%s: currssi %d currate %u roamrssi %d roamrate %u\n", + __func__, curRssi, curRate, roamRssi, roamRate); + } else { + curRate = roamRate; /* NB: insure compare below fails */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, + "%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi); + } + /* + * Check if a new ap should be used and switch. + * XXX deauth current ap + */ + if (curRate < roamRate || curRssi < roamRssi) { + if (time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { + /* + * Scan cache contents are too old; force a scan now + * if possible so we have current state to make a + * decision with. We don't kick off a bg scan if + * we're using dynamic turbo and boosted or if the + * channel is busy. + * XXX force immediate switch on scan complete + */ + if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && + time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)) + ieee80211_bg_scan(vap, 0); + return; + } + se->base.se_rssi = curRssi; + selbs = select_bss(ss, vap, IEEE80211_MSG_ROAM); + if (selbs != NULL && selbs != se) { + struct ieee80211_channel *chan; + + IEEE80211_DPRINTF(vap, + IEEE80211_MSG_ROAM | IEEE80211_MSG_DEBUG, + "%s: ROAM: curRate %u, roamRate %u, " + "curRssi %d, roamRssi %d\n", __func__, + curRate, roamRate, curRssi, roamRssi); + + chan = selbs->base.se_chan; + if (selbs->se_flags & STA_DEMOTE11B) + chan = demote11b(vap, chan); + (void) ieee80211_sta_join(vap, chan, &selbs->base); + } + } +} + +/* + * Age entries in the scan cache. + * XXX also do roaming since it's convenient + */ +static void +sta_age(struct ieee80211_scan_state *ss) +{ + struct ieee80211vap *vap = ss->ss_vap; + + adhoc_age(ss); + /* + * If rate control is enabled check periodically to see if + * we should roam from our current connection to one that + * might be better. This only applies when we're operating + * in sta mode and automatic roaming is set. + * XXX defer if busy + * XXX repeater station + * XXX do when !bgscan? + */ + KASSERT(vap->iv_opmode == IEEE80211_M_STA, + ("wrong mode %u", vap->iv_opmode)); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO && + (vap->iv_flags & IEEE80211_F_BGSCAN) && + vap->iv_state >= IEEE80211_S_RUN) + /* XXX vap is implicit */ + sta_roam_check(ss, vap); +} + +/* + * Iterate over the entries in the scan cache, invoking + * the callback function on each one. + */ +static void +sta_iterate(struct ieee80211_scan_state *ss, + ieee80211_scan_iter_func *f, void *arg) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se; + u_int gen; + + mtx_lock(&st->st_scanlock); + gen = st->st_scaniter++; +restart: + IEEE80211_SCAN_TABLE_LOCK(st); + TAILQ_FOREACH(se, &st->st_entry, se_list) { + if (se->se_scangen != gen) { + se->se_scangen = gen; + /* update public state */ + se->base.se_age = ticks - se->se_lastupdate; + IEEE80211_SCAN_TABLE_UNLOCK(st); + (*f)(arg, &se->base); + goto restart; + } + } + IEEE80211_SCAN_TABLE_UNLOCK(st); + + mtx_unlock(&st->st_scanlock); +} + +static void +sta_assoc_fail(struct ieee80211_scan_state *ss, + const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se; + + se = sta_lookup(st, macaddr); + if (se != NULL) { + se->se_fails++; + se->se_lastfail = ticks; + IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN, + macaddr, "%s: reason %u fails %u", + __func__, reason, se->se_fails); + } +} + +static void +sta_assoc_success(struct ieee80211_scan_state *ss, + const uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se; + + se = sta_lookup(st, macaddr); + if (se != NULL) { +#if 0 + se->se_fails = 0; + IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN, + macaddr, "%s: fails %u", + __func__, se->se_fails); +#endif + se->se_lastassoc = ticks; + } +} + +static const struct ieee80211_scanner sta_default = { + .scan_name = "default", + .scan_attach = sta_attach, + .scan_detach = sta_detach, + .scan_start = sta_start, + .scan_restart = sta_restart, + .scan_cancel = sta_cancel, + .scan_end = sta_pick_bss, + .scan_flush = sta_flush, + .scan_add = sta_add, + .scan_age = sta_age, + .scan_iterate = sta_iterate, + .scan_assoc_fail = sta_assoc_fail, + .scan_assoc_success = sta_assoc_success, +}; +IEEE80211_SCANNER_ALG(sta, IEEE80211_M_STA, sta_default); + +/* + * Adhoc mode-specific support. + */ + +static const uint16_t adhocWorld[] = /* 36, 40, 44, 48 */ +{ 5180, 5200, 5220, 5240 }; +static const uint16_t adhocFcc3[] = /* 36, 40, 44, 48 145, 149, 153, 157, 161, 165 */ +{ 5180, 5200, 5220, 5240, 5725, 5745, 5765, 5785, 5805, 5825 }; +static const uint16_t adhocMkk[] = /* 34, 38, 42, 46 */ +{ 5170, 5190, 5210, 5230 }; +static const uint16_t adhoc11b[] = /* 10, 11 */ +{ 2457, 2462 }; + +static const struct scanlist adhocScanTable[] = { + { IEEE80211_MODE_11B, X(adhoc11b) }, + { IEEE80211_MODE_11A, X(adhocWorld) }, + { IEEE80211_MODE_11A, X(adhocFcc3) }, + { IEEE80211_MODE_11B, X(adhocMkk) }, + { .list = NULL } +}; +#undef X + +/* + * Start an adhoc-mode scan by populating the channel list. + */ +static int +adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct sta_table *st = ss->ss_priv; + + makescanlist(ss, vap, adhocScanTable); + + if (ss->ss_mindwell == 0) + ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */ + if (ss->ss_maxdwell == 0) + ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */ + + st->st_scangen++; + st->st_newscan = 1; + + return 0; +} + +/* + * Select a channel to start an adhoc network on. + * The channel list was populated with appropriate + * channels so select one that looks least occupied. + */ +static struct ieee80211_channel * +adhoc_pick_channel(struct ieee80211_scan_state *ss, int flags) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se; + struct ieee80211_channel *c, *bestchan; + int i, bestrssi, maxrssi; + + bestchan = NULL; + bestrssi = -1; + + IEEE80211_SCAN_TABLE_LOCK(st); + for (i = 0; i < ss->ss_last; i++) { + c = ss->ss_chans[i]; + /* never consider a channel with radar */ + if (IEEE80211_IS_CHAN_RADAR(c)) + continue; + /* skip channels disallowed by regulatory settings */ + if (IEEE80211_IS_CHAN_NOADHOC(c)) + continue; + /* check channel attributes for band compatibility */ + if (flags != 0 && (c->ic_flags & flags) != flags) + continue; + maxrssi = 0; + TAILQ_FOREACH(se, &st->st_entry, se_list) { + if (se->base.se_chan != c) + continue; + if (se->base.se_rssi > maxrssi) + maxrssi = se->base.se_rssi; + } + if (bestchan == NULL || maxrssi < bestrssi) + bestchan = c; + } + IEEE80211_SCAN_TABLE_UNLOCK(st); + + return bestchan; +} + +/* + * Pick an ibss network to join or find a channel + * to use to start an ibss network. + */ +static int +adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *selbs; + struct ieee80211_channel *chan; + + KASSERT(vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_AHDEMO || + vap->iv_opmode == IEEE80211_M_MBSS, + ("wrong opmode %u", vap->iv_opmode)); + + if (st->st_newscan) { + sta_update_notseen(st); + st->st_newscan = 0; + } + if (ss->ss_flags & IEEE80211_SCAN_NOPICK) { + /* + * Manual/background scan, don't select+join the + * bss, just return. The scanning framework will + * handle notification that this has completed. + */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + return 1; + } + /* + * Automatic sequencing; look for a candidate and + * if found join the network. + */ + /* NB: unlocked read should be ok */ + if (TAILQ_FIRST(&st->st_entry) == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no scan candidate\n", __func__); + if (ss->ss_flags & IEEE80211_SCAN_NOJOIN) + return 0; +notfound: + /* NB: never auto-start a tdma network for slot !0 */ +#ifdef IEEE80211_SUPPORT_TDMA + if (vap->iv_des_nssid && + ((vap->iv_caps & IEEE80211_C_TDMA) == 0 || + ieee80211_tdma_getslot(vap) == 0)) { +#else + if (vap->iv_des_nssid) { +#endif + /* + * No existing adhoc network to join and we have + * an ssid; start one up. If no channel was + * specified, try to select a channel. + */ + if (vap->iv_des_chan == IEEE80211_CHAN_ANYC || + IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { + struct ieee80211com *ic = vap->iv_ic; + + chan = adhoc_pick_channel(ss, 0); + if (chan != NULL) + chan = ieee80211_ht_adjust_channel(ic, + chan, vap->iv_flags_ht); + } else + chan = vap->iv_des_chan; + if (chan != NULL) { + ieee80211_create_ibss(vap, chan); + return 1; + } + } + /* + * If nothing suitable was found decrement + * the failure counts so entries will be + * reconsidered the next time around. We + * really want to do this only for sta's + * where we've previously had some success. + */ + sta_dec_fails(st); + st->st_newscan = 1; + return 0; /* restart scan */ + } + selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN); + if (ss->ss_flags & IEEE80211_SCAN_NOJOIN) + return (selbs != NULL); + if (selbs == NULL) + goto notfound; + chan = selbs->base.se_chan; + if (selbs->se_flags & STA_DEMOTE11B) + chan = demote11b(vap, chan); + if (!ieee80211_sta_join(vap, chan, &selbs->base)) + goto notfound; + return 1; /* terminate scan */ +} + +/* + * Age entries in the scan cache. + */ +static void +adhoc_age(struct ieee80211_scan_state *ss) +{ + struct sta_table *st = ss->ss_priv; + struct sta_entry *se, *next; + + IEEE80211_SCAN_TABLE_LOCK(st); + TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) { + if (se->se_notseen > STA_PURGE_SCANS) { + TAILQ_REMOVE(&st->st_entry, se, se_list); + LIST_REMOVE(se, se_hash); + ieee80211_ies_cleanup(&se->base.se_ies); + free(se, M_80211_SCAN); + } + } + IEEE80211_SCAN_TABLE_UNLOCK(st); +} + +static const struct ieee80211_scanner adhoc_default = { + .scan_name = "default", + .scan_attach = sta_attach, + .scan_detach = sta_detach, + .scan_start = adhoc_start, + .scan_restart = sta_restart, + .scan_cancel = sta_cancel, + .scan_end = adhoc_pick_bss, + .scan_flush = sta_flush, + .scan_pickchan = adhoc_pick_channel, + .scan_add = sta_add, + .scan_age = adhoc_age, + .scan_iterate = sta_iterate, + .scan_assoc_fail = sta_assoc_fail, + .scan_assoc_success = sta_assoc_success, +}; +IEEE80211_SCANNER_ALG(ibss, IEEE80211_M_IBSS, adhoc_default); +IEEE80211_SCANNER_ALG(ahdemo, IEEE80211_M_AHDEMO, adhoc_default); + +static void +ap_force_promisc(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + + IEEE80211_LOCK(ic); + /* set interface into promiscuous mode */ + ifp->if_flags |= IFF_PROMISC; + ieee80211_runtask(ic, &ic->ic_promisc_task); + IEEE80211_UNLOCK(ic); +} + +static void +ap_reset_promisc(struct ieee80211com *ic) +{ + IEEE80211_LOCK(ic); + ieee80211_syncifflag_locked(ic, IFF_PROMISC); + IEEE80211_UNLOCK(ic); +} + +static int +ap_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct sta_table *st = ss->ss_priv; + + makescanlist(ss, vap, staScanTable); + + if (ss->ss_mindwell == 0) + ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */ + if (ss->ss_maxdwell == 0) + ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */ + + st->st_scangen++; + st->st_newscan = 1; + + ap_force_promisc(vap->iv_ic); + return 0; +} + +/* + * Cancel an ongoing scan. + */ +static int +ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + ap_reset_promisc(vap->iv_ic); + return 0; +} + +/* + * Pick a quiet channel to use for ap operation. + */ +static struct ieee80211_channel * +ap_pick_channel(struct ieee80211_scan_state *ss, int flags) +{ + struct sta_table *st = ss->ss_priv; + struct ieee80211_channel *bestchan = NULL; + int i; + + /* XXX select channel more intelligently, e.g. channel spread, power */ + /* NB: use scan list order to preserve channel preference */ + for (i = 0; i < ss->ss_last; i++) { + struct ieee80211_channel *chan = ss->ss_chans[i]; + /* + * If the channel is unoccupied the max rssi + * should be zero; just take it. Otherwise + * track the channel with the lowest rssi and + * use that when all channels appear occupied. + */ + if (IEEE80211_IS_CHAN_RADAR(chan)) + continue; + if (IEEE80211_IS_CHAN_NOHOSTAP(chan)) + continue; + /* check channel attributes for band compatibility */ + if (flags != 0 && (chan->ic_flags & flags) != flags) + continue; + KASSERT(sizeof(chan->ic_ieee) == 1, ("ic_chan size")); + /* XXX channel have interference */ + if (st->st_maxrssi[chan->ic_ieee] == 0) { + /* XXX use other considerations */ + return chan; + } + if (bestchan == NULL || + st->st_maxrssi[chan->ic_ieee] < st->st_maxrssi[bestchan->ic_ieee]) + bestchan = chan; + } + return bestchan; +} + +/* + * Pick a quiet channel to use for ap operation. + */ +static int +ap_end(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel *bestchan; + + KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP, + ("wrong opmode %u", vap->iv_opmode)); + bestchan = ap_pick_channel(ss, 0); + if (bestchan == NULL) { + /* no suitable channel, should not happen */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no suitable channel! (should not happen)\n", __func__); + /* XXX print something? */ + return 0; /* restart scan */ + } + /* + * If this is a dynamic turbo channel, start with the unboosted one. + */ + if (IEEE80211_IS_CHAN_TURBO(bestchan)) { + bestchan = ieee80211_find_channel(ic, bestchan->ic_freq, + bestchan->ic_flags & ~IEEE80211_CHAN_TURBO); + if (bestchan == NULL) { + /* should never happen ?? */ + return 0; + } + } + ap_reset_promisc(ic); + if (ss->ss_flags & (IEEE80211_SCAN_NOPICK | IEEE80211_SCAN_NOJOIN)) { + /* + * Manual/background scan, don't select+join the + * bss, just return. The scanning framework will + * handle notification that this has completed. + */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + return 1; + } + ieee80211_create_ibss(vap, + ieee80211_ht_adjust_channel(ic, bestchan, vap->iv_flags_ht)); + return 1; +} + +static const struct ieee80211_scanner ap_default = { + .scan_name = "default", + .scan_attach = sta_attach, + .scan_detach = sta_detach, + .scan_start = ap_start, + .scan_restart = sta_restart, + .scan_cancel = ap_cancel, + .scan_end = ap_end, + .scan_flush = sta_flush, + .scan_pickchan = ap_pick_channel, + .scan_add = sta_add, + .scan_age = adhoc_age, + .scan_iterate = sta_iterate, + .scan_assoc_success = sta_assoc_success, + .scan_assoc_fail = sta_assoc_fail, +}; +IEEE80211_SCANNER_ALG(ap, IEEE80211_M_HOSTAP, ap_default); + +#ifdef IEEE80211_SUPPORT_MESH +/* + * Pick an mbss network to join or find a channel + * to use to start an mbss network. + */ +static int +mesh_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) +{ + struct sta_table *st = ss->ss_priv; + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct sta_entry *selbs; + struct ieee80211_channel *chan; + + KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, + ("wrong opmode %u", vap->iv_opmode)); + + if (st->st_newscan) { + sta_update_notseen(st); + st->st_newscan = 0; + } + if (ss->ss_flags & IEEE80211_SCAN_NOPICK) { + /* + * Manual/background scan, don't select+join the + * bss, just return. The scanning framework will + * handle notification that this has completed. + */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + return 1; + } + /* + * Automatic sequencing; look for a candidate and + * if found join the network. + */ + /* NB: unlocked read should be ok */ + if (TAILQ_FIRST(&st->st_entry) == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no scan candidate\n", __func__); + if (ss->ss_flags & IEEE80211_SCAN_NOJOIN) + return 0; +notfound: + if (ms->ms_idlen != 0) { + /* + * No existing mbss network to join and we have + * a meshid; start one up. If no channel was + * specified, try to select a channel. + */ + if (vap->iv_des_chan == IEEE80211_CHAN_ANYC || + IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { + struct ieee80211com *ic = vap->iv_ic; + + chan = adhoc_pick_channel(ss, 0); + if (chan != NULL) + chan = ieee80211_ht_adjust_channel(ic, + chan, vap->iv_flags_ht); + } else + chan = vap->iv_des_chan; + if (chan != NULL) { + ieee80211_create_ibss(vap, chan); + return 1; + } + } + /* + * If nothing suitable was found decrement + * the failure counts so entries will be + * reconsidered the next time around. We + * really want to do this only for sta's + * where we've previously had some success. + */ + sta_dec_fails(st); + st->st_newscan = 1; + return 0; /* restart scan */ + } + selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN); + if (ss->ss_flags & IEEE80211_SCAN_NOJOIN) + return (selbs != NULL); + if (selbs == NULL) + goto notfound; + chan = selbs->base.se_chan; + if (selbs->se_flags & STA_DEMOTE11B) + chan = demote11b(vap, chan); + if (!ieee80211_sta_join(vap, chan, &selbs->base)) + goto notfound; + return 1; /* terminate scan */ +} + +static const struct ieee80211_scanner mesh_default = { + .scan_name = "default", + .scan_attach = sta_attach, + .scan_detach = sta_detach, + .scan_start = adhoc_start, + .scan_restart = sta_restart, + .scan_cancel = sta_cancel, + .scan_end = mesh_pick_bss, + .scan_flush = sta_flush, + .scan_pickchan = adhoc_pick_channel, + .scan_add = sta_add, + .scan_age = adhoc_age, + .scan_iterate = sta_iterate, + .scan_assoc_fail = sta_assoc_fail, + .scan_assoc_success = sta_assoc_success, +}; +IEEE80211_SCANNER_ALG(mesh, IEEE80211_M_MBSS, mesh_default); +#endif /* IEEE80211_SUPPORT_MESH */ diff --git a/freebsd/sys/net80211/ieee80211_sta.c b/freebsd/sys/net80211/ieee80211_sta.c new file mode 100644 index 00000000..67704469 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_sta.c @@ -0,0 +1,1748 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 Station mode support. + */ +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/errno.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net/bpf.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_sta.h> +#include <freebsd/net80211/ieee80211_input.h> +#ifdef IEEE80211_SUPPORT_SUPERG +#include <freebsd/net80211/ieee80211_superg.h> +#endif +#include <freebsd/net80211/ieee80211_ratectl.h> + +#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) + +static void sta_vattach(struct ieee80211vap *); +static void sta_beacon_miss(struct ieee80211vap *); +static int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int sta_input(struct ieee80211_node *, struct mbuf *, int, int); +static void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *, + int subtype, int rssi, int nf); +static void sta_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype); + +void +ieee80211_sta_attach(struct ieee80211com *ic) +{ + ic->ic_vattach[IEEE80211_M_STA] = sta_vattach; +} + +void +ieee80211_sta_detach(struct ieee80211com *ic) +{ +} + +static void +sta_vdetach(struct ieee80211vap *vap) +{ +} + +static void +sta_vattach(struct ieee80211vap *vap) +{ + vap->iv_newstate = sta_newstate; + vap->iv_input = sta_input; + vap->iv_recv_mgmt = sta_recv_mgmt; + vap->iv_recv_ctl = sta_recv_ctl; + vap->iv_opdetach = sta_vdetach; + vap->iv_bmiss = sta_beacon_miss; +} + +/* + * Handle a beacon miss event. The common code filters out + * spurious events that can happen when scanning and/or before + * reaching RUN state. + */ +static void +sta_beacon_miss(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning")); + KASSERT(vap->iv_state >= IEEE80211_S_RUN, + ("wrong state %s", ieee80211_state_name[vap->iv_state])); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, + "beacon miss, mode %s state %s\n", + ieee80211_opmode_name[vap->iv_opmode], + ieee80211_state_name[vap->iv_state]); + + if (vap->iv_state == IEEE80211_S_CSA) { + /* + * A Channel Switch is pending; assume we missed the + * beacon that would've completed the process and just + * force the switch. If we made a mistake we'll not + * find the AP on the new channel and fall back to a + * normal scan. + */ + ieee80211_csa_completeswitch(ic); + return; + } + if (++vap->iv_bmiss_count < vap->iv_bmiss_max) { + /* + * Send a directed probe req before falling back to a + * scan; if we receive a response ic_bmiss_count will + * be reset. Some cards mistakenly report beacon miss + * so this avoids the expensive scan if the ap is + * still there. + */ + ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr, + vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid, + vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen); + return; + } + + callout_stop(&vap->iv_swbmiss); + vap->iv_bmiss_count = 0; + vap->iv_stats.is_beacon_miss++; + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { +#ifdef IEEE80211_SUPPORT_SUPERG + struct ieee80211com *ic = vap->iv_ic; + + /* + * If we receive a beacon miss interrupt when using + * dynamic turbo, attempt to switch modes before + * reassociating. + */ + if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP)) + ieee80211_dturbo_switch(vap, + ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO); +#endif + /* + * Try to reassociate before scanning for a new ap. + */ + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1); + } else { + /* + * Somebody else is controlling state changes (e.g. + * a user-mode app) don't do anything that would + * confuse them; just drop into scan mode so they'll + * notified of the state change and given control. + */ + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + } +} + +/* + * Handle deauth with reason. We retry only for + * the cases where we might succeed. Otherwise + * we downgrade the ap and scan. + */ +static void +sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason) +{ + switch (reason) { + case IEEE80211_STATUS_SUCCESS: /* NB: MLME assoc */ + case IEEE80211_STATUS_TIMEOUT: + case IEEE80211_REASON_ASSOC_EXPIRE: + case IEEE80211_REASON_NOT_AUTHED: + case IEEE80211_REASON_NOT_ASSOCED: + case IEEE80211_REASON_ASSOC_LEAVE: + case IEEE80211_REASON_ASSOC_NOT_AUTHED: + IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); + break; + default: + ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_check_scan_current(vap); + break; + } +} + +/* + * IEEE80211_M_STA vap state machine handler. + * This routine handles the main states in the 802.11 protocol. + */ +static int +sta_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(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 */ + 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 */ + if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) + callout_stop(&vap->iv_swbmiss); + switch (nstate) { + case IEEE80211_S_INIT: + switch (ostate) { + case IEEE80211_S_SLEEP: + /* XXX wakeup */ + case IEEE80211_S_RUN: + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + ieee80211_sta_leave(ni); + break; + case IEEE80211_S_ASSOC: + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + break; + case IEEE80211_S_SCAN: + ieee80211_cancel_scan(vap); + break; + default: + goto invalid; + } + if (ostate != IEEE80211_S_INIT) { + /* NB: optimize INIT -> INIT case */ + ieee80211_reset_bss(vap); + } + if (vap->iv_auth->ia_detach != NULL) + vap->iv_auth->ia_detach(vap); + break; + case IEEE80211_S_SCAN: + switch (ostate) { + case IEEE80211_S_INIT: + /* + * 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: + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + /* + * These can happen either because of a timeout + * on an assoc/auth response or because of a + * change in state that requires a reset. For + * the former we're called with a non-zero arg + * that is the cause for the failure; pass this + * to the scan code so it can update state. + * Otherwise trigger a new scan unless we're in + * manual roaming mode in which case an application + * must issue an explicit scan request. + */ + if (arg != 0) + ieee80211_scan_assoc_fail(vap, + vap->iv_bss->ni_macaddr, arg); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_check_scan_current(vap); + break; + case IEEE80211_S_RUN: /* beacon miss */ + /* + * Beacon miss. Notify user space and if not + * under control of a user application (roaming + * manual) kick off a scan to re-connect. + */ + ieee80211_sta_leave(ni); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) + ieee80211_check_scan_current(vap); + break; + default: + goto invalid; + } + break; + case IEEE80211_S_AUTH: + switch (ostate) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, 1); + break; + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + switch (arg & 0xff) { + case IEEE80211_FC0_SUBTYPE_AUTH: + /* ??? */ + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + sta_authretry(vap, ni, arg>>8); + break; + } + break; + case IEEE80211_S_RUN: + switch (arg & 0xff) { + case IEEE80211_FC0_SUBTYPE_AUTH: + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + vap->iv_state = ostate; /* stay RUN */ + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + ieee80211_sta_leave(ni); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { + /* try to reauth */ + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, 1); + } + break; + } + break; + default: + goto invalid; + } + break; + case IEEE80211_S_ASSOC: + switch (ostate) { + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); + break; + case IEEE80211_S_SLEEP: /* cannot happen */ + case IEEE80211_S_RUN: + ieee80211_sta_leave(ni); + if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) { + IEEE80211_SEND_MGMT(ni, arg ? + IEEE80211_FC0_SUBTYPE_REASSOC_REQ : + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); + } + break; + default: + goto invalid; + } + break; + case IEEE80211_S_RUN: + if (vap->iv_flags & IEEE80211_F_WPA) { + /* XXX validate prerequisites */ + } + switch (ostate) { + case IEEE80211_S_RUN: + case IEEE80211_S_CSA: + break; + case IEEE80211_S_AUTH: /* when join is done in fw */ + case IEEE80211_S_ASSOC: +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_debug(vap)) { + ieee80211_note(vap, "%s with %s ssid ", + (vap->iv_opmode == IEEE80211_M_STA ? + "associated" : "synchronized"), + 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 + ieee80211_scan_assoc_success(vap, ni->ni_macaddr); + ieee80211_notify_node_join(ni, + arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); + break; + case IEEE80211_S_SLEEP: + ieee80211_sta_pwrsave(vap, 0); + break; + default: + goto invalid; + } + ieee80211_sync_curchan(ic); + if (ostate != IEEE80211_S_RUN && + (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) { + /* + * Start s/w beacon miss timer for devices w/o + * hardware support. We fudge a bit here since + * we're doing this in software. + */ + vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS( + 2 * vap->iv_bmissthreshold * ni->ni_intval); + vap->iv_swbmiss_count = 0; + callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, + ieee80211_swbmiss, vap); + } + /* + * 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 (ic->ic_newassoc != NULL) + ic->ic_newassoc(vap->iv_bss, ostate != IEEE80211_S_RUN); + break; + case IEEE80211_S_CSA: + if (ostate != IEEE80211_S_RUN) + goto invalid; + 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; +} + +/* + * Return non-zero if the frame is an echo of a multicast + * frame sent by ourself. The dir is known to be DSTODS. + */ +static __inline int +isdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh) +{ +#define QWH4(wh) ((const struct ieee80211_qosframe_addr4 *)wh) +#define WH4(wh) ((const struct ieee80211_frame_addr4 *)wh) + const uint8_t *sa; + + KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode")); + + if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) + return 0; + sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4; + return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr); +#undef WH4 +#undef QWH4 +} + +/* + * Return non-zero if the frame is an echo of a multicast + * frame sent by ourself. The dir is known to be FROMDS. + */ +static __inline int +isfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh) +{ + KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode")); + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) + return 0; + return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr); +} + +/* + * 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 0; + } + 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 +sta_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 = 1; /* mbuf need to be tapped. */ + 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; + + 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) { + bssid = wh->i_addr2; + if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { + /* not interested in */ + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, + bssid, 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) && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { + 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 */ + } + /* + * 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) && + (dir == IEEE80211_FC1_DIR_FROMDS || + dir == IEEE80211_FC1_DIR_DSTODS) && + ieee80211_ampdu_reorder(ni, m) != 0) { + m = NULL; + goto out; + } + resubmit_ampdu: + if (dir == IEEE80211_FC1_DIR_FROMDS) { + if ((ifp->if_flags & IFF_SIMPLEX) && + isfromds_mcastecho(vap, wh)) { + /* + * In IEEE802.11 network, multicast + * packets sent from "me" are broadcast + * from the AP; silently discard for + * SIMPLEX interface. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "data", "%s", "multicast echo"); + vap->iv_stats.is_rx_mcastecho++; + goto out; + } + if ((vap->iv_flags & IEEE80211_F_DWDS) && + IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* + * DWDS sta's must drop 3-address mcast frames + * as they will be sent separately as a 4-addr + * frame. Accepting the 3-addr frame will + * confuse the bridge into thinking the sending + * sta is located at the end of WDS link. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, + "3-address data", "%s", "DWDS enabled"); + vap->iv_stats.is_rx_mcastecho++; + goto out; + } + } else if (dir == IEEE80211_FC1_DIR_DSTODS) { + if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_INPUT, wh, "4-address data", + "%s", "DWDS not enabled"); + vap->iv_stats.is_rx_wrongdir++; + goto out; + } + if ((ifp->if_flags & IFF_SIMPLEX) && + isdstods_mcastecho(vap, wh)) { + /* + * In IEEE802.11 network, multicast + * packets sent from "me" are broadcast + * from the AP; silently discard for + * SIMPLEX interface. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, + "4-address data", "%s", "multicast echo"); + vap->iv_stats.is_rx_mcastecho++; + goto out; + } + } else { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, + "data", "incorrect dir 0x%x", dir); + vap->iv_stats.is_rx_wrongdir++; + goto out; + } + + /* + * 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) && 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) { + if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { + /* + * Only shared key auth frames with a challenge + * should be encrypted, discard all others. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], + "%s", "WEP set but not permitted"); + vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ + goto out; + } + if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, "mgt", "%s", "WEP set but PRIVACY off"); + vap->iv_stats.is_rx_noprivacy++; + goto out; + } + hdrspace = ieee80211_hdrspace(ic, wh); + key = ieee80211_crypto_decap(ni, m, hdrspace); + if (key == NULL) { + /* NB: stats+msgs handled in crypto_decap */ + goto out; + } + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + } + 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, NULL, "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 +sta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh, + int rssi, int nf, uint16_t seq, uint16_t status) +{ + struct ieee80211vap *vap = ni->ni_vap; + + if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "open auth", + "bad sta auth mode %u", ni->ni_authmode); + vap->iv_stats.is_rx_bad_auth++; /* XXX */ + return; + } + if (vap->iv_state != IEEE80211_S_AUTH || + seq != IEEE80211_AUTH_OPEN_RESPONSE) { + vap->iv_stats.is_rx_bad_auth++; + return; + } + if (status != 0) { + IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ni, "open auth failed (reason %d)", status); + vap->iv_stats.is_rx_auth_fail++; + vap->iv_stats.is_rx_authfail_code = status; + ieee80211_new_state(vap, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); + } else + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); +} + +static void +sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh, + uint8_t *frm, uint8_t *efrm, int rssi, int nf, + uint16_t seq, uint16_t status) +{ + struct ieee80211vap *vap = ni->ni_vap; + uint8_t *challenge; + int estatus; + + /* + * NB: this can happen as we allow pre-shared key + * authentication to be enabled w/o wep being turned + * on so that configuration of these can be done + * in any order. It may be better to enforce the + * ordering in which case this check would just be + * for sanity/consistency. + */ + if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "%s", " PRIVACY is disabled"); + estatus = IEEE80211_STATUS_ALG; + goto bad; + } + /* + * Pre-shared key authentication is evil; accept + * it only if explicitly configured (it is supported + * mainly for compatibility with clients like OS X). + */ + if (ni->ni_authmode != IEEE80211_AUTH_AUTO && + ni->ni_authmode != IEEE80211_AUTH_SHARED) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad sta auth mode %u", ni->ni_authmode); + vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ + estatus = IEEE80211_STATUS_ALG; + goto bad; + } + + challenge = NULL; + if (frm + 1 < efrm) { + if ((frm[1] + 2) > (efrm - frm)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "ie %d/%d too long", + frm[0], (frm[1] + 2) - (efrm - frm)); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (*frm == IEEE80211_ELEMID_CHALLENGE) + challenge = frm; + frm += frm[1] + 2; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_CHALLENGE: + case IEEE80211_AUTH_SHARED_RESPONSE: + if (challenge == NULL) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "%s", "no challenge"); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (challenge[1] != IEEE80211_CHALLENGE_LEN) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH, + ni->ni_macaddr, "shared key auth", + "bad challenge len %d", challenge[1]); + vap->iv_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + default: + break; + } + if (vap->iv_state != IEEE80211_S_AUTH) + return; + switch (seq) { + case IEEE80211_AUTH_SHARED_PASS: + if (ni->ni_challenge != NULL) { + free(ni->ni_challenge, M_80211_NODE); + ni->ni_challenge = NULL; + } + if (status != 0) { + IEEE80211_NOTE_FRAME(vap, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh, + "shared key auth failed (reason %d)", status); + vap->iv_stats.is_rx_auth_fail++; + vap->iv_stats.is_rx_authfail_code = status; + return; + } + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); + break; + case IEEE80211_AUTH_SHARED_CHALLENGE: + if (!ieee80211_alloc_challenge(ni)) + return; + /* XXX could optimize by passing recvd challenge */ + memcpy(ni->ni_challenge, &challenge[2], challenge[1]); + IEEE80211_SEND_MGMT(ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + break; + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH, + wh, "shared key auth", "bad seq %d", seq); + vap->iv_stats.is_rx_bad_auth++; + return; + } + return; +bad: + /* + * Kick the state machine. This short-circuits + * using the mgt frame timeout to trigger the + * state transition. + */ + if (vap->iv_state == IEEE80211_S_AUTH) + ieee80211_new_state(vap, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); +} + +static int +ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm, + const struct ieee80211_frame *wh) +{ +#define MS(_v, _f) (((_v) & _f) >> _f##_S) + struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme; + u_int len = frm[1], qosinfo; + int i; + + if (len < sizeof(struct ieee80211_wme_param)-2) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME, + wh, "WME", "too short, len %u", len); + return -1; + } + qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)]; + qosinfo &= WME_QOSINFO_COUNT; + /* XXX do proper check for wraparound */ + if (qosinfo == wme->wme_wmeChanParams.cap_info) + return 0; + frm += __offsetof(struct ieee80211_wme_param, params_acParams); + for (i = 0; i < WME_NUM_AC; i++) { + struct wmeParams *wmep = + &wme->wme_wmeChanParams.cap_wmeParams[i]; + /* NB: ACI not used */ + wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM); + wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN); + wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN); + wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX); + wmep->wmep_txopLimit = LE_READ_2(frm+2); + frm += 4; + } + wme->wme_wmeChanParams.cap_info = qosinfo; + return 1; +#undef MS +} + +/* + * Process 11h Channel Switch Announcement (CSA) ie. If this + * is the first CSA then initiate the switch. Otherwise we + * track state and trigger completion and/or cancel of the switch. + * XXX should be public for IBSS use + */ +static void +ieee80211_parse_csaparams(struct ieee80211vap *vap, uint8_t *frm, + const struct ieee80211_frame *wh) +{ + struct ieee80211com *ic = vap->iv_ic; + const struct ieee80211_csa_ie *csa = + (const struct ieee80211_csa_ie *) frm; + + KASSERT(vap->iv_state >= IEEE80211_S_RUN, + ("state %s", ieee80211_state_name[vap->iv_state])); + + if (csa->csa_mode > 1) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, + wh, "CSA", "invalid mode %u", csa->csa_mode); + return; + } + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) { + /* + * Convert the channel number to a channel reference. We + * try first to preserve turbo attribute of the current + * channel then fallback. Note this will not work if the + * CSA specifies a channel that requires a band switch (e.g. + * 11a => 11g). This is intentional as 11h is defined only + * for 5GHz/11a and because the switch does not involve a + * reassociation, protocol state (capabilities, negotated + * rates, etc) may/will be wrong. + */ + struct ieee80211_channel *c = + ieee80211_find_channel_byieee(ic, csa->csa_newchan, + (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_ALLTURBO)); + if (c == NULL) { + c = ieee80211_find_channel_byieee(ic, + csa->csa_newchan, + (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_ALL)); + if (c == NULL) { + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, + wh, "CSA", "invalid channel %u", + csa->csa_newchan); + goto done; + } + } +#if IEEE80211_CSA_COUNT_MIN > 0 + if (csa->csa_count < IEEE80211_CSA_COUNT_MIN) { + /* + * Require at least IEEE80211_CSA_COUNT_MIN count to + * reduce the risk of being redirected by a fabricated + * CSA. If a valid CSA is dropped we'll still get a + * beacon miss when the AP leaves the channel so we'll + * eventually follow to the new channel. + * + * NOTE: this violates the 11h spec that states that + * count may be any value and if 0 then a switch + * should happen asap. + */ + IEEE80211_DISCARD_IE(vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, + wh, "CSA", "count %u too small, must be >= %u", + csa->csa_count, IEEE80211_CSA_COUNT_MIN); + goto done; + } +#endif + ieee80211_csa_startswitch(ic, c, csa->csa_mode, csa->csa_count); + } else { + /* + * Validate this ie against the initial CSA. We require + * mode and channel not change and the count must be + * monotonically decreasing. This may be pointless and + * canceling the switch as a result may be too paranoid but + * in the worst case if we drop out of CSA because of this + * and the AP does move then we'll just end up taking a + * beacon miss and scan to find the AP. + * + * XXX may want <= on count as we also process ProbeResp + * frames and those may come in w/ the same count as the + * previous beacon; but doing so leaves us open to a stuck + * count until we add a dead-man timer + */ + if (!(csa->csa_count < ic->ic_csa_count && + csa->csa_mode == ic->ic_csa_mode && + csa->csa_newchan == ieee80211_chan2ieee(ic, ic->ic_csa_newchan))) { + IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_DOTH, wh, + "CSA ie mismatch, initial ie <%d,%d,%d>, " + "this ie <%d,%d,%d>", ic->ic_csa_mode, + ic->ic_csa_newchan, ic->ic_csa_count, + csa->csa_mode, csa->csa_newchan, csa->csa_count); + ieee80211_csa_cancelswitch(ic); + } else { + if (csa->csa_count <= 1) + ieee80211_csa_completeswitch(ic); + else + ic->ic_csa_count = csa->csa_count; + } + } +done: + IEEE80211_UNLOCK(ic); +} + +/* + * Return non-zero if a background scan may be continued: + * o bg scan is active + * o no channel switch is pending + * o there has not been any traffic recently + * + * Note we do not check if there is an administrative enable; + * this is only done to start the scan. We assume that any + * change in state will be accompanied by a request to cancel + * active scans which will otherwise cause this test to fail. + */ +static __inline int +contbgscan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) && + (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && + vap->iv_state == IEEE80211_S_RUN && /* XXX? */ + time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); +} + +/* + * Return non-zero if a backgrond scan may be started: + * o bg scanning is administratively enabled + * o no channel switch is pending + * o we are not boosted on a dynamic turbo channel + * o there has not been a scan recently + * o there has not been any traffic recently + */ +static __inline int +startbgscan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + return ((vap->iv_flags & IEEE80211_F_BGSCAN) && + (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 && +#ifdef IEEE80211_SUPPORT_SUPERG + !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) && +#endif + time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) && + time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)); +} + +static void +sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, + int subtype, int rssi, int nf) +{ +#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) +#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + uint8_t *frm, *efrm; + uint8_t *rates, *xrates, *wme, *htcap, *htinfo; + uint8_t rate; + + 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: + * o when scanning, or + * o station mode when associated (to collect state + * updates such as 802.11g slot time) + * Frames otherwise received are discarded. + */ + if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + /* XXX probe response in sta mode when !scanning? */ + 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); + /* + * When operating in station mode, check for state updates. + * Be careful to ignore beacons received while doing a + * background scan. We consider only 11g/WMM stuff right now. + */ + if (ni->ni_associd != 0 && + ((ic->ic_flags & IEEE80211_F_SCAN) == 0 || + IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) { + /* record tsf of last beacon */ + memcpy(ni->ni_tstamp.data, scan.tstamp, + sizeof(ni->ni_tstamp)); + /* count beacon frame for s/w bmiss handling */ + vap->iv_swbmiss_count++; + vap->iv_bmiss_count = 0; + if (ni->ni_erp != scan.erp) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, + wh->i_addr2, + "erp change: was 0x%x, now 0x%x", + ni->ni_erp, scan.erp); + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && + (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) + ic->ic_flags |= IEEE80211_F_USEPROT; + else + ic->ic_flags &= ~IEEE80211_F_USEPROT; + ni->ni_erp = scan.erp; + /* XXX statistic */ + /* XXX driver notification */ + } + if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, + wh->i_addr2, + "capabilities change: was 0x%x, now 0x%x", + ni->ni_capinfo, scan.capinfo); + /* + * NB: we assume short preamble doesn't + * change dynamically + */ + ieee80211_set_shortslottime(ic, + IEEE80211_IS_CHAN_A(ic->ic_bsschan) || + (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME) + | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME); + /* XXX statistic */ + } + if (scan.wme != NULL && + (ni->ni_flags & IEEE80211_NODE_QOS) && + ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0) + ieee80211_wme_updateparams(vap); +#ifdef IEEE80211_SUPPORT_SUPERG + if (scan.ath != NULL) + ieee80211_parse_athparams(ni, scan.ath, wh); +#endif + if (scan.htcap != NULL && scan.htinfo != NULL && + (vap->iv_flags_ht & IEEE80211_FHT_HT)) { + ieee80211_ht_updateparams(ni, + scan.htcap, scan.htinfo); + /* XXX state changes? */ + } + if (scan.tim != NULL) { + struct ieee80211_tim_ie *tim = + (struct ieee80211_tim_ie *) scan.tim; +#if 0 + int aid = IEEE80211_AID(ni->ni_associd); + int ix = aid / NBBY; + int min = tim->tim_bitctl &~ 1; + int max = tim->tim_len + min - 4; + if ((tim->tim_bitctl&1) || + (min <= ix && ix <= max && + isset(tim->tim_bitmap - min, aid))) { + /* + * XXX Do not let bg scan kick off + * we are expecting data. + */ + ic->ic_lastdata = ticks; + ieee80211_sta_pwrsave(vap, 0); + } +#endif + ni->ni_dtim_count = tim->tim_count; + ni->ni_dtim_period = tim->tim_period; + } + if (scan.csa != NULL && + (vap->iv_flags & IEEE80211_F_DOTH)) + ieee80211_parse_csaparams(vap, scan.csa, wh); + else if (ic->ic_flags & IEEE80211_F_CSAPENDING) { + /* + * No CSA ie or 11h disabled, but a channel + * switch is pending; drop out so we aren't + * stuck in CSA state. If the AP really is + * moving we'll get a beacon miss and scan. + */ + IEEE80211_LOCK(ic); + ieee80211_csa_cancelswitch(ic); + IEEE80211_UNLOCK(ic); + } + /* + * If scanning, pass the info to the scan module. + * Otherwise, check if it's the right time to do + * a background scan. Background scanning must + * be enabled and we must not be operating in the + * turbo phase of dynamic turbo mode. Then, + * it's been a while since the last background + * scan and if no data frames have come through + * recently, kick off a scan. Note that this + * is the mechanism by which a background scan + * is started _and_ continued each time we + * return on-channel to receive a beacon from + * our ap. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) { + ieee80211_add_scan(vap, &scan, wh, + subtype, rssi, nf); + } else if (contbgscan(vap)) { + ieee80211_bg_scan(vap, 0); + } else if (startbgscan(vap)) { + vap->iv_stats.is_scan_bg++; +#if 0 + /* wakeup if we are sleeing */ + ieee80211_set_pwrsave(vap, 0); +#endif + ieee80211_bg_scan(vap, 0); + } + return; + } + /* + * 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; + } + break; + } + + case IEEE80211_FC0_SUBTYPE_AUTH: { + uint16_t algo, seq, status; + /* + * auth frame format + * [2] algorithm + * [2] sequence + * [2] status + * [tlv*] challenge + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); + algo = le16toh(*(uint16_t *)frm); + seq = le16toh(*(uint16_t *)(frm + 2)); + status = le16toh(*(uint16_t *)(frm + 4)); + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2, + "recv auth frame with algorithm %d seq %d", algo, seq); + + if (vap->iv_flags & IEEE80211_F_COUNTERM) { + IEEE80211_DISCARD(vap, + IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, + wh, "auth", "%s", "TKIP countermeasures enabled"); + vap->iv_stats.is_rx_auth_countermeasures++; + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + ieee80211_send_error(ni, wh->i_addr2, + IEEE80211_FC0_SUBTYPE_AUTH, + IEEE80211_REASON_MIC_FAILURE); + } + return; + } + if (algo == IEEE80211_AUTH_ALG_SHARED) + sta_auth_shared(ni, wh, frm + 6, efrm, rssi, nf, + seq, status); + else if (algo == IEEE80211_AUTH_ALG_OPEN) + sta_auth_open(ni, wh, rssi, nf, seq, status); + else { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "auth", "unsupported alg %d", algo); + vap->iv_stats.is_rx_auth_unsupported++; + return; + } + break; + } + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { + uint16_t capinfo, associd; + uint16_t status; + + if (vap->iv_state != IEEE80211_S_ASSOC) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + + /* + * asresp frame format + * [2] capability information + * [2] status + * [2] association ID + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] WME + * [tlv] HT capabilities + * [tlv] HT info + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return); + ni = vap->iv_bss; + capinfo = le16toh(*(uint16_t *)frm); + frm += 2; + status = le16toh(*(uint16_t *)frm); + frm += 2; + if (status != 0) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, + wh->i_addr2, "%sassoc failed (reason %d)", + ISREASSOC(subtype) ? "re" : "", status); + vap->iv_stats.is_rx_auth_fail++; /* XXX */ + return; + } + associd = le16toh(*(uint16_t *)frm); + frm += 2; + + rates = xrates = wme = htcap = htinfo = NULL; + while (efrm - frm > 1) { + IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); + switch (*frm) { + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + case IEEE80211_ELEMID_HTCAP: + htcap = frm; + break; + case IEEE80211_ELEMID_HTINFO: + htinfo = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswmeoui(frm)) + wme = frm; + 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 (htcap == NULL) + htcap = frm; + } else if (ishtinfooui(frm)) { + if (htinfo == NULL) + htcap = frm; + } + } + /* XXX Atheros OUI support */ + 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); + rate = ieee80211_setup_rates(ni, rates, xrates, + IEEE80211_F_JOIN | + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (rate & IEEE80211_RATE_BASIC) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC, + wh->i_addr2, + "%sassoc failed (rate set mismatch)", + ISREASSOC(subtype) ? "re" : ""); + vap->iv_stats.is_rx_assoc_norate++; + ieee80211_new_state(vap, IEEE80211_S_SCAN, + IEEE80211_SCAN_FAIL_STATUS); + return; + } + + ni->ni_capinfo = capinfo; + ni->ni_associd = associd; + if (ni->ni_jointime == 0) + ni->ni_jointime = time_uptime; + if (wme != NULL && + ieee80211_parse_wmeparams(vap, wme, wh) >= 0) { + ni->ni_flags |= IEEE80211_NODE_QOS; + ieee80211_wme_updateparams(vap); + } else + ni->ni_flags &= ~IEEE80211_NODE_QOS; + /* + * Setup HT state according to the negotiation. + * + * NB: shouldn't need to check if HT use is enabled but some + * ap's send back HT ie's even when we don't indicate we + * are HT capable in our AssocReq. + */ + if (htcap != NULL && htinfo != NULL && + (vap->iv_flags_ht & IEEE80211_FHT_HT)) { + ieee80211_ht_node_init(ni); + ieee80211_ht_updateparams(ni, htcap, htinfo); + ieee80211_setup_htrates(ni, htcap, + IEEE80211_F_JOIN | IEEE80211_F_DOBRS); + ieee80211_setup_basic_htrates(ni, htinfo); + ieee80211_node_setuptxparms(ni); + ieee80211_ratectl_node_init(ni); + } else { +#ifdef IEEE80211_SUPPORT_SUPERG + if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_ATH)) + ieee80211_ff_node_init(ni); +#endif + } + /* + * Configure state now that we are associated. + * + * XXX may need different/additional driver callbacks? + */ + if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } else { + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + ic->ic_flags |= IEEE80211_F_USEBARKER; + } + ieee80211_set_shortslottime(ic, + IEEE80211_IS_CHAN_A(ic->ic_curchan) || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + /* + * Honor ERP protection. + * + * NB: ni_erp should zero for non-11g operation. + */ + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && + (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION)) + ic->ic_flags |= IEEE80211_F_USEPROT; + else + ic->ic_flags &= ~IEEE80211_F_USEPROT; + IEEE80211_NOTE_MAC(vap, + IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2, + "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s", + ISREASSOC(subtype) ? "re" : "", + IEEE80211_NODE_AID(ni), + ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", + ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", + ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "", + ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", + ni->ni_flags & IEEE80211_NODE_HT ? + (ni->ni_chw == 40 ? ", HT40" : ", HT20") : "", + ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", + ni->ni_flags & IEEE80211_NODE_MIMO_RTS ? " (+SMPS-DYN)" : + ni->ni_flags & IEEE80211_NODE_MIMO_PS ? " (+SMPS)" : "", + ni->ni_flags & IEEE80211_NODE_RIFS ? " (+RIFS)" : "", + IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ? + ", fast-frames" : "", + IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ? + ", turbo" : "" + ); + ieee80211_new_state(vap, IEEE80211_S_RUN, subtype); + break; + } + + case IEEE80211_FC0_SUBTYPE_DEAUTH: { + uint16_t reason; + + if (vap->iv_state == IEEE80211_S_SCAN) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { + /* NB: can happen when in promiscuous mode */ + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + + /* + * deauth frame format + * [2] reason + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); + reason = le16toh(*(uint16_t *)frm); + + vap->iv_stats.is_rx_deauth++; + vap->iv_stats.is_rx_deauth_code = reason; + IEEE80211_NODE_STAT(ni, rx_deauth); + + IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, + "recv deauthenticate (reason %d)", reason); + ieee80211_new_state(vap, IEEE80211_S_AUTH, + (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH); + break; + } + + case IEEE80211_FC0_SUBTYPE_DISASSOC: { + uint16_t reason; + + if (vap->iv_state != IEEE80211_S_RUN && + vap->iv_state != IEEE80211_S_ASSOC && + vap->iv_state != IEEE80211_S_AUTH) { + vap->iv_stats.is_rx_mgtdiscard++; + return; + } + if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) { + /* NB: can happen when in promiscuous mode */ + vap->iv_stats.is_rx_mgtdiscard++; + break; + } + + /* + * disassoc frame format + * [2] reason + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return); + reason = le16toh(*(uint16_t *)frm); + + vap->iv_stats.is_rx_disassoc++; + vap->iv_stats.is_rx_disassoc_code = reason; + IEEE80211_NODE_STAT(ni, rx_disassoc); + + IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, + "recv disassociate (reason %d)", reason); + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); + break; + } + + case IEEE80211_FC0_SUBTYPE_ACTION: + if (vap->iv_state == IEEE80211_S_RUN) { + if (ieee80211_parse_action(ni, m0) == 0) + ic->ic_recv_action(ni, wh, frm, efrm); + } else + vap->iv_stats.is_rx_mgtdiscard++; + break; + + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + 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 ISREASSOC +#undef ISPROBE +} + +static void +sta_recv_ctl(struct ieee80211_node *ni, struct mbuf *m0, int subtype) +{ +} diff --git a/freebsd/sys/net80211/ieee80211_sta.h b/freebsd/sys/net80211/ieee80211_sta.h new file mode 100644 index 00000000..43316f5b --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_sta.h @@ -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 _NET80211_IEEE80211_STA_HH_ +#define _NET80211_IEEE80211_STA_HH_ + +/* + * Station-mode implementation definitions. + */ +void ieee80211_sta_attach(struct ieee80211com *); +void ieee80211_sta_detach(struct ieee80211com *); +void ieee80211_sta_vattach(struct ieee80211vap *); +#endif /* !_NET80211_IEEE80211_STA_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_superg.c b/freebsd/sys/net80211/ieee80211_superg.c new file mode 100644 index 00000000..dd886e41 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_superg.c @@ -0,0 +1,902 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/endian.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/bpf.h> +#include <freebsd/net/ethernet.h> +#include <freebsd/net/if.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/if_media.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_input.h> +#include <freebsd/net80211/ieee80211_phy.h> +#include <freebsd/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); diff --git a/freebsd/sys/net80211/ieee80211_superg.h b/freebsd/sys/net80211/ieee80211_superg.h new file mode 100644 index 00000000..bda45dda --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_superg.h @@ -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 _NET80211_IEEE80211_SUPERG_HH_ +#define _NET80211_IEEE80211_SUPERG_HH_ + +/* + * 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 /* _NET80211_IEEE80211_SUPERG_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_tdma.c b/freebsd/sys/net80211/ieee80211_tdma.c new file mode 100644 index 00000000..c41ca491 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_tdma.c @@ -0,0 +1,822 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 TDMA mode support. + */ +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_tdma.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/errno.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net/bpf.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_tdma.h> +#include <freebsd/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]); + + callout_stop(&vap->iv_swbmiss); + + 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); diff --git a/freebsd/sys/net80211/ieee80211_tdma.h b/freebsd/sys/net80211/ieee80211_tdma.h new file mode 100644 index 00000000..989d6417 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_tdma.h @@ -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 _NET80211_IEEE80211_TDMA_HH_ +#define _NET80211_IEEE80211_TDMA_HH_ + +/* + * 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 /* !_NET80211_IEEE80211_TDMA_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_var.h b/freebsd/sys/net80211/ieee80211_var.h new file mode 100644 index 00000000..2ab54b78 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_var.h @@ -0,0 +1,916 @@ +/*- + * 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 _NET80211_IEEE80211_VAR_HH_ +#define _NET80211_IEEE80211_VAR_HH_ + +/* + * Definitions for IEEE 802.11 drivers. + */ +/* NB: portability glue must go first */ +#if defined(__NetBSD__) +#include <freebsd/net80211/ieee80211_netbsd.h> +#elif defined(__FreeBSD__) +#include <freebsd/net80211/ieee80211_freebsd.h> +#elif defined(__linux__) +#include <freebsd/net80211/ieee80211_linux.h> +#else +#error "No support for your operating system!" +#endif + +#include <freebsd/net80211/_ieee80211.h> +#include <freebsd/net80211/ieee80211.h> +#include <freebsd/net80211/ieee80211_ageq.h> +#include <freebsd/net80211/ieee80211_crypto.h> +#include <freebsd/net80211/ieee80211_dfs.h> +#include <freebsd/net80211/ieee80211_ioctl.h> /* for ieee80211_stats */ +#include <freebsd/net80211/ieee80211_phy.h> +#include <freebsd/net80211/ieee80211_power.h> +#include <freebsd/net80211/ieee80211_node.h> +#include <freebsd/net80211/ieee80211_proto.h> +#include <freebsd/net80211/ieee80211_radiotap.h> +#include <freebsd/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[]; /* 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 */ + int ic_lastdata; /* time of last data frame */ + int 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 */ + int ic_lastnonerp; /* last time non-ERP sta noted*/ + int 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 */ + + const struct ieee80211_ratectl *iv_rate; + void *iv_rs; /* private ratectl 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[6]; +}; +MALLOC_DECLARE(M_80211_VAP); + +#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_UNIQMAC 0x00040000 /* CONF: user or computed mac */ + +#define IEEE80211_FEXT_BITS \ + "\20\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\10RESUME" \ + "\0114ADDR\12NONEPR_PR\13SWBMISS\14DFS\15DOTD\16STATEWAIT\17REINIT" \ + "\20BPF\21WDSLEGACY\22PROBECHAN\23UNIQMAC" + +/* 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(const struct ieee80211vap *, const char *, ...); +void ieee80211_note_mac(const struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN], const char *, ...); +void ieee80211_note_frame(const 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(const struct ieee80211vap *, + const struct ieee80211_frame *, const char *type, const char *fmt, ...); +void ieee80211_discard_ie(const struct ieee80211vap *, + const struct ieee80211_frame *, const char *type, const char *fmt, ...); +void ieee80211_discard_mac(const 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 /* _NET80211_IEEE80211_VAR_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_wds.c b/freebsd/sys/net80211/ieee80211_wds.c new file mode 100644 index 00000000..b0f5378d --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_wds.c @@ -0,0 +1,789 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/sys/cdefs.h> +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 WDS mode support. + */ +#include <freebsd/local/opt_inet.h> +#include <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/kernel.h> + +#include <freebsd/sys/socket.h> +#include <freebsd/sys/sockio.h> +#include <freebsd/sys/endian.h> +#include <freebsd/sys/errno.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sysctl.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/if_llc.h> +#include <freebsd/net/ethernet.h> + +#include <freebsd/net/bpf.h> + +#include <freebsd/net80211/ieee80211_var.h> +#include <freebsd/net80211/ieee80211_wds.h> +#include <freebsd/net80211/ieee80211_input.h> +#ifdef IEEE80211_SUPPORT_SUPERG +#include <freebsd/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 = 1; /* mbuf need to be tapped. */ + 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")); + + 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; + } +} diff --git a/freebsd/sys/net80211/ieee80211_wds.h b/freebsd/sys/net80211/ieee80211_wds.h new file mode 100644 index 00000000..200cba27 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_wds.h @@ -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 _NET80211_IEEE80211_WDS_HH_ +#define _NET80211_IEEE80211_WDS_HH_ + +/* + * 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 /* !_NET80211_IEEE80211_WDS_HH_ */ diff --git a/freebsd/sys/net80211/ieee80211_xauth.c b/freebsd/sys/net80211/ieee80211_xauth.c new file mode 100644 index 00000000..55318069 --- /dev/null +++ b/freebsd/sys/net80211/ieee80211_xauth.c @@ -0,0 +1,78 @@ +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * 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 <freebsd/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 <freebsd/local/opt_wlan.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/mbuf.h> +#include <freebsd/sys/module.h> + +#include <freebsd/sys/socket.h> + +#include <freebsd/net/if.h> +#include <freebsd/net/if_media.h> +#include <freebsd/net/ethernet.h> +#include <freebsd/net/route.h> + +#include <freebsd/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); |