summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/net80211
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2013-10-09 22:42:09 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2013-10-10 09:06:58 +0200
commitbceabc95c1c85d793200446fa85f1ddc6313ea29 (patch)
tree973c8bd8deca9fd69913f2895cc91e0e6114d46c /freebsd/sys/net80211
parentAdd FreeBSD sources as a submodule (diff)
downloadrtems-libbsd-bceabc95c1c85d793200446fa85f1ddc6313ea29.tar.bz2
Move files to match FreeBSD layout
Diffstat (limited to 'freebsd/sys/net80211')
-rw-r--r--freebsd/sys/net80211/_ieee80211.h396
-rw-r--r--freebsd/sys/net80211/ieee80211.c1638
-rw-r--r--freebsd/sys/net80211/ieee80211.h1087
-rw-r--r--freebsd/sys/net80211/ieee80211_acl.c341
-rw-r--r--freebsd/sys/net80211/ieee80211_action.c281
-rw-r--r--freebsd/sys/net80211/ieee80211_action.h52
-rw-r--r--freebsd/sys/net80211/ieee80211_adhoc.c929
-rw-r--r--freebsd/sys/net80211/ieee80211_adhoc.h35
-rw-r--r--freebsd/sys/net80211/ieee80211_ageq.c239
-rw-r--r--freebsd/sys/net80211/ieee80211_ageq.h54
-rw-r--r--freebsd/sys/net80211/ieee80211_amrr.c319
-rw-r--r--freebsd/sys/net80211/ieee80211_amrr.h61
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto.c663
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto.h245
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto_ccmp.c636
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto_none.c146
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto_tkip.c1000
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto_wep.c482
-rw-r--r--freebsd/sys/net80211/ieee80211_ddb.c881
-rw-r--r--freebsd/sys/net80211/ieee80211_dfs.c379
-rw-r--r--freebsd/sys/net80211/ieee80211_dfs.h57
-rw-r--r--freebsd/sys/net80211/ieee80211_freebsd.c831
-rw-r--r--freebsd/sys/net80211/ieee80211_freebsd.h550
-rw-r--r--freebsd/sys/net80211/ieee80211_hostap.c2307
-rw-r--r--freebsd/sys/net80211/ieee80211_hostap.h35
-rw-r--r--freebsd/sys/net80211/ieee80211_ht.c2523
-rw-r--r--freebsd/sys/net80211/ieee80211_ht.h202
-rw-r--r--freebsd/sys/net80211/ieee80211_hwmp.c1440
-rw-r--r--freebsd/sys/net80211/ieee80211_input.c852
-rw-r--r--freebsd/sys/net80211/ieee80211_input.h160
-rw-r--r--freebsd/sys/net80211/ieee80211_ioctl.c3349
-rw-r--r--freebsd/sys/net80211/ieee80211_ioctl.h849
-rw-r--r--freebsd/sys/net80211/ieee80211_mesh.c2755
-rw-r--r--freebsd/sys/net80211/ieee80211_mesh.h503
-rw-r--r--freebsd/sys/net80211/ieee80211_monitor.c140
-rw-r--r--freebsd/sys/net80211/ieee80211_monitor.h35
-rw-r--r--freebsd/sys/net80211/ieee80211_node.c2641
-rw-r--r--freebsd/sys/net80211/ieee80211_node.h456
-rw-r--r--freebsd/sys/net80211/ieee80211_output.c3043
-rw-r--r--freebsd/sys/net80211/ieee80211_phy.c467
-rw-r--r--freebsd/sys/net80211/ieee80211_phy.h155
-rw-r--r--freebsd/sys/net80211/ieee80211_power.c529
-rw-r--r--freebsd/sys/net80211/ieee80211_power.h79
-rw-r--r--freebsd/sys/net80211/ieee80211_proto.c1888
-rw-r--r--freebsd/sys/net80211/ieee80211_proto.h387
-rw-r--r--freebsd/sys/net80211/ieee80211_radiotap.c357
-rw-r--r--freebsd/sys/net80211/ieee80211_radiotap.h234
-rw-r--r--freebsd/sys/net80211/ieee80211_ratectl.c94
-rw-r--r--freebsd/sys/net80211/ieee80211_ratectl.h117
-rw-r--r--freebsd/sys/net80211/ieee80211_ratectl_none.c116
-rw-r--r--freebsd/sys/net80211/ieee80211_regdomain.c450
-rw-r--r--freebsd/sys/net80211/ieee80211_regdomain.h282
-rw-r--r--freebsd/sys/net80211/ieee80211_rssadapt.c351
-rw-r--r--freebsd/sys/net80211/ieee80211_rssadapt.h71
-rw-r--r--freebsd/sys/net80211/ieee80211_scan.c1240
-rw-r--r--freebsd/sys/net80211/ieee80211_scan.h301
-rw-r--r--freebsd/sys/net80211/ieee80211_scan_sta.c1928
-rw-r--r--freebsd/sys/net80211/ieee80211_sta.c1748
-rw-r--r--freebsd/sys/net80211/ieee80211_sta.h36
-rw-r--r--freebsd/sys/net80211/ieee80211_superg.c902
-rw-r--r--freebsd/sys/net80211/ieee80211_superg.h129
-rw-r--r--freebsd/sys/net80211/ieee80211_tdma.c822
-rw-r--r--freebsd/sys/net80211/ieee80211_tdma.h102
-rw-r--r--freebsd/sys/net80211/ieee80211_var.h916
-rw-r--r--freebsd/sys/net80211/ieee80211_wds.c789
-rw-r--r--freebsd/sys/net80211/ieee80211_wds.h39
-rw-r--r--freebsd/sys/net80211/ieee80211_xauth.c78
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(&params, 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,
+ &params);
+}
+
+#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(&params, 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, &params);
+}
+
+#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(&params, 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,
+ &params);
+}
+
+#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, &param, 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(&params, 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, &params);
+}
+
+/*
+ * 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(&params, 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, &params);
+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 = &reg->chaninfo.ic_chans[i];
+ if (c->ic_freq == 0 || c->ic_flags == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
+ "%s: invalid channel spec at [%u]\n", __func__, i);
+ return EINVAL;
+ }
+ if (c->ic_maxregpower == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
+ "%s: invalid channel spec, zero maxregpower, "
+ "freq %u flags 0x%x\n", __func__,
+ c->ic_freq, c->ic_flags);
+ return EINVAL;
+ }
+ if (c->ic_ieee == 0)
+ c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags);
+ if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0)
+ c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq +
+ (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20),
+ c->ic_flags);
+ if (c->ic_maxpower == 0)
+ c->ic_maxpower = 2*c->ic_maxregpower;
+ }
+ IEEE80211_LOCK(ic);
+ /* XXX bandaid; a running vap will likely crash */
+ if (!allvapsdown(ic)) {
+ IEEE80211_UNLOCK(ic);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
+ "%s: reject: vaps are running\n", __func__);
+ return EBUSY;
+ }
+ error = ic->ic_setregdomain(ic, &reg->rd,
+ reg->chaninfo.ic_nchans, reg->chaninfo.ic_chans);
+ if (error != 0) {
+ IEEE80211_UNLOCK(ic);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
+ "%s: driver rejected request, error %u\n", __func__, error);
+ return error;
+ }
+ /*
+ * Commit: copy in new channel table and reset media state.
+ * On return the state machines will be clocked so all vaps
+ * will reset their state.
+ *
+ * XXX ic_bsschan is marked undefined, must have vap's in
+ * INIT state or we blow up forcing stations off
+ */
+ /*
+ * Save any desired channel for restore below. Note this
+ * needs to be done for all vaps but for now we only do
+ * the one where the ioctl is issued.
+ */
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
+ desfreq = vap->iv_des_chan->ic_freq;
+ desflags = vap->iv_des_chan->ic_flags;
+ }
+ /* regdomain parameters */
+ memcpy(&ic->ic_regdomain, &reg->rd, sizeof(reg->rd));
+ /* channel table */
+ memcpy(ic->ic_channels, reg->chaninfo.ic_chans,
+ reg->chaninfo.ic_nchans * sizeof(struct ieee80211_channel));
+ ic->ic_nchans = reg->chaninfo.ic_nchans;
+ memset(&ic->ic_channels[ic->ic_nchans], 0,
+ (IEEE80211_CHAN_MAX - ic->ic_nchans) *
+ sizeof(struct ieee80211_channel));
+ ieee80211_media_init(ic);
+
+ /*
+ * Invalidate channel-related state.
+ */
+ if (ic->ic_countryie != NULL) {
+ free(ic->ic_countryie, M_80211_NODE_IE);
+ ic->ic_countryie = NULL;
+ }
+ ieee80211_scan_flush(vap);
+ ieee80211_dfs_reset(ic);
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
+ c = ieee80211_find_channel(ic, desfreq, desflags);
+ /* NB: may be NULL if not present in new channel list */
+ vap->iv_des_chan = (c != NULL) ? c : IEEE80211_CHAN_ANYC;
+ }
+ IEEE80211_UNLOCK(ic);
+
+ return 0;
+}
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, &param, sizeof(param));
+ frm += __offsetof(struct ieee80211_tdma_param, tdma_slot);
+ *frm++ = ts->tdma_slot;
+ *frm++ = ts->tdma_slotcnt;
+ /* NB: convert units to fit in 16-bits */
+ slotlen = ts->tdma_slotlen / 100; /* 100us units */
+ ADDSHORT(frm, slotlen);
+ *frm++ = ts->tdma_bintval;
+ *frm++ = ts->tdma_inuse[0];
+ frm += 10; /* pad+timestamp */
+ return frm;
+#undef ADDSHORT
+}
+#undef TDMA_OUI_BYTES
+
+/*
+ * Update TDMA state at TBTT.
+ */
+void
+ieee80211_tdma_update_beacon(struct ieee80211vap *vap,
+ struct ieee80211_beacon_offsets *bo)
+{
+ struct ieee80211_tdma_state *ts = vap->iv_tdma;
+
+ KASSERT(vap->iv_caps & IEEE80211_C_TDMA,
+ ("not a tdma vap, caps 0x%x", vap->iv_caps));
+
+ if (isset(bo->bo_flags, IEEE80211_BEACON_TDMA)) {
+ (void) ieee80211_add_tdma(bo->bo_tdma, vap);
+ clrbit(bo->bo_flags, IEEE80211_BEACON_TDMA);
+ }
+ if (ts->tdma_slot != 0) /* only on master */
+ return;
+ if (ts->tdma_count <= 0) {
+ /*
+ * Time to update the mask of active/inuse stations.
+ * We track stations that we've received a beacon
+ * frame from and update this mask periodically.
+ * This allows us to miss a few beacons before marking
+ * a slot free for re-use.
+ */
+ ts->tdma_inuse[0] = ts->tdma_active[0];
+ ts->tdma_active[0] = 0x01;
+ /* update next time 'round */
+ /* XXX use notify framework */
+ setbit(bo->bo_flags, IEEE80211_BEACON_TDMA);
+ /* NB: use s/w beacon miss threshold; may be too high */
+ ts->tdma_count = vap->iv_bmissthreshold-1;
+ } else
+ ts->tdma_count--;
+}
+
+static int
+tdma_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_tdma_state *ts = vap->iv_tdma;
+
+ if ((vap->iv_caps & IEEE80211_C_TDMA) == 0)
+ return EOPNOTSUPP;
+
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_TDMA_SLOT:
+ ireq->i_val = ts->tdma_slot;
+ break;
+ case IEEE80211_IOC_TDMA_SLOTCNT:
+ ireq->i_val = ts->tdma_slotcnt;
+ break;
+ case IEEE80211_IOC_TDMA_SLOTLEN:
+ ireq->i_val = ts->tdma_slotlen;
+ break;
+ case IEEE80211_IOC_TDMA_BINTERVAL:
+ ireq->i_val = ts->tdma_bintval;
+ break;
+ default:
+ return ENOSYS;
+ }
+ return 0;
+}
+IEEE80211_IOCTL_GET(tdma, tdma_ioctl_get80211);
+
+static int
+tdma_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_tdma_state *ts = vap->iv_tdma;
+
+ if ((vap->iv_caps & IEEE80211_C_TDMA) == 0)
+ return EOPNOTSUPP;
+
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_TDMA_SLOT:
+ if (!(0 <= ireq->i_val && ireq->i_val <= ts->tdma_slotcnt))
+ return EINVAL;
+ if (ireq->i_val != ts->tdma_slot) {
+ ts->tdma_slot = ireq->i_val;
+ goto restart;
+ }
+ break;
+ case IEEE80211_IOC_TDMA_SLOTCNT:
+ if (!TDMA_SLOTCNT_VALID(ireq->i_val))
+ return EINVAL;
+ if (ireq->i_val != ts->tdma_slotcnt) {
+ ts->tdma_slotcnt = ireq->i_val;
+ goto restart;
+ }
+ break;
+ case IEEE80211_IOC_TDMA_SLOTLEN:
+ /*
+ * XXX
+ * 150 insures at least 1/8 TU
+ * 0xfffff is the max duration for bursting
+ * (implict by way of 16-bit data type for i_val)
+ */
+ if (!TDMA_SLOTLEN_VALID(ireq->i_val))
+ return EINVAL;
+ if (ireq->i_val != ts->tdma_slotlen) {
+ ts->tdma_slotlen = ireq->i_val;
+ goto restart;
+ }
+ break;
+ case IEEE80211_IOC_TDMA_BINTERVAL:
+ if (!TDMA_BINTVAL_VALID(ireq->i_val))
+ return EINVAL;
+ if (ireq->i_val != ts->tdma_bintval) {
+ ts->tdma_bintval = ireq->i_val;
+ goto restart;
+ }
+ break;
+ default:
+ return ENOSYS;
+ }
+ return 0;
+restart:
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_TDMA);
+ return ERESTART;
+}
+IEEE80211_IOCTL_SET(tdma, tdma_ioctl_set80211);
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);