summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/net80211
diff options
context:
space:
mode:
authorChristian Mauderer <Christian.Mauderer@embedded-brains.de>2016-11-14 13:46:13 +0100
committerChristian Mauderer <Christian.Mauderer@embedded-brains.de>2017-01-17 12:50:57 +0100
commita241ea8e924154a217768b6e4910a62e0d25cfe2 (patch)
tree404fef7dab5f6dd06c0c3889dbc96214a5e226d4 /freebsd/sys/net80211
parentUse thread name support (diff)
downloadrtems-libbsd-a241ea8e924154a217768b6e4910a62e0d25cfe2.tar.bz2
Import IEEE 802.11 from FreeBSD.
Diffstat (limited to 'freebsd/sys/net80211')
-rw-r--r--freebsd/sys/net80211/_ieee80211.h138
-rw-r--r--freebsd/sys/net80211/ieee80211.c883
-rw-r--r--freebsd/sys/net80211/ieee80211.h342
-rw-r--r--freebsd/sys/net80211/ieee80211_acl.c26
-rw-r--r--freebsd/sys/net80211/ieee80211_action.c136
-rw-r--r--freebsd/sys/net80211/ieee80211_adhoc.c211
-rw-r--r--freebsd/sys/net80211/ieee80211_ageq.c2
-rw-r--r--freebsd/sys/net80211/ieee80211_alq.c158
-rw-r--r--freebsd/sys/net80211/ieee80211_alq.h59
-rw-r--r--freebsd/sys/net80211/ieee80211_amrr.c229
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto.c185
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto.h67
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto_ccmp.c84
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto_none.c19
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto_tkip.c137
-rw-r--r--freebsd/sys/net80211/ieee80211_crypto_wep.c87
-rw-r--r--freebsd/sys/net80211/ieee80211_ddb.c97
-rw-r--r--freebsd/sys/net80211/ieee80211_dfs.c114
-rw-r--r--freebsd/sys/net80211/ieee80211_dfs.h6
-rw-r--r--freebsd/sys/net80211/ieee80211_freebsd.c344
-rw-r--r--freebsd/sys/net80211/ieee80211_freebsd.h289
-rw-r--r--freebsd/sys/net80211/ieee80211_hostap.c342
-rw-r--r--freebsd/sys/net80211/ieee80211_hostap.h6
-rw-r--r--freebsd/sys/net80211/ieee80211_ht.c516
-rw-r--r--freebsd/sys/net80211/ieee80211_ht.h30
-rw-r--r--freebsd/sys/net80211/ieee80211_hwmp.c1536
-rw-r--r--freebsd/sys/net80211/ieee80211_input.c158
-rw-r--r--freebsd/sys/net80211/ieee80211_input.h72
-rw-r--r--freebsd/sys/net80211/ieee80211_ioctl.c697
-rw-r--r--freebsd/sys/net80211/ieee80211_ioctl.h28
-rw-r--r--freebsd/sys/net80211/ieee80211_mesh.c1510
-rw-r--r--freebsd/sys/net80211/ieee80211_mesh.h283
-rw-r--r--freebsd/sys/net80211/ieee80211_monitor.c8
-rw-r--r--freebsd/sys/net80211/ieee80211_node.c776
-rw-r--r--freebsd/sys/net80211/ieee80211_node.h39
-rw-r--r--freebsd/sys/net80211/ieee80211_output.c1296
-rw-r--r--freebsd/sys/net80211/ieee80211_phy.c199
-rw-r--r--freebsd/sys/net80211/ieee80211_phy.h63
-rw-r--r--freebsd/sys/net80211/ieee80211_power.c159
-rw-r--r--freebsd/sys/net80211/ieee80211_power.h8
-rw-r--r--freebsd/sys/net80211/ieee80211_proto.c449
-rw-r--r--freebsd/sys/net80211/ieee80211_proto.h90
-rw-r--r--freebsd/sys/net80211/ieee80211_radiotap.c41
-rw-r--r--freebsd/sys/net80211/ieee80211_radiotap.h21
-rw-r--r--freebsd/sys/net80211/ieee80211_ratectl.c44
-rw-r--r--freebsd/sys/net80211/ieee80211_ratectl.h92
-rw-r--r--freebsd/sys/net80211/ieee80211_ratectl_none.c14
-rw-r--r--freebsd/sys/net80211/ieee80211_regdomain.c91
-rw-r--r--freebsd/sys/net80211/ieee80211_regdomain.h11
-rw-r--r--freebsd/sys/net80211/ieee80211_rssadapt.c37
-rw-r--r--freebsd/sys/net80211/ieee80211_scan.c733
-rw-r--r--freebsd/sys/net80211/ieee80211_scan.h50
-rw-r--r--freebsd/sys/net80211/ieee80211_scan_sta.c118
-rw-r--r--freebsd/sys/net80211/ieee80211_scan_sw.c1016
-rw-r--r--freebsd/sys/net80211/ieee80211_scan_sw.h32
-rw-r--r--freebsd/sys/net80211/ieee80211_sta.c350
-rw-r--r--freebsd/sys/net80211/ieee80211_sta.h6
-rw-r--r--freebsd/sys/net80211/ieee80211_superg.c401
-rw-r--r--freebsd/sys/net80211/ieee80211_superg.h54
-rw-r--r--freebsd/sys/net80211/ieee80211_tdma.c36
-rw-r--r--freebsd/sys/net80211/ieee80211_tdma.h3
-rw-r--r--freebsd/sys/net80211/ieee80211_var.h253
-rw-r--r--freebsd/sys/net80211/ieee80211_wds.c96
-rw-r--r--freebsd/sys/net80211/ieee80211_xauth.c1
64 files changed, 11199 insertions, 4179 deletions
diff --git a/freebsd/sys/net80211/_ieee80211.h b/freebsd/sys/net80211/_ieee80211.h
index 3793c661..13155ea3 100644
--- a/freebsd/sys/net80211/_ieee80211.h
+++ b/freebsd/sys/net80211/_ieee80211.h
@@ -45,6 +45,7 @@ enum ieee80211_phytype {
IEEE80211_T_HT, /* high throughput */
IEEE80211_T_OFDM_HALF, /* 1/2 rate OFDM */
IEEE80211_T_OFDM_QUARTER, /* 1/4 rate OFDM */
+ IEEE80211_T_VHT, /* VHT PHY */
};
#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
@@ -68,8 +69,11 @@ enum ieee80211_phymode {
IEEE80211_MODE_11NG = 9, /* 2GHz, w/ HT */
IEEE80211_MODE_HALF = 10, /* OFDM, 1/2x clock */
IEEE80211_MODE_QUARTER = 11, /* OFDM, 1/4x clock */
+ IEEE80211_MODE_VHT_2GHZ = 12, /* 2GHz, VHT */
+ IEEE80211_MODE_VHT_5GHZ = 13, /* 5GHz, VHT */
};
-#define IEEE80211_MODE_MAX (IEEE80211_MODE_QUARTER+1)
+#define IEEE80211_MODE_MAX (IEEE80211_MODE_VHT_5GHZ+1)
+#define IEEE80211_MODE_BYTES howmany(IEEE80211_MODE_MAX, NBBY)
/*
* Operating mode. Devices do not necessarily support
@@ -133,7 +137,7 @@ enum ieee80211_roamingmode {
*/
struct ieee80211_channel {
uint32_t ic_flags; /* see below */
- uint16_t ic_freq; /* setting in MHz */
+ uint16_t ic_freq; /* primary centre frequency 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 */
@@ -143,10 +147,18 @@ struct ieee80211_channel {
int8_t ic_maxantgain; /* maximum antenna gain in .5 dBm */
uint8_t ic_pad;
uint16_t ic_devdata; /* opaque device/driver data */
+ uint8_t ic_vht_ch_freq1; /* VHT primary freq1 IEEE value */
+ uint8_t ic_vht_ch_freq2; /* VHT secondary 80MHz freq2 IEEE value */
+ uint16_t ic_freq2; /* VHT secondary 80MHz freq2 MHz */
};
-#define IEEE80211_CHAN_MAX 256
-#define IEEE80211_CHAN_BYTES 32 /* howmany(IEEE80211_CHAN_MAX, NBBY) */
+/*
+ * Note: for VHT operation we will need significantly more than
+ * IEEE80211_CHAN_MAX channels because of the combinations of
+ * VHT20, VHT40, VHT80, VHT80+80 and VHT160.
+ */
+#define IEEE80211_CHAN_MAX 1024
+#define IEEE80211_CHAN_BYTES howmany(IEEE80211_CHAN_MAX, NBBY)
#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */
#define IEEE80211_CHAN_ANYC \
((struct ieee80211_channel *) IEEE80211_CHAN_ANY)
@@ -176,14 +188,27 @@ struct ieee80211_channel {
#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_VHT20 0x01000000 /* VHT20 channel */
+#define IEEE80211_CHAN_VHT40U 0x02000000 /* VHT40 channel, ext above */
+#define IEEE80211_CHAN_VHT40D 0x04000000 /* VHT40 channel, ext below */
+#define IEEE80211_CHAN_VHT80 0x08000000 /* VHT80 channel */
+#define IEEE80211_CHAN_VHT80_80 0x10000000 /* VHT80+80 channel */
+#define IEEE80211_CHAN_VHT160 0x20000000 /* VHT160 channel */
+/* XXX note: 0x80000000 is used in src/sbin/ifconfig/ifieee80211.c :( */
#define IEEE80211_CHAN_HT40 (IEEE80211_CHAN_HT40U | IEEE80211_CHAN_HT40D)
#define IEEE80211_CHAN_HT (IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40)
+#define IEEE80211_CHAN_VHT40 (IEEE80211_CHAN_VHT40U | IEEE80211_CHAN_VHT40D)
+#define IEEE80211_CHAN_VHT (IEEE80211_CHAN_VHT20 | IEEE80211_CHAN_VHT40 \
+ | IEEE80211_CHAN_VHT80 | IEEE80211_CHAN_VHT80_80 \
+ | IEEE80211_CHAN_VHT160)
+
#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"
+ "\22HT40U\23HT40D\24DFS\0254MSXMIT\26NOADHOC\27NOHOSTAP\03011D" \
+ "\031VHT20\032VHT40U\033VHT40D\034VHT80\035VHT80_80\036VHT160"
/*
* Useful combinations of channel characteristics.
@@ -209,7 +234,7 @@ struct ieee80211_channel {
(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)
+ IEEE80211_CHAN_HT | IEEE80211_CHAN_VHT)
#define IEEE80211_CHAN_ALLTURBO \
(IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)
@@ -242,6 +267,8 @@ struct ieee80211_channel {
(((_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_DYN(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_DYN) == IEEE80211_CHAN_DYN)
#define IEEE80211_IS_CHAN_GFSK(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0)
#define IEEE80211_IS_CHAN_TURBO(_c) \
@@ -284,6 +311,29 @@ struct ieee80211_channel {
#define IEEE80211_IS_CHAN_11D(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_11D) != 0)
+#define IEEE80211_IS_CHAN_VHT(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_VHT) != 0)
+#define IEEE80211_IS_CHAN_VHT20(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_VHT20) != 0)
+#define IEEE80211_IS_CHAN_VHT40(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_VHT40) != 0)
+#define IEEE80211_IS_CHAN_VHT40U(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_VHT40U) != 0)
+#define IEEE80211_IS_CHAN_VHT40D(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_VHT40D) != 0)
+#define IEEE80211_IS_CHAN_VHTA(_c) \
+ (IEEE80211_IS_CHAN_5GHZ(_c) && \
+ ((_c)->ic_flags & IEEE80211_CHAN_VHT) != 0)
+#define IEEE80211_IS_CHAN_VHTG(_c) \
+ (IEEE80211_IS_CHAN_2GHZ(_c) && \
+ ((_c)->ic_flags & IEEE80211_CHAN_VHT) != 0)
+#define IEEE80211_IS_CHAN_VHT80(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_VHT80) != 0)
+#define IEEE80211_IS_CHAN_VHT80_80(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_VHT80_80) != 0)
+#define IEEE80211_IS_CHAN_VHT160(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_VHT160) != 0)
+
#define IEEE80211_CHAN2IEEE(_c) (_c)->ic_ieee
/* dynamic state */
@@ -388,16 +438,82 @@ struct ieee80211_regdomain {
* MIMO antenna/radio state.
*/
-#define IEEE80211_MAX_CHAINS 3
-#define IEEE80211_MAX_EVM_PILOTS 6
-
/*
* XXX This doesn't yet export both ctl/ext chain details
+ * XXX TODO: IEEE80211_MAX_CHAINS is defined in _freebsd.h, not here;
+ * figure out how to pull it in!
*/
struct ieee80211_mimo_info {
- int8_t rssi[IEEE80211_MAX_CHAINS]; /* per-antenna rssi */
- int8_t noise[IEEE80211_MAX_CHAINS]; /* per-antenna noise floor */
+ 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 */
};
+
+/*
+ * 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 */
+#define IEEE80211_C_SWSLEEP 0x00080000 /* CAPABILITY: do sleep here */
+#define IEEE80211_C_SWAMSDUTX 0x00100000 /* CAPABILITY: software A-MSDU TX */
+/* 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_HTC_RXUNEQUAL 0x00200000 /* CAPABILITY: RX unequal MCS */
+#define IEEE80211_HTC_RXMCS32 0x00400000 /* CAPABILITY: MCS32 support */
+#define IEEE80211_HTC_TXUNEQUAL 0x00800000 /* CAPABILITY: TX unequal MCS */
+#define IEEE80211_HTC_TXMCS32 0x01000000 /* CAPABILITY: MCS32 support */
+
+#define IEEE80211_C_HTCAP_BITS \
+ "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
+ "\21AMPDU\22AMSDU\23HT\24SMPS\25RIFS"
+
#endif /* _NET80211__IEEE80211_H_ */
diff --git a/freebsd/sys/net80211/ieee80211.c b/freebsd/sys/net80211/ieee80211.c
index 7021ff31..7fcd3dcd 100644
--- a/freebsd/sys/net80211/ieee80211.c
+++ b/freebsd/sys/net80211/ieee80211.c
@@ -37,10 +37,14 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
-
+#include <sys/malloc.h>
#include <sys/socket.h>
+#include <sys/sbuf.h>
+
+#include <machine/stdarg.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
@@ -82,19 +86,19 @@ const int ieee80211_opcap[IEEE80211_OPMODE_MAX] = {
#endif
};
-static const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] =
+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 void ieee80211_syncflag_vht_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 *);
+static uint64_t ieee80211_get_counter(struct ifnet *, ift_counter);
MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state");
@@ -120,7 +124,7 @@ static const struct ieee80211_rateset ieee80211_rateset_11g =
* all available channels as active, and pick
* a default channel if not already specified.
*/
-static void
+void
ieee80211_chan_init(struct ieee80211com *ic)
{
#define DEFAULTRATES(m, def) do { \
@@ -224,71 +228,101 @@ ieee80211_chan_init(struct ieee80211com *ic)
}
static void
-null_update_mcast(struct ifnet *ifp)
+null_update_mcast(struct ieee80211com *ic)
{
- if_printf(ifp, "need multicast update callback\n");
+
+ ic_printf(ic, "need multicast update callback\n");
}
static void
-null_update_promisc(struct ifnet *ifp)
+null_update_promisc(struct ieee80211com *ic)
{
- if_printf(ifp, "need promiscuous mode update callback\n");
+
+ ic_printf(ic, "need promiscuous mode update callback\n");
}
-static int
-null_transmit(struct ifnet *ifp, struct mbuf *m)
+static void
+null_update_chw(struct ieee80211com *ic)
{
- m_freem(m);
- ifp->if_oerrors++;
- return EACCES; /* XXX EIO/EPERM? */
+
+ ic_printf(ic, "%s: need callback\n", __func__);
}
-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);
+int
+ic_printf(struct ieee80211com *ic, const char * fmt, ...)
+{
+ va_list ap;
+ int retval;
+
+ retval = printf("%s: ", ic->ic_name);
+ va_start(ap, fmt);
+ retval += vprintf(fmt, ap);
+ va_end(ap);
+ return (retval);
}
-static void
-null_input(struct ifnet *ifp, struct mbuf *m)
+static LIST_HEAD(, ieee80211com) ic_head = LIST_HEAD_INITIALIZER(ic_head);
+static struct mtx ic_list_mtx;
+MTX_SYSINIT(ic_list, &ic_list_mtx, "ieee80211com list", MTX_DEF);
+
+static int
+sysctl_ieee80211coms(SYSCTL_HANDLER_ARGS)
{
- if_printf(ifp, "if_input should not be called\n");
- m_freem(m);
+ struct ieee80211com *ic;
+ struct sbuf sb;
+ char *sp;
+ int error;
+
+ error = sysctl_wire_old_buffer(req, 0);
+ if (error)
+ return (error);
+ sbuf_new_for_sysctl(&sb, NULL, 8, req);
+ sbuf_clear_flags(&sb, SBUF_INCLUDENUL);
+ sp = "";
+ mtx_lock(&ic_list_mtx);
+ LIST_FOREACH(ic, &ic_head, ic_next) {
+ sbuf_printf(&sb, "%s%s", sp, ic->ic_name);
+ sp = " ";
+ }
+ mtx_unlock(&ic_list_mtx);
+ error = sbuf_finish(&sb);
+ sbuf_delete(&sb);
+ return (error);
}
+SYSCTL_PROC(_net_wlan, OID_AUTO, devices,
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
+ sysctl_ieee80211coms, "A", "names of available 802.11 devices");
+
/*
* 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])
+ieee80211_ifattach(struct ieee80211com *ic)
{
- 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);
+ IEEE80211_LOCK_INIT(ic, ic->ic_name);
+ IEEE80211_TX_LOCK_INIT(ic, ic->ic_name);
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);
+ taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s net80211 taskq",
+ ic->ic_name);
+ ic->ic_ierrors = counter_u64_alloc(M_WAITOK);
+ ic->ic_oerrors = counter_u64_alloc(M_WAITOK);
/*
* 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);
+ ieee80211_chan_init(ic);
ic->ic_update_mcast = null_update_mcast;
ic->ic_update_promisc = null_update_promisc;
+ ic->ic_update_chw = null_update_chw;
ic->ic_hash_key = arc4random();
ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
@@ -309,22 +343,9 @@ ieee80211_ifattach(struct ieee80211com *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);
+ mtx_lock(&ic_list_mtx);
+ LIST_INSERT_HEAD(&ic_head, ic, ic_next);
+ mtx_unlock(&ic_list_mtx);
}
/*
@@ -336,11 +357,18 @@ ieee80211_ifattach(struct ieee80211com *ic,
void
ieee80211_ifdetach(struct ieee80211com *ic)
{
- struct ifnet *ifp = ic->ic_ifp;
struct ieee80211vap *vap;
- if_detach(ifp);
+ mtx_lock(&ic_list_mtx);
+ LIST_REMOVE(ic, ic_next);
+ mtx_unlock(&ic_list_mtx);
+
+ taskqueue_drain(taskqueue_thread, &ic->ic_restart_task);
+ /*
+ * The VAP is responsible for setting and clearing
+ * the VIMAGE context.
+ */
while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
ieee80211_vap_destroy(vap);
ieee80211_waitfor_parent(ic);
@@ -359,11 +387,39 @@ ieee80211_ifdetach(struct ieee80211com *ic)
ieee80211_power_detach(ic);
ieee80211_node_detach(ic);
- ifmedia_removeall(&ic->ic_media);
+ counter_u64_free(ic->ic_ierrors);
+ counter_u64_free(ic->ic_oerrors);
+
taskqueue_free(ic->ic_tq);
+ IEEE80211_TX_LOCK_DESTROY(ic);
IEEE80211_LOCK_DESTROY(ic);
}
+struct ieee80211com *
+ieee80211_find_com(const char *name)
+{
+ struct ieee80211com *ic;
+
+ mtx_lock(&ic_list_mtx);
+ LIST_FOREACH(ic, &ic_head, ic_next)
+ if (strcmp(ic->ic_name, name) == 0)
+ break;
+ mtx_unlock(&ic_list_mtx);
+
+ return (ic);
+}
+
+void
+ieee80211_iterate_coms(ieee80211_com_iter_func *f, void *arg)
+{
+ struct ieee80211com *ic;
+
+ mtx_lock(&ic_list_mtx);
+ LIST_FOREACH(ic, &ic_head, ic_next)
+ (*f)(arg, ic);
+ mtx_unlock(&ic_list_mtx);
+}
+
/*
* Default reset method for use with the ioctl support. This
* method is invoked after any state change in the 802.11
@@ -380,6 +436,47 @@ default_reset(struct ieee80211vap *vap, u_long cmd)
}
/*
+ * Default for updating the VAP default TX key index.
+ *
+ * Drivers that support TX offload as well as hardware encryption offload
+ * may need to be informed of key index changes separate from the key
+ * update.
+ */
+static void
+default_update_deftxkey(struct ieee80211vap *vap, ieee80211_keyix kid)
+{
+
+ /* XXX assert validity */
+ /* XXX assert we're in a key update block */
+ vap->iv_def_txkey = kid;
+}
+
+/*
+ * Add underlying device errors to vap errors.
+ */
+static uint64_t
+ieee80211_get_counter(struct ifnet *ifp, ift_counter cnt)
+{
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ uint64_t rv;
+
+ rv = if_get_counter_default(ifp, cnt);
+ switch (cnt) {
+ case IFCOUNTER_OERRORS:
+ rv += counter_u64_fetch(ic->ic_oerrors);
+ break;
+ case IFCOUNTER_IERRORS:
+ rv += counter_u64_fetch(ic->ic_ierrors);
+ break;
+ default:
+ break;
+ }
+
+ return (rv);
+}
+
+/*
* 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).
@@ -387,27 +484,24 @@ default_reset(struct ieee80211vap *vap, u_long cmd)
int
ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap,
const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode,
- int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
- const uint8_t macaddr[IEEE80211_ADDR_LEN])
+ int flags, const uint8_t bssid[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",
+ ic_printf(ic, "%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_transmit = ieee80211_vap_transmit;
+ ifp->if_qflush = ieee80211_vap_qflush;
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);
+ ifp->if_get_counter = ieee80211_get_counter;
vap->iv_ifp = ifp;
vap->iv_ic = ic;
@@ -419,6 +513,7 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap,
vap->iv_htextcaps = ic->ic_htextcaps;
vap->iv_opmode = opmode;
vap->iv_caps |= ieee80211_opcap[opmode];
+ IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_macaddr);
switch (opmode) {
case IEEE80211_M_WDS:
/*
@@ -485,7 +580,11 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap,
*/
vap->iv_reset = default_reset;
- IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr);
+ /*
+ * Install a default crypto key update method, the driver
+ * can override this.
+ */
+ vap->iv_update_deftxkey = default_update_deftxkey;
ieee80211_sysctl_vattach(vap);
ieee80211_crypto_vattach(vap);
@@ -510,8 +609,8 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap,
* 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)
+ieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change,
+ ifm_stat_cb_t media_stat, const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
struct ifnet *ifp = vap->iv_ifp;
struct ieee80211com *ic = vap->iv_ic;
@@ -521,7 +620,7 @@ ieee80211_vap_attach(struct ieee80211vap *vap,
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);
+ ic->ic_name, vap->iv_flags, vap->iv_flags_ext);
/*
* Do late attach work that cannot happen until after
@@ -539,16 +638,11 @@ ieee80211_vap_attach(struct ieee80211vap *vap,
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;
- }
+ ether_ifattach(ifp, macaddr);
+ IEEE80211_ADDR_COPY(vap->iv_myaddr, IF_LLADDR(ifp));
+ /* 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);
@@ -561,8 +655,12 @@ ieee80211_vap_attach(struct ieee80211vap *vap,
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_syncflag_vht_locked(ic, IEEE80211_FVHT_VHT);
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT40);
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80);
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80P80);
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT160);
IEEE80211_UNLOCK(ic);
return 1;
@@ -580,9 +678,10 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = vap->iv_ifp;
+ CURVNET_SET(ifp->if_vnet);
+
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n",
- __func__, ieee80211_opmode_name[vap->iv_opmode],
- ic->ic_ifp->if_xname);
+ __func__, ieee80211_opmode_name[vap->iv_opmode], ic->ic_name);
/* NB: bpfdetach is called by ether_ifdetach and claims all taps */
ether_ifdetach(ifp);
@@ -609,10 +708,19 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
ieee80211_syncflag_locked(ic, IEEE80211_F_BURST);
ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT);
ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40);
+
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_VHT);
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT40);
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80);
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80P80);
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT160);
+
/* 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);
+ if (vap->iv_ifflags & IFF_PROMISC)
+ ieee80211_promisc(vap, false);
+ if (vap->iv_ifflags & IFF_ALLMULTI)
+ ieee80211_allmulti(vap, false);
IEEE80211_UNLOCK(ic);
ifmedia_removeall(&vap->iv_media);
@@ -632,51 +740,51 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
ieee80211_sysctl_vdetach(vap);
if_free(ifp);
+
+ CURVNET_RESTORE();
}
/*
- * 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.
+ * Count number of vaps in promisc, and issue promisc on
+ * parent respectively.
*/
void
-ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag)
+ieee80211_promisc(struct ieee80211vap *vap, bool on)
{
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211vap *vap;
- int bit, oflags;
+ struct ieee80211com *ic = vap->iv_ic;
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);
- }
+ if (on) {
+ if (++ic->ic_promisc == 1)
+ ieee80211_runtask(ic, &ic->ic_promisc_task);
+ } else {
+ KASSERT(ic->ic_promisc > 0, ("%s: ic %p not promisc",
+ __func__, ic));
+ if (--ic->ic_promisc == 0)
+ ieee80211_runtask(ic, &ic->ic_promisc_task);
+ }
+}
+
+/*
+ * Count number of vaps in allmulti, and issue allmulti on
+ * parent respectively.
+ */
+void
+ieee80211_allmulti(struct ieee80211vap *vap, bool on)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if (on) {
+ if (++ic->ic_allmulti == 1)
+ ieee80211_runtask(ic, &ic->ic_mcast_task);
+ } else {
+ KASSERT(ic->ic_allmulti > 0, ("%s: ic %p not allmulti",
+ __func__, ic));
+ if (--ic->ic_allmulti == 0)
+ ieee80211_runtask(ic, &ic->ic_mcast_task);
}
}
@@ -761,6 +869,46 @@ ieee80211_syncflag_ht(struct ieee80211vap *vap, int flag)
}
/*
+ * Synchronize flags_vht 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_vht_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_vht & flag) {
+ bit = 1;
+ break;
+ }
+ if (bit)
+ ic->ic_flags_vht |= flag;
+ else
+ ic->ic_flags_vht &= ~flag;
+}
+
+void
+ieee80211_syncflag_vht(struct ieee80211vap *vap, int flag)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ if (flag < 0) {
+ flag = -flag;
+ vap->iv_flags_vht &= ~flag;
+ } else
+ vap->iv_flags_vht |= flag;
+ ieee80211_syncflag_vht_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.
@@ -872,7 +1020,7 @@ int
ieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c)
{
if (c == NULL) {
- if_printf(ic->ic_ifp, "invalid channel (NULL)\n");
+ ic_printf(ic, "invalid channel (NULL)\n");
return 0; /* XXX */
}
return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee);
@@ -911,6 +1059,308 @@ ieee80211_ieee2mhz(u_int chan, u_int flags)
}
}
+static __inline void
+set_extchan(struct ieee80211_channel *c)
+{
+
+ /*
+ * IEEE Std 802.11-2012, page 1738, subclause 20.3.15.4:
+ * "the secondary channel number shall be 'N + [1,-1] * 4'
+ */
+ if (c->ic_flags & IEEE80211_CHAN_HT40U)
+ c->ic_extieee = c->ic_ieee + 4;
+ else if (c->ic_flags & IEEE80211_CHAN_HT40D)
+ c->ic_extieee = c->ic_ieee - 4;
+ else
+ c->ic_extieee = 0;
+}
+
+static int
+addchan(struct ieee80211_channel chans[], int maxchans, int *nchans,
+ uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t flags)
+{
+ struct ieee80211_channel *c;
+
+ if (*nchans >= maxchans)
+ return (ENOBUFS);
+
+ c = &chans[(*nchans)++];
+ c->ic_ieee = ieee;
+ c->ic_freq = freq != 0 ? freq : ieee80211_ieee2mhz(ieee, flags);
+ c->ic_maxregpower = maxregpower;
+ c->ic_maxpower = 2 * maxregpower;
+ c->ic_flags = flags;
+ set_extchan(c);
+
+ return (0);
+}
+
+static int
+copychan_prev(struct ieee80211_channel chans[], int maxchans, int *nchans,
+ uint32_t flags)
+{
+ struct ieee80211_channel *c;
+
+ KASSERT(*nchans > 0, ("channel list is empty\n"));
+
+ if (*nchans >= maxchans)
+ return (ENOBUFS);
+
+ c = &chans[(*nchans)++];
+ c[0] = c[-1];
+ c->ic_flags = flags;
+ set_extchan(c);
+
+ return (0);
+}
+
+static void
+getflags_2ghz(const uint8_t bands[], uint32_t flags[], int ht40)
+{
+ int nmodes;
+
+ nmodes = 0;
+ if (isset(bands, IEEE80211_MODE_11B))
+ flags[nmodes++] = IEEE80211_CHAN_B;
+ if (isset(bands, IEEE80211_MODE_11G))
+ flags[nmodes++] = IEEE80211_CHAN_G;
+ if (isset(bands, IEEE80211_MODE_11NG))
+ flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT20;
+ if (ht40) {
+ flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U;
+ flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D;
+ }
+ flags[nmodes] = 0;
+}
+
+static void
+getflags_5ghz(const uint8_t bands[], uint32_t flags[], int ht40)
+{
+ int nmodes;
+
+ nmodes = 0;
+ if (isset(bands, IEEE80211_MODE_11A))
+ flags[nmodes++] = IEEE80211_CHAN_A;
+ if (isset(bands, IEEE80211_MODE_11NA))
+ flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20;
+ if (ht40) {
+ flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U;
+ flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D;
+ }
+ flags[nmodes] = 0;
+}
+
+static void
+getflags(const uint8_t bands[], uint32_t flags[], int ht40)
+{
+
+ flags[0] = 0;
+ if (isset(bands, IEEE80211_MODE_11A) ||
+ isset(bands, IEEE80211_MODE_11NA)) {
+ if (isset(bands, IEEE80211_MODE_11B) ||
+ isset(bands, IEEE80211_MODE_11G) ||
+ isset(bands, IEEE80211_MODE_11NG))
+ return;
+
+ getflags_5ghz(bands, flags, ht40);
+ } else
+ getflags_2ghz(bands, flags, ht40);
+}
+
+/*
+ * Add one 20 MHz channel into specified channel list.
+ */
+int
+ieee80211_add_channel(struct ieee80211_channel chans[], int maxchans,
+ int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower,
+ uint32_t chan_flags, const uint8_t bands[])
+{
+ uint32_t flags[IEEE80211_MODE_MAX];
+ int i, error;
+
+ getflags(bands, flags, 0);
+ KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__));
+
+ error = addchan(chans, maxchans, nchans, ieee, freq, maxregpower,
+ flags[0] | chan_flags);
+ for (i = 1; flags[i] != 0 && error == 0; i++) {
+ error = copychan_prev(chans, maxchans, nchans,
+ flags[i] | chan_flags);
+ }
+
+ return (error);
+}
+
+static struct ieee80211_channel *
+findchannel(struct ieee80211_channel chans[], int nchans, uint16_t freq,
+ uint32_t flags)
+{
+ struct ieee80211_channel *c;
+ int i;
+
+ flags &= IEEE80211_CHAN_ALLTURBO;
+ /* brute force search */
+ for (i = 0; i < nchans; i++) {
+ c = &chans[i];
+ if (c->ic_freq == freq &&
+ (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+ return c;
+ }
+ return NULL;
+}
+
+/*
+ * Add 40 MHz channel pair into specified channel list.
+ */
+int
+ieee80211_add_channel_ht40(struct ieee80211_channel chans[], int maxchans,
+ int *nchans, uint8_t ieee, int8_t maxregpower, uint32_t flags)
+{
+ struct ieee80211_channel *cent, *extc;
+ uint16_t freq;
+ int error;
+
+ freq = ieee80211_ieee2mhz(ieee, flags);
+
+ /*
+ * Each entry defines an HT40 channel pair; find the
+ * center channel, then the extension channel above.
+ */
+ flags |= IEEE80211_CHAN_HT20;
+ cent = findchannel(chans, *nchans, freq, flags);
+ if (cent == NULL)
+ return (EINVAL);
+
+ extc = findchannel(chans, *nchans, freq + 20, flags);
+ if (extc == NULL)
+ return (ENOENT);
+
+ flags &= ~IEEE80211_CHAN_HT;
+ error = addchan(chans, maxchans, nchans, cent->ic_ieee, cent->ic_freq,
+ maxregpower, flags | IEEE80211_CHAN_HT40U);
+ if (error != 0)
+ return (error);
+
+ error = addchan(chans, maxchans, nchans, extc->ic_ieee, extc->ic_freq,
+ maxregpower, flags | IEEE80211_CHAN_HT40D);
+
+ return (error);
+}
+
+/*
+ * Fetch the center frequency for the primary channel.
+ */
+uint32_t
+ieee80211_get_channel_center_freq(const struct ieee80211_channel *c)
+{
+
+ return (c->ic_freq);
+}
+
+/*
+ * Fetch the center frequency for the primary BAND channel.
+ *
+ * For 5, 10, 20MHz channels it'll be the normally configured channel
+ * frequency.
+ *
+ * For 40MHz, 80MHz, 160Mhz channels it'll the the centre of the
+ * wide channel, not the centre of the primary channel (that's ic_freq).
+ *
+ * For 80+80MHz channels this will be the centre of the primary
+ * 80MHz channel; the secondary 80MHz channel will be center_freq2().
+ */
+
+uint32_t
+ieee80211_get_channel_center_freq1(const struct ieee80211_channel *c)
+{
+
+ if (IEEE80211_IS_CHAN_HT40U(c)) {
+ return (c->ic_freq + 10);
+ }
+ if (IEEE80211_IS_CHAN_HT40D(c)) {
+ return (c->ic_freq - 10);
+ }
+
+ return (c->ic_freq);
+}
+
+/*
+ * For now, no 80+80 support; this is zero.
+ */
+uint32_t
+ieee80211_get_channel_center_freq2(const struct ieee80211_channel *c)
+{
+
+ return (0);
+}
+
+/*
+ * Adds channels into specified channel list (ieee[] array must be sorted).
+ * Channels are already sorted.
+ */
+static int
+add_chanlist(struct ieee80211_channel chans[], int maxchans, int *nchans,
+ const uint8_t ieee[], int nieee, uint32_t flags[])
+{
+ uint16_t freq;
+ int i, j, error;
+
+ for (i = 0; i < nieee; i++) {
+ freq = ieee80211_ieee2mhz(ieee[i], flags[0]);
+ for (j = 0; flags[j] != 0; j++) {
+ if (flags[j] & IEEE80211_CHAN_HT40D)
+ if (i == 0 || ieee[i] < ieee[0] + 4 ||
+ freq - 20 !=
+ ieee80211_ieee2mhz(ieee[i] - 4, flags[j]))
+ continue;
+ if (flags[j] & IEEE80211_CHAN_HT40U)
+ if (i == nieee - 1 ||
+ ieee[i] + 4 > ieee[nieee - 1] ||
+ freq + 20 !=
+ ieee80211_ieee2mhz(ieee[i] + 4, flags[j]))
+ continue;
+
+ if (j == 0) {
+ error = addchan(chans, maxchans, nchans,
+ ieee[i], freq, 0, flags[j]);
+ } else {
+ error = copychan_prev(chans, maxchans, nchans,
+ flags[j]);
+ }
+ if (error != 0)
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+int
+ieee80211_add_channel_list_2ghz(struct ieee80211_channel chans[], int maxchans,
+ int *nchans, const uint8_t ieee[], int nieee, const uint8_t bands[],
+ int ht40)
+{
+ uint32_t flags[IEEE80211_MODE_MAX];
+
+ getflags_2ghz(bands, flags, ht40);
+ KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__));
+
+ return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags));
+}
+
+int
+ieee80211_add_channel_list_5ghz(struct ieee80211_channel chans[], int maxchans,
+ int *nchans, const uint8_t ieee[], int nieee, const uint8_t bands[],
+ int ht40)
+{
+ uint32_t flags[IEEE80211_MODE_MAX];
+
+ getflags_5ghz(bands, flags, ht40);
+ KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__));
+
+ return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags));
+}
+
/*
* Locate a channel given a frequency+flags. We cache
* the previous lookup to optimize switching between two
@@ -920,7 +1370,6 @@ 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;
@@ -928,13 +1377,7 @@ ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags)
(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;
+ return (findchannel(ic->ic_channels, ic->ic_nchans, freq, flags));
}
/*
@@ -963,6 +1406,75 @@ ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags)
return NULL;
}
+/*
+ * Lookup a channel suitable for the given rx status.
+ *
+ * This is used to find a channel for a frame (eg beacon, probe
+ * response) based purely on the received PHY information.
+ *
+ * For now it tries to do it based on R_FREQ / R_IEEE.
+ * This is enough for 11bg and 11a (and thus 11ng/11na)
+ * but it will not be enough for GSM, PSB channels and the
+ * like. It also doesn't know about legacy-turbog and
+ * legacy-turbo modes, which some offload NICs actually
+ * support in weird ways.
+ *
+ * Takes the ic and rxstatus; returns the channel or NULL
+ * if not found.
+ *
+ * XXX TODO: Add support for that when the need arises.
+ */
+struct ieee80211_channel *
+ieee80211_lookup_channel_rxstatus(struct ieee80211vap *vap,
+ const struct ieee80211_rx_stats *rxs)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ uint32_t flags;
+ struct ieee80211_channel *c;
+
+ if (rxs == NULL)
+ return (NULL);
+
+ /*
+ * Strictly speaking we only use freq for now,
+ * however later on we may wish to just store
+ * the ieee for verification.
+ */
+ if ((rxs->r_flags & IEEE80211_R_FREQ) == 0)
+ return (NULL);
+ if ((rxs->r_flags & IEEE80211_R_IEEE) == 0)
+ return (NULL);
+
+ /*
+ * If the rx status contains a valid ieee/freq, then
+ * ensure we populate the correct channel information
+ * in rxchan before passing it up to the scan infrastructure.
+ * Offload NICs will pass up beacons from all channels
+ * during background scans.
+ */
+
+ /* Determine a band */
+ /* XXX should be done by the driver? */
+ if (rxs->c_freq < 3000) {
+ flags = IEEE80211_CHAN_G;
+ } else {
+ flags = IEEE80211_CHAN_A;
+ }
+
+ /* Channel lookup */
+ c = ieee80211_find_channel(ic, rxs->c_freq, flags);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_INPUT,
+ "%s: freq=%d, ieee=%d, flags=0x%08x; c=%p\n",
+ __func__,
+ (int) rxs->c_freq,
+ (int) rxs->c_ieee,
+ flags,
+ c);
+
+ return (c);
+}
+
static void
addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword)
{
@@ -1097,39 +1609,6 @@ ieee80211_media_setup(struct ieee80211com *ic,
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)
@@ -1141,7 +1620,6 @@ ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *
void
ieee80211_announce(struct ieee80211com *ic)
{
- struct ifnet *ifp = ic->ic_ifp;
int i, rate, mword;
enum ieee80211_phymode mode;
const struct ieee80211_rateset *rs;
@@ -1150,7 +1628,7 @@ ieee80211_announce(struct ieee80211com *ic)
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]);
+ ic_printf(ic, "%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);
@@ -1259,15 +1737,6 @@ media2mode(const struct ifmedia_entry *ime, uint32_t flags, uint16_t *mode)
}
/*
- * 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
@@ -1344,23 +1813,6 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
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)
{
@@ -1374,7 +1826,8 @@ ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
* 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) {
+ if (vap->iv_state == IEEE80211_S_RUN ||
+ vap->iv_state == IEEE80211_S_SLEEP) {
imr->ifm_status |= IFM_ACTIVE;
mode = ieee80211_chan2mode(ic->ic_curchan);
} else
@@ -1485,7 +1938,6 @@ findmedia(const struct ratemedia rates[], int n, u_int match)
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 },
@@ -1517,7 +1969,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m
{ 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 */
+ /* NB: OFDM72 doesn't really exist so we don't handle it */
};
static const struct ratemedia htrates[] = {
{ 0, IFM_IEEE80211_MCS },
@@ -1606,7 +2058,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m
if (mode == IEEE80211_MODE_11NA) {
if (rate & IEEE80211_RATE_MCS) {
rate &= ~IEEE80211_RATE_MCS;
- m = findmedia(htrates, N(htrates), rate);
+ m = findmedia(htrates, nitems(htrates), rate);
if (m != IFM_AUTO)
return m | IFM_IEEE80211_11NA;
}
@@ -1614,7 +2066,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m
/* 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);
+ m = findmedia(htrates, nitems(htrates), rate);
if (m != IFM_AUTO)
return m | IFM_IEEE80211_11NG;
}
@@ -1627,31 +2079,36 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m
case IEEE80211_MODE_11NA:
case IEEE80211_MODE_TURBO_A:
case IEEE80211_MODE_STURBO_A:
- return findmedia(rates, N(rates), rate | IFM_IEEE80211_11A);
+ return findmedia(rates, nitems(rates),
+ rate | IFM_IEEE80211_11A);
case IEEE80211_MODE_11B:
- return findmedia(rates, N(rates), rate | IFM_IEEE80211_11B);
+ return findmedia(rates, nitems(rates),
+ rate | IFM_IEEE80211_11B);
case IEEE80211_MODE_FH:
- return findmedia(rates, N(rates), rate | IFM_IEEE80211_FH);
+ return findmedia(rates, nitems(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),
+ return findmedia(rates, nitems(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 findmedia(rates, nitems(rates), rate | IFM_IEEE80211_11G);
+ case IEEE80211_MODE_VHT_2GHZ:
+ case IEEE80211_MODE_VHT_5GHZ:
+ /* XXX TODO: need to figure out mapping for VHT rates */
+ return IFM_AUTO;
}
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 */
@@ -1678,10 +2135,10 @@ ieee80211_media2rate(int mword)
9, /* IFM_IEEE80211_OFDM4 */
54, /* IFM_IEEE80211_OFDM27 */
-1, /* IFM_IEEE80211_MCS */
+ -1, /* IFM_IEEE80211_VHT */
};
- return IFM_SUBTYPE(mword) < N(ieeerates) ?
+ return IFM_SUBTYPE(mword) < nitems(ieeerates) ?
ieeerates[IFM_SUBTYPE(mword)] : 0;
-#undef N
}
/*
@@ -1719,3 +2176,25 @@ ieee80211_mac_hash(const struct ieee80211com *ic,
return c;
}
#undef mix
+
+char
+ieee80211_channel_type_char(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_VHT(c))
+ return 'v';
+ 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';
+}
diff --git a/freebsd/sys/net80211/ieee80211.h b/freebsd/sys/net80211/ieee80211.h
index 43d87b3a..aa2ddb09 100644
--- a/freebsd/sys/net80211/ieee80211.h
+++ b/freebsd/sys/net80211/ieee80211.h
@@ -36,6 +36,10 @@
/* is 802.11 address multicast/broadcast? */
#define IEEE80211_IS_MULTICAST(_a) (*(_a) & 0x01)
+#ifdef _KERNEL
+extern const uint8_t ieee80211broadcastaddr[];
+#endif
+
typedef uint16_t ieee80211_seq;
/* IEEE 802.11 PLCP header */
@@ -125,6 +129,7 @@ struct ieee80211_qosframe_addr4 {
#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_TIMING_ADV 0x60
#define IEEE80211_FC0_SUBTYPE_BEACON 0x80
#define IEEE80211_FC0_SUBTYPE_ATIM 0x90
#define IEEE80211_FC0_SUBTYPE_DISASSOC 0xa0
@@ -133,6 +138,7 @@ struct ieee80211_qosframe_addr4 {
#define IEEE80211_FC0_SUBTYPE_ACTION 0xd0
#define IEEE80211_FC0_SUBTYPE_ACTION_NOACK 0xe0
/* for TYPE_CTL */
+#define IEEE80211_FC0_SUBTYPE_CONTROL_WRAP 0x70
#define IEEE80211_FC0_SUBTYPE_BAR 0x80
#define IEEE80211_FC0_SUBTYPE_BA 0x90
#define IEEE80211_FC0_SUBTYPE_PS_POLL 0xa0
@@ -151,8 +157,21 @@ struct ieee80211_qosframe_addr4 {
#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_CFACK 0x90
+#define IEEE80211_FC0_SUBTYPE_QOS_CFPOLL 0xa0
+#define IEEE80211_FC0_SUBTYPE_QOS_CFACKPOLL 0xb0
#define IEEE80211_FC0_SUBTYPE_QOS_NULL 0xc0
+#define IEEE80211_IS_MGMT(wh) \
+ (!! (((wh)->i_fc[0] & IEEE80211_FC0_TYPE_MASK) \
+ == IEEE80211_FC0_TYPE_MGT))
+
+#define IEEE80211_FC0_QOSDATA \
+ (IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0)
+
+#define IEEE80211_IS_QOSDATA(wh) \
+ ((wh)->i_fc[0] == IEEE80211_FC0_QOSDATA)
+
#define IEEE80211_FC1_DIR_MASK 0x03
#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */
#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */
@@ -166,9 +185,14 @@ struct ieee80211_qosframe_addr4 {
#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_PROTECTED 0x40
#define IEEE80211_FC1_ORDER 0x80
+#define IEEE80211_HAS_SEQ(type, subtype) \
+ ((type) != IEEE80211_FC0_TYPE_CTL && \
+ !((type) == IEEE80211_FC0_TYPE_DATA && \
+ ((subtype) & IEEE80211_FC0_SUBTYPE_QOS_NULL) == \
+ IEEE80211_FC0_SUBTYPE_QOS_NULL))
#define IEEE80211_SEQ_FRAG_MASK 0x000f
#define IEEE80211_SEQ_FRAG_SHIFT 0
#define IEEE80211_SEQ_SEQ_MASK 0xfff0
@@ -188,6 +212,8 @@ struct ieee80211_qosframe_addr4 {
#define IEEE80211_NWID_LEN 32
#define IEEE80211_MESHID_LEN 32
+#define IEEE80211_QOS_CTL_LEN 2
+
#define IEEE80211_QOS_TXOP 0x00ff
/* bit 8 is reserved */
#define IEEE80211_QOS_AMSDU 0x80
@@ -199,6 +225,13 @@ struct ieee80211_qosframe_addr4 {
#define IEEE80211_QOS_EOSP 0x10 /* EndOfService Period*/
#define IEEE80211_QOS_EOSP_S 4
#define IEEE80211_QOS_TID 0x0f
+/* qos[1] byte used for all frames sent by mesh STAs in a mesh BSS */
+#define IEEE80211_QOS_MC 0x01 /* Mesh control */
+/* Mesh power save level*/
+#define IEEE80211_QOS_MESH_PSL 0x02
+/* Mesh Receiver Service Period Initiated */
+#define IEEE80211_QOS_RSPI 0x04
+/* bits 11 to 15 reserved */
/* does frame have QoS sequence control data */
#define IEEE80211_QOS_HAS_SEQ(wh) \
@@ -299,6 +332,27 @@ struct ieee80211_wme_param {
} __packed;
/*
+ * WME U-APSD qos info field defines
+ */
+#define WME_CAPINFO_UAPSD_EN 0x00000080
+#define WME_CAPINFO_UAPSD_VO 0x00000001
+#define WME_CAPINFO_UAPSD_VI 0x00000002
+#define WME_CAPINFO_UAPSD_BK 0x00000004
+#define WME_CAPINFO_UAPSD_BE 0x00000008
+#define WME_CAPINFO_UAPSD_ACFLAGS_SHIFT 0
+#define WME_CAPINFO_UAPSD_ACFLAGS_MASK 0xF
+#define WME_CAPINFO_UAPSD_MAXSP_SHIFT 5
+#define WME_CAPINFO_UAPSD_MAXSP_MASK 0x3
+#define WME_CAPINFO_IE_OFFSET 8
+#define WME_UAPSD_MAXSP(_qosinfo) \
+ (((_qosinfo) >> WME_CAPINFO_UAPSD_MAXSP_SHIFT) & \
+ WME_CAPINFO_UAPSD_MAXSP_MASK)
+#define WME_UAPSD_AC_ENABLED(_ac, _qosinfo) \
+ ((1 << (3 - (_ac))) & ( \
+ ((_qosinfo) >> WME_CAPINFO_UAPSD_ACFLAGS_SHIFT) & \
+ WME_CAPINFO_UAPSD_ACFLAGS_MASK))
+
+/*
* Management Notification Frame
*/
struct ieee80211_mnf {
@@ -325,6 +379,10 @@ struct ieee80211_action {
#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_MESH 13 /* Mesh */
+#define IEEE80211_ACTION_CAT_SELF_PROT 15 /* Self-protected */
+/* 16 - 125 reserved */
+#define IEEE80211_ACTION_CAT_VHT 21
#define IEEE80211_ACTION_CAT_VENDOR 127 /* Vendor Specific */
#define IEEE80211_ACTION_HT_TXCHWIDTH 0 /* recommended xmit chan width*/
@@ -674,6 +732,150 @@ struct ieee80211_ie_htinfo {
#define IEEE80211_HTINFO_BASIC_STBCMCS_S 0
#define IEEE80211_HTINFO_DUALPROTECTED 0x80
+
+/*
+ * 802.11ac definitions - 802.11ac-2013 .
+ */
+
+/*
+ * Maximum length of A-MPDU that the STA can RX in VHT.
+ * Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
+ */
+#define IEEE80211_VHTCAP_MAX_AMPDU_8K 0
+#define IEEE80211_VHTCAP_MAX_AMPDU_16K 1
+#define IEEE80211_VHTCAP_MAX_AMPDU_32K 2
+#define IEEE80211_VHTCAP_MAX_AMPDU_64K 3
+#define IEEE80211_VHTCAP_MAX_AMPDU_128K 4
+#define IEEE80211_VHTCAP_MAX_AMPDU_256K 5
+#define IEEE80211_VHTCAP_MAX_AMPDU_512K 6
+#define IEEE80211_VHTCAP_MAX_AMPDU_1024K 7
+
+/*
+ * VHT MCS information.
+ * + rx_highest/tx_highest: optional; maximum long GI VHT PPDU
+ * data rate. 1Mbit/sec units.
+ * + rx_mcs_map/tx_mcs_map: bitmap of per-stream supported MCS;
+ * 2 bits each.
+ */
+#define IEEE80211_VHT_MCS_SUPPORT_0_7 0 /* MCS0-7 */
+#define IEEE80211_VHT_MCS_SUPPORT_0_8 1 /* MCS0-8 */
+#define IEEE80211_VHT_MCS_SUPPORT_0_9 2 /* MCS0-9 */
+#define IEEE80211_VHT_MCS_NOT_SUPPORTED 3 /* not supported */
+
+struct ieee80211_vht_mcs_info {
+ uint16_t rx_mcs_map;
+ uint16_t rx_highest;
+ uint16_t tx_mcs_map;
+ uint16_t tx_highest;
+} __packed;
+
+/* VHT capabilities element: 802.11ac-2013 8.4.2.160 */
+struct ieee80211_ie_vhtcap {
+ uint8_t ie;
+ uint8_t len;
+ uint32_t vht_cap_info;
+ struct ieee80211_vht_mcs_info supp_mcs;
+} __packed;
+
+/* VHT operation mode subfields - 802.11ac-2013 Table 8.183x */
+#define IEEE80211_VHT_CHANWIDTH_USE_HT 0 /* Use HT IE for chw */
+#define IEEE80211_VHT_CHANWIDTH_80MHZ 1 /* 80MHz */
+#define IEEE80211_VHT_CHANWIDTH_160MHZ 2 /* 160MHz */
+#define IEEE80211_VHT_CHANWIDTH_80P80MHZ 3 /* 80+80MHz */
+
+/* VHT operation IE - 802.11ac-2013 8.4.2.161 */
+struct ieee80211_ie_vht_operation {
+ uint8_t ie;
+ uint8_t len;
+ uint8_t chan_width;
+ uint8_t center_freq_seg1_idx;
+ uint8_t center_freq_seg2_idx;
+ uint16_t basic_mcs_set;
+} __packed;
+
+/* 802.11ac VHT Capabilities */
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895 0x00000000
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_7991 0x00000001
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_11454 0x00000002
+#define IEEE80211_VHTCAP_MAX_MPDU_MASK 0x00000003
+#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004
+#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008
+#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK 0x0000000C
+#define IEEE80211_VHTCAP_RXLDPC 0x00000010
+#define IEEE80211_VHTCAP_SHORT_GI_80 0x00000020
+#define IEEE80211_VHTCAP_SHORT_GI_160 0x00000040
+#define IEEE80211_VHTCAP_TXSTBC 0x00000080
+#define IEEE80211_VHTCAP_RXSTBC_1 0x00000100
+#define IEEE80211_VHTCAP_RXSTBC_2 0x00000200
+#define IEEE80211_VHTCAP_RXSTBC_3 0x00000300
+#define IEEE80211_VHTCAP_RXSTBC_4 0x00000400
+#define IEEE80211_VHTCAP_RXSTBC_MASK 0x00000700
+#define IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE 0x00000800
+#define IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE 0x00001000
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT 13
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK \
+ (7 << IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT)
+#define IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_SHIFT 16
+#define IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK \
+ (7 << IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_SHIFT)
+#define IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE 0x00080000
+#define IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE 0x00100000
+#define IEEE80211_VHTCAP_VHT_TXOP_PS 0x00200000
+#define IEEE80211_VHTCAP_HTC_VHT 0x00400000
+#define IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT 23
+#define IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK \
+ (7 << IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT)
+#define IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB 0x08000000
+#define IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB 0x0c000000
+#define IEEE80211_VHTCAP_RX_ANTENNA_PATTERN 0x10000000
+#define IEEE80211_VHTCAP_TX_ANTENNA_PATTERN 0x20000000
+
+/*
+ * XXX TODO: add the rest of the bits
+ */
+#define IEEE80211_VHTCAP_BITS \
+ "\20\1MPDU7991\2MPDU11454\3CHAN160\4CHAN8080\5RXLDPC\6SHORTGI80" \
+ "\7SHORTGI160\10RXSTBC1\11RXSTBC2\12RXSTBC3\13RXSTBC4\14BFERCAP" \
+ "\15BFEECAP\27VHT\37RXANTPTN\40TXANTPTN"
+
+/*
+ * VHT Transmit Power Envelope element - 802.11ac-2013 8.4.2.164
+ *
+ * This defines the maximum transmit power for various bandwidths.
+ */
+/*
+ * Count is how many elements follow and what they're for:
+ *
+ * 0 - 20 MHz
+ * 1 - 20+40 MHz
+ * 2 - 20+40+80 MHz
+ * 3 - 20+40+80+(160, 80+80) MHz
+ */
+#define IEEE80211_VHT_TXPWRENV_INFO_COUNT_SHIFT 0
+#define IEEE80211_VHT_TXPWRENV_INFO_COUNT_MASK 0x07
+
+/*
+ * Unit is the tx power representation. It should be EIRP for now;
+ * other values are reserved.
+ */
+#define IEEE80211_VHT_TXPWRENV_UNIT_MASK 0x38
+#define IEEE80211_VHT_TXPWRENV_UNIT_SHIFT 3
+
+/* This value is within the unit mask/shift above */
+#define IEEE80211_VHT_TXPWRENV_UNIT_EIRP 0
+
+struct ieee80211_ie_vht_txpwrenv {
+ uint8_t ie;
+ uint8_t len;
+ uint8_t tx_info;
+ int8_t tx_elem[0]; /* TX power elements, 1/2 dB, signed */
+};
+
+/* VHT action codes */
+#define WLAN_ACTION_VHT_COMPRESSED_BF 0
+#define WLAN_ACTION_VHT_GROUPID_MGMT 1
+#define WLAN_ACTION_VHT_OPMODE_NOTIF 2
+
/*
* Management information element payloads.
*/
@@ -687,6 +889,9 @@ enum {
IEEE80211_ELEMID_TIM = 5,
IEEE80211_ELEMID_IBSSPARMS = 6,
IEEE80211_ELEMID_COUNTRY = 7,
+ IEEE80211_ELEMID_BSSLOAD = 11,
+ IEEE80211_ELEMID_TSPEC = 13,
+ IEEE80211_ELEMID_TCLAS = 14,
IEEE80211_ELEMID_CHALLENGE = 16,
/* 17-31 reserved for challenge text extension */
IEEE80211_ELEMID_PWRCNSTR = 32,
@@ -704,7 +909,19 @@ enum {
IEEE80211_ELEMID_QOS = 46,
IEEE80211_ELEMID_RSN = 48,
IEEE80211_ELEMID_XRATES = 50,
+ IEEE80211_ELEMID_APCHANREP = 51,
IEEE80211_ELEMID_HTINFO = 61,
+ IEEE80211_ELEMID_SECCHAN_OFFSET = 62,
+ IEEE80211_ELEMID_RRM_ENACAPS = 70,
+ IEEE80211_ELEMID_MULTIBSSID = 71,
+ IEEE80211_ELEMID_COEX_2040 = 72,
+ IEEE80211_ELEMID_INTOL_CHN_REPORT = 73,
+ IEEE80211_ELEMID_OVERLAP_BSS_SCAN_PARAM = 74,
+ IEEE80211_ELEMID_TSF_REQ = 91,
+ IEEE80211_ELEMID_TSF_RESP = 92,
+ IEEE80211_ELEMID_WNM_SLEEP_MODE = 93,
+ IEEE80211_ELEMID_TIM_BCAST_REQ = 94,
+ IEEE80211_ELEMID_TIM_BCAST_RESP = 95,
IEEE80211_ELEMID_TPC = 150,
IEEE80211_ELEMID_CCKM = 156,
IEEE80211_ELEMID_VENDOR = 221, /* vendor private */
@@ -725,9 +942,10 @@ enum {
IEEE80211_ELEMID_MESHAWAKEW = 119,
IEEE80211_ELEMID_MESHBEACONT = 120,
/* 121-124 MMCAOP not implemented yet */
- IEEE80211_ELEMID_MESHPANN = 125, /* XXX: is GANN now, not used */
+ IEEE80211_ELEMID_MESHGANN = 125,
IEEE80211_ELEMID_MESHRANN = 126,
/* 127 Extended Capabilities */
+ IEEE80211_ELEMID_EXTCAP = 127,
/* 128-129 reserved */
IEEE80211_ELEMID_MESHPREQ = 130,
IEEE80211_ELEMID_MESHPREP = 131,
@@ -736,6 +954,11 @@ enum {
IEEE80211_ELEMID_MESHPXU = 137,
IEEE80211_ELEMID_MESHPXUC = 138,
IEEE80211_ELEMID_MESHAH = 60, /* XXX: remove */
+
+ /* 802.11ac */
+ IEEE80211_ELEMID_VHT_CAP = 191,
+ IEEE80211_ELEMID_VHT_OPMODE = 192,
+ IEEE80211_ELEMID_VHT_PWR_ENV = 195,
};
struct ieee80211_tim_ie {
@@ -762,6 +985,90 @@ struct ieee80211_country_ie {
#define IEEE80211_COUNTRY_MAX_SIZE \
(sizeof(struct ieee80211_country_ie) + 3*(IEEE80211_COUNTRY_MAX_BANDS-1))
+struct ieee80211_bss_load_ie {
+ uint8_t ie;
+ uint8_t len;
+ uint16_t sta_count; /* station count */
+ uint8_t chan_load; /* channel utilization */
+ uint8_t aac; /* available admission capacity */
+} __packed;
+
+struct ieee80211_ap_chan_report_ie {
+ uint8_t ie;
+ uint8_t len;
+ uint8_t i_class; /* operating class */
+ /* Annex E, E.1 Country information and operating classes */
+ uint8_t chan_list[0];
+} __packed;
+
+#define IEEE80211_EXTCAP_CMS (1ULL << 0) /* 20/40 BSS coexistence management support */
+#define IEEE80211_EXTCAP_RSVD_1 (1ULL << 1)
+#define IEEE80211_EXTCAP_ECS (1ULL << 2) /* extended channel switching */
+#define IEEE80211_EXTCAP_RSVD_3 (1ULL << 3)
+#define IEEE80211_EXTCAP_PSMP_CAP (1ULL << 4) /* PSMP capability */
+#define IEEE80211_EXTCAP_RSVD_5 (1ULL << 5)
+#define IEEE80211_EXTCAP_S_PSMP_SUPP (1ULL << 6)
+#define IEEE80211_EXTCAP_EVENT (1ULL << 7)
+#define IEEE80211_EXTCAP_DIAGNOSTICS (1ULL << 8)
+#define IEEE80211_EXTCAP_MCAST_DIAG (1ULL << 9)
+#define IEEE80211_EXTCAP_LOC_TRACKING (1ULL << 10)
+#define IEEE80211_EXTCAP_FMS (1ULL << 11)
+#define IEEE80211_EXTCAP_PROXY_ARP (1ULL << 12)
+#define IEEE80211_EXTCAP_CIR (1ULL << 13) /* collocated interference reporting */
+#define IEEE80211_EXTCAP_CIVIC_LOC (1ULL << 14)
+#define IEEE80211_EXTCAP_GEOSPATIAL_LOC (1ULL << 15)
+#define IEEE80211_EXTCAP_TFS (1ULL << 16)
+#define IEEE80211_EXTCAP_WNM_SLEEPMODE (1ULL << 17)
+#define IEEE80211_EXTCAP_TIM_BROADCAST (1ULL << 18)
+#define IEEE80211_EXTCAP_BSS_TRANSITION (1ULL << 19)
+#define IEEE80211_EXTCAP_QOS_TRAF_CAP (1ULL << 20)
+#define IEEE80211_EXTCAP_AC_STA_COUNT (1ULL << 21)
+#define IEEE80211_EXTCAP_M_BSSID (1ULL << 22) /* multiple BSSID field */
+#define IEEE80211_EXTCAP_TIMING_MEAS (1ULL << 23)
+#define IEEE80211_EXTCAP_CHAN_USAGE (1ULL << 24)
+#define IEEE80211_EXTCAP_SSID_LIST (1ULL << 25)
+#define IEEE80211_EXTCAP_DMS (1ULL << 26)
+#define IEEE80211_EXTCAP_UTC_TSF_OFFSET (1ULL << 27)
+#define IEEE80211_EXTCAP_TLDS_BUF_STA_SUPP (1ULL << 28) /* TDLS peer U-APSP buffer STA support */
+#define IEEE80211_EXTCAP_TLDS_PPSM_SUPP (1ULL << 29) /* TDLS peer PSM support */
+#define IEEE80211_EXTCAP_TLDS_CH_SW (1ULL << 30) /* TDLS channel switching */
+#define IEEE80211_EXTCAP_INTERWORKING (1ULL << 31)
+#define IEEE80211_EXTCAP_QOSMAP (1ULL << 32)
+#define IEEE80211_EXTCAP_EBR (1ULL << 33)
+#define IEEE80211_EXTCAP_SSPN_IF (1ULL << 34)
+#define IEEE80211_EXTCAP_RSVD_35 (1ULL << 35)
+#define IEEE80211_EXTCAP_MSGCF_CAP (1ULL << 36)
+#define IEEE80211_EXTCAP_TLDS_SUPP (1ULL << 37)
+#define IEEE80211_EXTCAP_TLDS_PROHIB (1ULL << 38)
+#define IEEE80211_EXTCAP_TLDS_CH_SW_PROHIB (1ULL << 39) /* TDLS channel switching prohibited */
+#define IEEE80211_EXTCAP_RUF (1ULL << 40) /* reject unadmitted frame */
+/* service interval granularity */
+#define IEEE80211_EXTCAP_SIG \
+ ((1ULL << 41) | (1ULL << 42) | (1ULL << 43))
+#define IEEE80211_EXTCAP_ID_LOC (1ULL << 44)
+#define IEEE80211_EXTCAP_U_APSD_COEX (1ULL << 45)
+#define IEEE80211_EXTCAP_WNM_NOTIFICATION (1ULL << 46)
+#define IEEE80211_EXTCAP_RSVD_47 (1ULL << 47)
+#define IEEE80211_EXTCAP_SSID (1ULL << 48) /* UTF-8 SSID */
+/* bits 49-n are reserved */
+
+struct ieee80211_extcap_ie {
+ uint8_t ie;
+ uint8_t len;
+} __packed;
+
+/*
+ * 802.11h Quiet Time Element.
+ */
+struct ieee80211_quiet_ie {
+ uint8_t quiet_ie; /* IEEE80211_ELEMID_QUIET */
+ uint8_t len;
+ uint8_t tbttcount; /* quiet start */
+ uint8_t period; /* beacon intervals between quiets */
+ uint16_t duration; /* TUs of each quiet*/
+ uint16_t offset; /* TUs of from TBTT of quiet start */
+} __packed;
+
/*
* 802.11h Channel Switch Announcement (CSA).
*/
@@ -784,8 +1091,9 @@ struct ieee80211_csa_ie {
/* rate set entries are in .5 Mb/s units, and potentially marked as basic */
#define IEEE80211_RATE_BASIC 0x80
#define IEEE80211_RATE_VAL 0x7f
+#define IEEE80211_RV(v) ((v) & IEEE80211_RATE_VAL)
-/* EPR information element flags */
+/* ERP information element flags */
#define IEEE80211_ERP_NON_ERP_PRESENT 0x01
#define IEEE80211_ERP_USE_PROTECTION 0x02
#define IEEE80211_ERP_LONG_PREAMBLE 0x04
@@ -921,19 +1229,21 @@ enum {
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_REASON_PEER_LINK_CANCELED = 52, /* 11s */
+ IEEE80211_REASON_MESH_MAX_PEERS = 53, /* 11s */
+ IEEE80211_REASON_MESH_CPVIOLATION = 54, /* 11s */
+ IEEE80211_REASON_MESH_CLOSE_RCVD = 55, /* 11s */
+ IEEE80211_REASON_MESH_MAX_RETRIES = 56, /* 11s */
+ IEEE80211_REASON_MESH_CONFIRM_TIMEOUT = 57, /* 11s */
+ IEEE80211_REASON_MESH_INVALID_GTK = 58, /* 11s */
+ IEEE80211_REASON_MESH_INCONS_PARAMS = 59, /* 11s */
+ IEEE80211_REASON_MESH_INVALID_SECURITY = 60, /* 11s */
+ IEEE80211_REASON_MESH_PERR_NO_PROXY = 61, /* 11s */
+ IEEE80211_REASON_MESH_PERR_NO_FI = 62, /* 11s */
+ IEEE80211_REASON_MESH_PERR_DEST_UNREACH = 63, /* 11s */
+ IEEE80211_REASON_MESH_MAC_ALRDY_EXISTS_MBSS = 64, /* 11s */
+ IEEE80211_REASON_MESH_CHAN_SWITCH_REG = 65, /* 11s */
+ IEEE80211_REASON_MESH_CHAN_SWITCH_UNSPEC = 66, /* 11s */
IEEE80211_STATUS_SUCCESS = 0,
IEEE80211_STATUS_UNSPECIFIED = 1,
diff --git a/freebsd/sys/net80211/ieee80211_acl.c b/freebsd/sys/net80211/ieee80211_acl.c
index 3bfb0a30..d9997a84 100644
--- a/freebsd/sys/net80211/ieee80211_acl.c
+++ b/freebsd/sys/net80211/ieee80211_acl.c
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/queue.h>
@@ -101,8 +102,8 @@ acl_attach(struct ieee80211vap *vap)
{
struct aclstate *as;
- as = (struct aclstate *) malloc(sizeof(struct aclstate),
- M_80211_ACL, M_NOWAIT | M_ZERO);
+ as = (struct aclstate *) IEEE80211_MALLOC(sizeof(struct aclstate),
+ M_80211_ACL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (as == NULL)
return 0;
ACL_LOCK_INIT(as, "acl");
@@ -125,7 +126,7 @@ acl_detach(struct ieee80211vap *vap)
acl_free_all(vap);
vap->iv_as = NULL;
ACL_LOCK_DESTROY(as);
- free(as, M_80211_ACL);
+ IEEE80211_FREE(as, M_80211_ACL);
}
static __inline struct acl *
@@ -149,12 +150,12 @@ _acl_free(struct aclstate *as, struct acl *acl)
TAILQ_REMOVE(&as->as_list, acl, acl_list);
LIST_REMOVE(acl, acl_hash);
- free(acl, M_80211_ACL);
+ IEEE80211_FREE(acl, M_80211_ACL);
as->as_nacls--;
}
static int
-acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_check(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
{
struct aclstate *as = vap->iv_as;
@@ -163,9 +164,9 @@ acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
case ACL_POLICY_RADIUS:
return 1;
case ACL_POLICY_ALLOW:
- return _find_acl(as, mac) != NULL;
+ return _find_acl(as, wh->i_addr2) != NULL;
case ACL_POLICY_DENY:
- return _find_acl(as, mac) == NULL;
+ return _find_acl(as, wh->i_addr2) == NULL;
}
return 0; /* should not happen */
}
@@ -177,7 +178,8 @@ acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
struct acl *acl, *new;
int hash;
- new = (struct acl *) malloc(sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
+ new = (struct acl *) IEEE80211_MALLOC(sizeof(struct acl),
+ M_80211_ACL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (new == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s failed, no memory\n", ether_sprintf(mac));
@@ -190,7 +192,7 @@ acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
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_FREE(new, M_80211_ACL);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s failed, already present\n",
ether_sprintf(mac));
@@ -304,8 +306,8 @@ acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
ireq->i_len = space; /* return required space */
return 0; /* NB: must not error */
}
- ap = (struct ieee80211req_maclist *) malloc(space,
- M_TEMP, M_NOWAIT);
+ ap = (struct ieee80211req_maclist *) IEEE80211_MALLOC(space,
+ M_TEMP, IEEE80211_M_NOWAIT);
if (ap == NULL)
return ENOMEM;
i = 0;
@@ -320,7 +322,7 @@ acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
ireq->i_len = space;
} else
error = copyout(ap, ireq->i_data, ireq->i_len);
- free(ap, M_TEMP);
+ IEEE80211_FREE(ap, M_TEMP);
return error;
}
return EINVAL;
diff --git a/freebsd/sys/net80211/ieee80211_action.c b/freebsd/sys/net80211/ieee80211_action.c
index 1135895b..6c1f1b91 100644
--- a/freebsd/sys/net80211/ieee80211_action.c
+++ b/freebsd/sys/net80211/ieee80211_action.c
@@ -39,11 +39,13 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
@@ -69,10 +71,8 @@ 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] = {
+static ieee80211_send_action_func *meshaction_send_action[12] = {
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,
};
@@ -81,44 +81,46 @@ static ieee80211_send_action_func *vendor_send_action[8] = {
send_inval, send_inval, send_inval, send_inval,
};
+static ieee80211_send_action_func *vht_send_action[3] = {
+ 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))
+ if (act >= nitems(ba_send_action))
break;
ba_send_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_HT:
- if (act >= N(ht_send_action))
+ if (act >= nitems(ht_send_action))
break;
ht_send_action[act] = f;
return 0;
- case IEEE80211_ACTION_CAT_MESHPEERING:
- if (act >= N(meshpl_send_action))
+ case IEEE80211_ACTION_CAT_SELF_PROT:
+ if (act >= nitems(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))
+ case IEEE80211_ACTION_CAT_MESH:
+ if (act >= nitems(meshaction_send_action))
break;
- hwmp_send_action[act] = f;
+ meshaction_send_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_VENDOR:
- if (act >= N(vendor_send_action))
+ if (act >= nitems(vendor_send_action))
break;
vendor_send_action[act] = f;
return 0;
+ case IEEE80211_ACTION_CAT_VHT:
+ if (act >= nitems(vht_send_action))
+ break;
+ vht_send_action[act] = f;
+ return 0;
}
return EINVAL;
-#undef N
}
void
@@ -130,37 +132,35 @@ ieee80211_send_action_unregister(int cat, int act)
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))
+ if (act < nitems(ba_send_action))
f = ba_send_action[act];
break;
case IEEE80211_ACTION_CAT_HT:
- if (act < N(ht_send_action))
+ if (act < nitems(ht_send_action))
f = ht_send_action[act];
break;
- case IEEE80211_ACTION_CAT_MESHPEERING:
- if (act < N(meshpl_send_action))
+ case IEEE80211_ACTION_CAT_SELF_PROT:
+ if (act < nitems(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];
+ case IEEE80211_ACTION_CAT_MESH:
+ if (act < nitems(meshaction_send_action))
+ f = meshaction_send_action[act];
break;
case IEEE80211_ACTION_CAT_VENDOR:
- if (act < N(vendor_send_action))
+ if (act < nitems(vendor_send_action))
f = vendor_send_action[act];
break;
+ case IEEE80211_ACTION_CAT_VHT:
+ if (act < nitems(vht_send_action))
+ f = vht_send_action[act];
+ break;
}
return f(ni, cat, act, sa);
-#undef N
}
static int
@@ -182,10 +182,8 @@ 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] = {
+static ieee80211_recv_action_func *meshaction_recv_action[12] = {
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,
};
@@ -194,44 +192,46 @@ static ieee80211_recv_action_func *vendor_recv_action[8] = {
recv_inval, recv_inval, recv_inval, recv_inval,
};
+static ieee80211_recv_action_func *vht_recv_action[3] = {
+ 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))
+ if (act >= nitems(ba_recv_action))
break;
ba_recv_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_HT:
- if (act >= N(ht_recv_action))
+ if (act >= nitems(ht_recv_action))
break;
ht_recv_action[act] = f;
return 0;
- case IEEE80211_ACTION_CAT_MESHPEERING:
- if (act >= N(meshpl_recv_action))
+ case IEEE80211_ACTION_CAT_SELF_PROT:
+ if (act >= nitems(meshpl_recv_action))
break;
meshpl_recv_action[act] = f;
return 0;
- case IEEE80211_ACTION_CAT_MESHLMETRIC:
- if (act >= N(meshlm_recv_action))
+ case IEEE80211_ACTION_CAT_MESH:
+ if (act >= nitems(meshaction_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;
+ meshaction_recv_action[act] = f;
return 0;
case IEEE80211_ACTION_CAT_VENDOR:
- if (act >= N(vendor_recv_action))
+ if (act >= nitems(vendor_recv_action))
break;
vendor_recv_action[act] = f;
return 0;
+ case IEEE80211_ACTION_CAT_VHT:
+ if (act >= nitems(vht_recv_action))
+ break;
+ vht_recv_action[act] = f;
+ return 0;
}
return EINVAL;
-#undef N
}
void
@@ -245,37 +245,45 @@ 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;
+ struct ieee80211vap *vap = ni->ni_vap;
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))
+ if (ia->ia_action < nitems(ba_recv_action))
f = ba_recv_action[ia->ia_action];
break;
case IEEE80211_ACTION_CAT_HT:
- if (ia->ia_action < N(ht_recv_action))
+ if (ia->ia_action < nitems(ht_recv_action))
f = ht_recv_action[ia->ia_action];
break;
- case IEEE80211_ACTION_CAT_MESHPEERING:
- if (ia->ia_action < N(meshpl_recv_action))
+ case IEEE80211_ACTION_CAT_SELF_PROT:
+ if (ia->ia_action < nitems(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];
+ case IEEE80211_ACTION_CAT_MESH:
+ if (ni == vap->iv_bss ||
+ ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
+ ni->ni_macaddr, NULL,
+ "peer link not yet established (%d), cat %s act %u",
+ ni->ni_mlstate, "mesh action", ia->ia_action);
+ vap->iv_stats.is_mesh_nolink++;
+ break;
+ }
+ if (ia->ia_action < nitems(meshaction_recv_action))
+ f = meshaction_recv_action[ia->ia_action];
break;
case IEEE80211_ACTION_CAT_VENDOR:
- if (ia->ia_action < N(vendor_recv_action))
+ if (ia->ia_action < nitems(vendor_recv_action))
f = vendor_recv_action[ia->ia_action];
break;
+ case IEEE80211_ACTION_CAT_VHT:
+ if (ia->ia_action < nitems(vht_recv_action))
+ f = vht_recv_action[ia->ia_action];
+ break;
}
return f(ni, wh, frm, efrm);
-#undef N
}
diff --git a/freebsd/sys/net80211/ieee80211_adhoc.c b/freebsd/sys/net80211/ieee80211_adhoc.c
index 4c330976..834c84cb 100644
--- a/freebsd/sys/net80211/ieee80211_adhoc.c
+++ b/freebsd/sys/net80211/ieee80211_adhoc.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/if_llc.h>
#include <net/ethernet.h>
@@ -65,16 +66,18 @@ __FBSDID("$FreeBSD$");
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
+#include <net80211/ieee80211_sta.h>
#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 int adhoc_input(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_rx_stats *, int, int);
static void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *,
- int subtype, int, int);
+ int subtype, const struct ieee80211_rx_stats *, int, int);
static void ahdemo_recv_mgmt(struct ieee80211_node *, struct mbuf *,
- int subtype, int, int);
+ int subtype, const struct ieee80211_rx_stats *rxs, int, int);
static void adhoc_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype);
void
@@ -119,9 +122,9 @@ adhoc_vattach(struct ieee80211vap *vap)
static void
sta_leave(void *arg, struct ieee80211_node *ni)
{
- struct ieee80211vap *vap = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
- if (ni->ni_vap == vap && ni != vap->iv_bss)
+ if (ni != vap->iv_bss)
ieee80211_node_leave(ni);
}
@@ -163,7 +166,8 @@ adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
switch (ostate) {
case IEEE80211_S_RUN: /* beacon miss */
/* purge station table; entries are stale */
- ieee80211_iterate_nodes(&ic->ic_sta, sta_leave, vap);
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap,
+ sta_leave, NULL);
/* fall thru... */
case IEEE80211_S_INIT:
if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
@@ -172,7 +176,9 @@ adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* Already have a channel; bypass the
* scan and startup immediately.
*/
- ieee80211_create_ibss(vap, vap->iv_des_chan);
+ ieee80211_create_ibss(vap,
+ ieee80211_ht_adjust_channel(ic,
+ vap->iv_des_chan, vap->iv_flags_ht));
break;
}
/*
@@ -212,6 +218,19 @@ adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
/* 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 */
+ ni = vap->iv_bss;
+ break;
case IEEE80211_S_SCAN:
#ifdef IEEE80211_DEBUG
if (ieee80211_msg_debug(vap)) {
@@ -227,6 +246,8 @@ adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
}
#endif
break;
+ case IEEE80211_S_RUN: /* IBSS merge */
+ break;
default:
goto invalid;
}
@@ -244,7 +265,7 @@ adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
ic->ic_newassoc(ni, ostate != IEEE80211_S_RUN);
break;
case IEEE80211_S_SLEEP:
- ieee80211_sta_pwrsave(vap, 0);
+ vap->iv_sta_ps(vap, 0);
break;
default:
invalid:
@@ -285,9 +306,9 @@ doprint(struct ieee80211vap *vap, int subtype)
* by the 802.11 layer.
*/
static int
-adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
+adhoc_input(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
-#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;
@@ -297,7 +318,16 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
int hdrspace, need_tap = 1; /* mbuf need to be tapped. */
uint8_t dir, type, subtype, qos;
uint8_t *bssid;
- uint16_t rxseq;
+ int is_hw_decrypted = 0;
+ int has_decrypted = 0;
+
+ /*
+ * Some devices do hardware decryption all the way through
+ * to pretending the frame wasn't encrypted in the first place.
+ * So, tag it appropriately so it isn't discarded inappropriately.
+ */
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED))
+ is_hw_decrypted = 1;
if (m->m_flags & M_AMPDU_MPDU) {
/*
@@ -367,7 +397,10 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
/*
* Validate the bssid.
*/
- if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
+ if (!(type == IEEE80211_FC0_TYPE_MGT &&
+ (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
+ subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) &&
+ !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,
@@ -396,8 +429,12 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
goto err;
}
/*
- * Fake up a node for this newly
- * discovered member of the IBSS.
+ * Fake up a node for this newly discovered member
+ * of the IBSS.
+ *
+ * Note: This doesn't "upgrade" the node to 11n;
+ * that will happen after a probe request/response
+ * exchange.
*/
ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2);
if (ni == NULL) {
@@ -407,29 +444,14 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
}
IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
ni->ni_noise = nf;
- if (HAS_SEQ(type)) {
+ if (IEEE80211_HAS_SEQ(type, subtype) &&
+ IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
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 (! ieee80211_check_rxseq(ni, wh)) {
- /* 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);
+ if (! ieee80211_check_rxseq(ni, wh, bssid))
goto out;
- }
- ni->ni_rxseqs[tid] = rxseq;
}
}
@@ -473,7 +495,7 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
* crypto cipher modules used to do delayed update
* of replay sequence numbers.
*/
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (is_hw_decrypted || wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
/*
* Discard encrypted frames when privacy is off.
@@ -484,14 +506,14 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
IEEE80211_NODE_STAT(ni, rx_noprivacy);
goto out;
}
- key = ieee80211_crypto_decap(ni, m, hdrspace);
- if (key == NULL) {
+ if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
/* 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;
+ wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+ has_decrypted = 1;
} else {
/* XXX M_WEP and IEEE80211_F_PRIVACY */
key = NULL;
@@ -522,7 +544,7 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
/*
* Next strip any MSDU crypto bits.
*/
- if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ if (!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++;
@@ -576,7 +598,8 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
* any non-PAE frames received without encryption.
*/
if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
- (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) &&
+ (is_hw_decrypted == 0) &&
eh->ether_type != htons(ETHERTYPE_PAE)) {
/*
* Drop unencrypted frames.
@@ -624,18 +647,17 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
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],
+ ieee80211_mgt_subtype_name(subtype),
ether_sprintf(wh->i_addr2), rssi);
}
#endif
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
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);
+ vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
goto out;
case IEEE80211_FC0_TYPE_CTL:
@@ -651,7 +673,7 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
break;
}
err:
- ifp->if_ierrors++;
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
out:
if (m != NULL) {
if (need_tap && ieee80211_radiotap_active_vap(vap))
@@ -680,13 +702,17 @@ is11bclient(const uint8_t *rates, const uint8_t *xrates)
static void
adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
- int subtype, int rssi, int nf)
+ int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_channel *rxchan = ic->ic_curchan;
struct ieee80211_frame *wh;
- uint8_t *frm, *efrm, *sfrm;
+ uint8_t *frm, *efrm;
uint8_t *ssid, *rates, *xrates;
+#if 0
+ int ht_state_change = 0;
+#endif
wh = mtod(m0, struct ieee80211_frame *);
frm = (uint8_t *)&wh[1];
@@ -695,11 +721,17 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
case IEEE80211_FC0_SUBTYPE_BEACON: {
struct ieee80211_scanparams scan;
+ struct ieee80211_channel *c;
/*
* We process beacon/probe response
* frames to discover neighbors.
*/
- if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ if (rxs != NULL) {
+ c = ieee80211_lookup_channel_rxstatus(vap, rxs);
+ if (c != NULL)
+ rxchan = c;
+ }
+ if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0)
return;
/*
* Count frame now that we know it's to be processed.
@@ -725,21 +757,56 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
ieee80211_probe_curchan(vap, 1);
ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
}
- ieee80211_add_scan(vap, &scan, wh, subtype, rssi, nf);
+ ieee80211_add_scan(vap, rxchan, &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.
+ *
+ * XXX TODO:
+ *
+ * Here we're not scanning; so if we have an
+ * SSID then make sure it matches our SSID.
+ * Otherwise this code will match on all IBSS
+ * beacons/probe requests for all SSIDs,
+ * filling the node table with nodes that
+ * aren't ours.
*/
- ni = ieee80211_add_neighbor(vap, wh, &scan);
+ if (ieee80211_ibss_node_check_new(ni, &scan)) {
+ ni = ieee80211_add_neighbor(vap, wh, &scan);
+ /*
+ * Send a probe request so we announce 11n
+ * capabilities.
+ */
+ ieee80211_send_probereq(ni, /* node */
+ vap->iv_myaddr, /* SA */
+ ni->ni_macaddr, /* DA */
+ vap->iv_bss->ni_bssid, /* BSSID */
+ vap->iv_bss->ni_essid,
+ vap->iv_bss->ni_esslen); /* SSID */
+ } else
+ ni = NULL;
+
} else if (ni->ni_capinfo == 0) {
/*
* Update faked node created on transmit.
* Note this also updates the tsf.
*/
ieee80211_init_neighbor(ni, wh, &scan);
+
+ /*
+ * Send a probe request so we announce 11n
+ * capabilities.
+ */
+ ieee80211_send_probereq(ni, /* node */
+ vap->iv_myaddr, /* SA */
+ ni->ni_macaddr, /* DA */
+ vap->iv_bss->ni_bssid, /* BSSID */
+ vap->iv_bss->ni_essid,
+ vap->iv_bss->ni_esslen); /* SSID */
} else {
/*
* Record tsf for potential resync.
@@ -747,10 +814,42 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
memcpy(ni->ni_tstamp.data, scan.tstamp,
sizeof(ni->ni_tstamp));
}
+ /*
+ * This isn't enabled yet - otherwise it would
+ * update the HT parameters and channel width
+ * from any node, which could lead to lots of
+ * strange behaviour if the 11n nodes aren't
+ * exactly configured to match.
+ */
+#if 0
+ if (scan.htcap != NULL && scan.htinfo != NULL &&
+ (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
+ if (ieee80211_ht_updateparams(ni,
+ scan.htcap, scan.htinfo))
+ ht_state_change = 1;
+ }
+#endif
if (ni != NULL) {
IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
ni->ni_noise = nf;
}
+ /*
+ * Same here - the channel width change should
+ * be applied to the specific peer node, not
+ * to the ic. Ie, the interface configuration
+ * should stay in its current channel width;
+ * but it should change the rate control and
+ * any queued frames for the given node only.
+ *
+ * Since there's no (current) way to inform
+ * the driver that a channel width change has
+ * occurred for a single node, just stub this
+ * out.
+ */
+#if 0
+ if (ht_state_change)
+ ieee80211_update_chw(ic);
+#endif
}
break;
}
@@ -778,7 +877,6 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
* [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) {
@@ -819,11 +917,18 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
*/
ieee80211_send_proberesp(vap, wh->i_addr2,
is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
+
+ /*
+ * Note: we don't benefit from stashing the probe request
+ * IEs away to use for IBSS negotiation, because we
+ * typically don't get all of the IEs.
+ */
break;
case IEEE80211_FC0_SUBTYPE_ACTION:
case IEEE80211_FC0_SUBTYPE_ACTION_NOACK:
- if (ni == vap->iv_bss) {
+ if ((ni == vap->iv_bss) &&
+ !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, NULL, "%s", "unknown node");
vap->iv_stats.is_rx_mgtdiscard++;
@@ -847,6 +952,7 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
case IEEE80211_FC0_SUBTYPE_ATIM:
case IEEE80211_FC0_SUBTYPE_DISASSOC:
case IEEE80211_FC0_SUBTYPE_AUTH:
@@ -868,7 +974,7 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
static void
ahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
- int subtype, int rssi, int nf)
+ int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
@@ -879,7 +985,7 @@ ahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
* a site-survey.
*/
if (ic->ic_flags & IEEE80211_F_SCAN)
- adhoc_recv_mgmt(ni, m0, subtype, rssi, nf);
+ adhoc_recv_mgmt(ni, m0, subtype, rxs, rssi, nf);
else {
wh = mtod(m0, struct ieee80211_frame *);
switch (subtype) {
@@ -889,6 +995,7 @@ ahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
case IEEE80211_FC0_SUBTYPE_BEACON:
case IEEE80211_FC0_SUBTYPE_ATIM:
case IEEE80211_FC0_SUBTYPE_DISASSOC:
diff --git a/freebsd/sys/net80211/ieee80211_ageq.c b/freebsd/sys/net80211/ieee80211_ageq.c
index 2c6f0475..2d6bd89e 100644
--- a/freebsd/sys/net80211/ieee80211_ageq.c
+++ b/freebsd/sys/net80211/ieee80211_ageq.c
@@ -36,10 +36,12 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
diff --git a/freebsd/sys/net80211/ieee80211_alq.c b/freebsd/sys/net80211/ieee80211_alq.c
new file mode 100644
index 00000000..b0df55d2
--- /dev/null
+++ b/freebsd/sys/net80211/ieee80211_alq.c
@@ -0,0 +1,158 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2011 Adrian Chadd, Xenion Lty Ltd
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * net80211 fast-logging support, primarily for debugging.
+ *
+ * This implements a single debugging queue which includes
+ * per-device enumeration where needed.
+ */
+
+#include <rtems/bsd/local/opt_wlan.h>
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/ucred.h>
+#include <sys/alq.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_freebsd.h>
+#include <net80211/ieee80211_alq.h>
+
+static struct alq *ieee80211_alq;
+static int ieee80211_alq_lost;
+static int ieee80211_alq_logged;
+static char ieee80211_alq_logfile[MAXPATHLEN] = "/tmp/net80211.log";
+static unsigned int ieee80211_alq_qsize = 64*1024;
+
+static int
+ieee80211_alq_setlogging(int enable)
+{
+ int error;
+
+ if (enable) {
+ if (ieee80211_alq)
+ alq_close(ieee80211_alq);
+
+ error = alq_open(&ieee80211_alq,
+ ieee80211_alq_logfile,
+ curthread->td_ucred,
+ ALQ_DEFAULT_CMODE,
+ sizeof (struct ieee80211_alq_rec),
+ ieee80211_alq_qsize);
+ ieee80211_alq_lost = 0;
+ ieee80211_alq_logged = 0;
+ printf("net80211: logging to %s enabled; "
+ "struct size %d bytes\n",
+ ieee80211_alq_logfile,
+ sizeof(struct ieee80211_alq_rec));
+ } else {
+ if (ieee80211_alq)
+ alq_close(ieee80211_alq);
+ ieee80211_alq = NULL;
+ printf("net80211: logging disabled\n");
+ error = 0;
+ }
+ return (error);
+}
+
+static int
+sysctl_ieee80211_alq_log(SYSCTL_HANDLER_ARGS)
+{
+ int error, enable;
+
+ enable = (ieee80211_alq != NULL);
+ error = sysctl_handle_int(oidp, &enable, 0, req);
+ if (error || !req->newptr)
+ return (error);
+ else
+ return (ieee80211_alq_setlogging(enable));
+}
+
+SYSCTL_PROC(_net_wlan, OID_AUTO, alq, CTLTYPE_INT|CTLFLAG_RW,
+ 0, 0, sysctl_ieee80211_alq_log, "I", "Enable net80211 alq logging");
+SYSCTL_INT(_net_wlan, OID_AUTO, alq_size, CTLFLAG_RW,
+ &ieee80211_alq_qsize, 0, "In-memory log size (#records)");
+SYSCTL_INT(_net_wlan, OID_AUTO, alq_lost, CTLFLAG_RW,
+ &ieee80211_alq_lost, 0, "Debugging operations not logged");
+SYSCTL_INT(_net_wlan, OID_AUTO, alq_logged, CTLFLAG_RW,
+ &ieee80211_alq_logged, 0, "Debugging operations logged");
+
+static struct ale *
+ieee80211_alq_get(void)
+{
+ struct ale *ale;
+
+ ale = alq_get(ieee80211_alq, ALQ_NOWAIT);
+ if (!ale)
+ ieee80211_alq_lost++;
+ else
+ ieee80211_alq_logged++;
+ return ale;
+}
+
+void
+ieee80211_alq_log(struct ieee80211vap *vap, uint8_t op, u_char *p, int l)
+{
+ struct ale *ale;
+ struct ieee80211_alq_rec *r;
+
+ if (ieee80211_alq == NULL)
+ return;
+
+ ale = ieee80211_alq_get();
+ if (! ale)
+ return;
+
+ r = (struct ieee80211_alq_rec *) ale->ae_data;
+ r->r_timestamp = htonl(ticks);
+ r->r_version = 1;
+ r->r_wlan = htons(vap->iv_ifp->if_dunit);
+ r->r_op = op;
+ r->r_threadid = htonl((uint32_t) curthread->td_tid);
+ memcpy(&r->r_payload, p, MIN(l, sizeof(r->r_payload)));
+ alq_post(ieee80211_alq, ale);
+}
diff --git a/freebsd/sys/net80211/ieee80211_alq.h b/freebsd/sys/net80211/ieee80211_alq.h
new file mode 100644
index 00000000..7537e933
--- /dev/null
+++ b/freebsd/sys/net80211/ieee80211_alq.h
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2011 Adrian Chadd, Xenion Lty Ltd
+ * 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 __IEEE80211_ALQ_H__
+#define __IEEE80211_ALQ_H__
+
+#define IEEE80211_ALQ_MAX_PAYLOAD 1024
+
+/*
+ * timestamp
+ * wlan interface
+ * operation
+ * sub-operation
+ * rest of structure - operation specific
+ */
+
+#define IEEE80211_ALQ_SRC_NET80211 0x0001
+/* Drivers define their own numbers above 0xff */
+
+struct ieee80211_alq_rec {
+ uint64_t r_timestamp; /* XXX may wrap! */
+ uint32_t r_threadid; /* current thread id */
+ uint16_t r_wlan; /* wlan interface number */
+ uint16_t r_src; /* source - driver, net80211 */
+ uint32_t r_flags; /* flags */
+ uint32_t r_op; /* top-level operation id */
+ uint32_t r_len; /* length of hdr + payload */
+ /* Operation payload follows here */
+};
+
+/* General logging function */
+extern int ieee80211_alq_log(struct ieee80211com *ic,
+ struct ieee80211vap *vap, uint32_t op, uint32_t flags,
+ uint16_t srcid, const uint8_t *src, size_t len);
+
+#endif /* __IEEE80211_ALQ_H__ */
diff --git a/freebsd/sys/net80211/ieee80211_amrr.c b/freebsd/sys/net80211/ieee80211_amrr.c
index b453c2f4..865bcaf9 100644
--- a/freebsd/sys/net80211/ieee80211_amrr.c
+++ b/freebsd/sys/net80211/ieee80211_amrr.c
@@ -35,12 +35,16 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/module.h>
+#include <sys/sbuf.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
+#include <net/ethernet.h>
#ifdef INET
#include <netinet/in.h>
@@ -48,6 +52,7 @@ __FBSDID("$FreeBSD$");
#endif
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_ht.h>
#include <net80211/ieee80211_amrr.h>
#include <net80211/ieee80211_ratectl.h>
@@ -66,13 +71,14 @@ 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_tx_complete(const struct ieee80211_node *,
+ const struct ieee80211_ratectl_tx_status *);
+static void amrr_tx_update_cb(void *, struct ieee80211_node *);
+static void amrr_tx_update(struct ieee80211vap *vap,
+ struct ieee80211_ratectl_tx_stats *);
static void amrr_sysctlattach(struct ieee80211vap *,
struct sysctl_ctx_list *, struct sysctl_oid *);
+static void amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
/* number of references from net80211 layer */
static int nrefs = 0;
@@ -89,6 +95,7 @@ static const struct ieee80211_ratectl amrr = {
.ir_tx_complete = amrr_tx_complete,
.ir_tx_update = amrr_tx_update,
.ir_setinterval = amrr_setinterval,
+ .ir_node_stats = amrr_node_stats,
};
IEEE80211_RATECTL_MODULE(amrr, 1);
IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
@@ -112,8 +119,8 @@ amrr_init(struct ieee80211vap *vap)
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);
+ amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
+ M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (amrr == NULL) {
if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
return;
@@ -127,20 +134,40 @@ amrr_init(struct ieee80211vap *vap)
static void
amrr_deinit(struct ieee80211vap *vap)
{
- free(vap->iv_rs, M_80211_RATECTL);
+ IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
+}
+
+/*
+ * Return whether 11n rates are possible.
+ *
+ * Some 11n devices may return HT information but no HT rates.
+ * Thus, we shouldn't treat them as an 11n node.
+ */
+static int
+amrr_node_is_11n(struct ieee80211_node *ni)
+{
+
+ if (ni->ni_chan == NULL)
+ return (0);
+ if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+ return (0);
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0)
+ return (0);
+ return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
}
static void
amrr_node_init(struct ieee80211_node *ni)
{
- const struct ieee80211_rateset *rs = &ni->ni_rates;
+ const struct ieee80211_rateset *rs = NULL;
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_amrr *amrr = vap->iv_rs;
struct ieee80211_amrr_node *amn;
+ uint8_t rate;
if (ni->ni_rctls == NULL) {
- ni->ni_rctls = amn = malloc(sizeof(struct ieee80211_amrr_node),
- M_80211_RATECTL, M_NOWAIT|M_ZERO);
+ ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
+ M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (amn == NULL) {
if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
"structure\n");
@@ -154,22 +181,60 @@ amrr_node_init(struct ieee80211_node *ni)
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;
+ /* 11n or not? Pick the right rateset */
+ if (amrr_node_is_11n(ni)) {
+ /* XXX ew */
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: 11n node", __func__);
+ rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+ } else {
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: non-11n node", __func__);
+ rs = &ni->ni_rates;
+ }
+
+ /* Initial rate - lowest */
+ rate = rs->rs_rates[0];
+
+ /* XXX clear the basic rate flag if it's not 11n */
+ if (! amrr_node_is_11n(ni))
+ rate &= IEEE80211_RATE_VAL;
+
+ /* pick initial rate from the rateset - HT or otherwise */
+ /* Pick something low that's likely to succeed */
+ for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
+ amn->amn_rix--) {
+ /* legacy - anything < 36mbit, stop searching */
+ /* 11n - stop at MCS4 */
+ if (amrr_node_is_11n(ni)) {
+ if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
+ break;
+ } else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
+ break;
+ }
+ rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+
+ /* if the rate is an 11n rate, ensure the MCS bit is set */
+ if (amrr_node_is_11n(ni))
+ rate |= IEEE80211_RATE_MCS;
+
+ /* Assign initial rate from the rateset */
+ ni->ni_txrate = rate;
amn->amn_ticks = ticks;
+ /* XXX TODO: we really need a rate-to-string method */
+ /* XXX TODO: non-11n rate should be divided by two.. */
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
- "AMRR initial rate %d", ni->ni_txrate);
+ "AMRR: nrates=%d, initial rate %s%d",
+ rs->rs_nrates,
+ amrr_node_is_11n(ni) ? "MCS " : "",
+ rate & IEEE80211_RATE_VAL);
}
static void
amrr_node_deinit(struct ieee80211_node *ni)
{
- free(ni->ni_rctls, M_80211_RATECTL);
+ IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
}
static int
@@ -177,19 +242,46 @@ amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
struct ieee80211_node *ni)
{
int rix = amn->amn_rix;
+ const struct ieee80211_rateset *rs = NULL;
KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
+ /* 11n or not? Pick the right rateset */
+ if (amrr_node_is_11n(ni)) {
+ /* XXX ew */
+ rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+ } else {
+ rs = &ni->ni_rates;
+ }
+
+ /* XXX TODO: we really need a rate-to-string method */
+ /* XXX TODO: non-11n rate should be divided by two.. */
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
+ rs->rs_rates[rix] & IEEE80211_RATE_VAL,
+ amn->amn_txcnt,
+ amn->amn_retrycnt);
+
+ /*
+ * XXX This is totally bogus for 11n, as although high MCS
+ * rates for each stream may be failing, the next stream
+ * should be checked.
+ *
+ * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
+ * MCS23, we should skip 6/7 and try 8 onwards.
+ */
if (is_success(amn)) {
amn->amn_success++;
if (amn->amn_success >= amn->amn_success_threshold &&
- rix + 1 < ni->ni_rates.rs_nrates) {
+ rix + 1 < rs->rs_nrates) {
amn->amn_recovery = 1;
amn->amn_success = 0;
rix++;
+ /* XXX TODO: we really need a rate-to-string method */
+ /* XXX TODO: non-11n rate should be divided by two.. */
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,
+ rs->rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
} else {
amn->amn_recovery = 0;
@@ -208,9 +300,11 @@ amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
amrr->amrr_min_success_threshold;
}
rix--;
+ /* XXX TODO: we really need a rate-to-string method */
+ /* XXX TODO: non-11n rate should be divided by two.. */
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,
+ rs->rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
}
amn->amn_recovery = 0;
@@ -233,14 +327,27 @@ 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;
+ const struct ieee80211_rateset *rs = NULL;
int rix;
+ /* 11n or not? Pick the right rateset */
+ if (amrr_node_is_11n(ni)) {
+ /* XXX ew */
+ rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+ } else {
+ rs = &ni->ni_rates;
+ }
+
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;
+ ni->ni_txrate = rs->rs_rates[rix];
+ /* XXX strip basic rate flag from txrate, if non-11n */
+ if (amrr_node_is_11n(ni))
+ ni->ni_txrate |= IEEE80211_RATE_MCS;
+ else
+ ni->ni_txrate &= IEEE80211_RATE_VAL;
amn->amn_rix = rix;
}
amn->amn_ticks = ticks;
@@ -255,34 +362,56 @@ amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
* 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)
+amrr_tx_complete(const struct ieee80211_node *ni,
+ const struct ieee80211_ratectl_tx_status *status)
{
struct ieee80211_amrr_node *amn = ni->ni_rctls;
- int retries = *(int *)arg1;
+ int retries;
+
+ retries = 0;
+ if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY)
+ retries = status->long_retries;
amn->amn_txcnt++;
- if (ok)
+ if (status->status == IEEE80211_RATECTL_TX_SUCCESS)
amn->amn_success++;
amn->amn_retrycnt += retries;
}
+static void
+amrr_tx_update_cb(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211_ratectl_tx_stats *stats = arg;
+ struct ieee80211_amrr_node *amn = ni->ni_rctls;
+ int txcnt, success, retrycnt;
+
+ txcnt = stats->nframes;
+ success = stats->nsuccess;
+ retrycnt = 0;
+ if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES)
+ retrycnt = stats->nretries;
+
+ amn->amn_txcnt += txcnt;
+ amn->amn_success += success;
+ amn->amn_retrycnt += retrycnt;
+}
+
/*
* 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)
+amrr_tx_update(struct ieee80211vap *vap,
+ struct ieee80211_ratectl_tx_stats *stats)
{
- 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;
+ if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE)
+ amrr_tx_update_cb(stats, stats->ni);
+ else {
+ ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap,
+ amrr_tx_update_cb, stats);
+ }
}
static int
@@ -317,3 +446,31 @@ amrr_sysctlattach(struct ieee80211vap *vap,
"amrr_min_sucess_threshold", CTLFLAG_RW,
&amrr->amrr_min_success_threshold, 0, "");
}
+
+static void
+amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
+{
+ int rate;
+ struct ieee80211_amrr_node *amn = ni->ni_rctls;
+ struct ieee80211_rateset *rs;
+
+ /* XXX TODO: check locking? */
+
+ /* XXX TODO: this should be a method */
+ if (amrr_node_is_11n(ni)) {
+ rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+ rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+ sbuf_printf(s, "rate: MCS %d\n", rate);
+ } else {
+ rs = &ni->ni_rates;
+ rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+ sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
+ }
+
+ sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
+ sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
+ sbuf_printf(s, "success: %u\n", amn->amn_success);
+ sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
+ sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
+ sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
+}
diff --git a/freebsd/sys/net80211/ieee80211_crypto.c b/freebsd/sys/net80211/ieee80211_crypto.c
index bbd82274..9dd6eb21 100644
--- a/freebsd/sys/net80211/ieee80211_crypto.c
+++ b/freebsd/sys/net80211/ieee80211_crypto.c
@@ -80,7 +80,7 @@ null_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k,
return 0;
*keyix = 0; /* NB: use key index 0 for ucast key */
} else {
- *keyix = k - vap->iv_nw_keys;
+ *keyix = ieee80211_crypto_get_key_wepidx(vap, k);
}
*rxkeyix = IEEE80211_KEYIX_NONE; /* XXX maybe *keyix? */
return 1;
@@ -91,8 +91,7 @@ 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])
+null_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
return 1;
}
@@ -134,7 +133,7 @@ dev_key_delete(struct ieee80211vap *vap,
static __inline int
dev_key_set(struct ieee80211vap *vap, const struct ieee80211_key *key)
{
- return vap->iv_key_set(vap, key, key->wk_macaddr);
+ return vap->iv_key_set(vap, key);
}
/*
@@ -524,16 +523,40 @@ ieee80211_crypto_setkey(struct ieee80211vap *vap, struct ieee80211_key *key)
}
/*
- * Add privacy headers appropriate for the specified key.
+ * Return index if the key is a WEP key (0..3); -1 otherwise.
+ *
+ * This is different to "get_keyid" which defaults to returning
+ * 0 for unicast keys; it assumes that it won't be used for WEP.
*/
+int
+ieee80211_crypto_get_key_wepidx(const struct ieee80211vap *vap,
+ const struct ieee80211_key *k)
+{
+
+ if (k >= &vap->iv_nw_keys[0] &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])
+ return (k - vap->iv_nw_keys);
+ return (-1);
+}
+
+/*
+ * Note: only supports a single unicast key (0).
+ */
+uint8_t
+ieee80211_crypto_get_keyid(struct ieee80211vap *vap, struct ieee80211_key *k)
+{
+ if (k >= &vap->iv_nw_keys[0] &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])
+ return (k - vap->iv_nw_keys);
+ else
+ return (0);
+}
+
struct ieee80211_key *
-ieee80211_crypto_encap(struct ieee80211_node *ni, struct mbuf *m)
+ieee80211_crypto_get_txkey(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.
@@ -552,22 +575,36 @@ ieee80211_crypto_encap(struct ieee80211_node *ni, struct mbuf *m)
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;
+ return &vap->iv_nw_keys[vap->iv_def_txkey];
}
- cip = k->wk_cipher;
- return (cip->ic_encap(k, m, keyid<<6) ? k : NULL);
+
+ return &ni->ni_ucastkey;
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+struct ieee80211_key *
+ieee80211_crypto_encap(struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ieee80211_key *k;
+ const struct ieee80211_cipher *cip;
+
+ if ((k = ieee80211_crypto_get_txkey(ni, m)) != NULL) {
+ cip = k->wk_cipher;
+ return (cip->ic_encap(k, m) ? k : NULL);
+ }
+
+ return 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)
+int
+ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen,
+ struct ieee80211_key **key)
{
#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
#define IEEE80211_WEP_MINLEN \
@@ -576,16 +613,38 @@ ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_key *k;
struct ieee80211_frame *wh;
+ const struct ieee80211_rx_stats *rxs;
const struct ieee80211_cipher *cip;
uint8_t keyid;
+ /*
+ * Check for hardware decryption and IV stripping.
+ * If the IV is stripped then we definitely can't find a key.
+ * Set the key to NULL but return true; upper layers
+ * will need to handle a NULL key for a successful
+ * decrypt.
+ */
+ rxs = ieee80211_get_rx_params_ptr(m);
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED)) {
+ if (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) {
+ /*
+ * Hardware decrypted, IV stripped.
+ * We can't find a key with a stripped IV.
+ * Return successful.
+ */
+ *key = NULL;
+ return (1);
+ }
+ }
+
/* 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;
+ *key = NULL;
+ return (0);
}
/*
@@ -611,14 +670,83 @@ ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
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;
+ *key = NULL;
+ return (0);
+ }
+
+ /*
+ * Attempt decryption.
+ *
+ * If we fail then don't return the key - return NULL
+ * and an error.
+ */
+ if (cip->ic_decap(k, m, hdrlen)) {
+ /* success */
+ *key = k;
+ return (1);
}
- return (cip->ic_decap(k, m, hdrlen) ? k : NULL);
+ /* Failure */
+ *key = NULL;
+ return (0);
#undef IEEE80211_WEP_MINLEN
#undef IEEE80211_WEP_HDRLEN
}
+/*
+ * Check and remove any MIC.
+ */
+int
+ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k,
+ struct mbuf *m, int force)
+{
+ const struct ieee80211_cipher *cip;
+ const struct ieee80211_rx_stats *rxs;
+ struct ieee80211_frame *wh;
+
+ rxs = ieee80211_get_rx_params_ptr(m);
+ wh = mtod(m, struct ieee80211_frame *);
+
+ /*
+ * Handle demic / mic errors from hardware-decrypted offload devices.
+ */
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED)) {
+ if (rxs->c_pktflags & IEEE80211_RX_F_FAIL_MIC) {
+ /*
+ * Hardware has said MIC failed. We don't care about
+ * whether it was stripped or not.
+ *
+ * Eventually - teach the demic methods in crypto
+ * modules to handle a NULL key and not to dereference
+ * it.
+ */
+ ieee80211_notify_michael_failure(vap, wh, -1);
+ return (0);
+ }
+
+ if (rxs->c_pktflags & IEEE80211_RX_F_MMIC_STRIP) {
+ /*
+ * Hardware has decrypted and not indicated a
+ * MIC failure and has stripped the MIC.
+ * We may not have a key, so for now just
+ * return OK.
+ */
+ return (1);
+ }
+ }
+
+ /*
+ * If we don't have a key at this point then we don't
+ * have to demic anything.
+ */
+ if (k == NULL)
+ return (1);
+
+ cip = k->wk_cipher;
+ return (cip->ic_miclen > 0 ? cip->ic_demic(k, m, force) : 1);
+}
+
+
static void
load_ucastkey(void *arg, struct ieee80211_node *ni)
{
@@ -661,3 +789,18 @@ ieee80211_crypto_reload_keys(struct ieee80211com *ic)
*/
ieee80211_iterate_nodes(&ic->ic_sta, load_ucastkey, NULL);
}
+
+/*
+ * Set the default key index for WEP, or KEYIX_NONE for no default TX key.
+ *
+ * This should be done as part of a key update block (iv_key_update_begin /
+ * iv_key_update_end.)
+ */
+void
+ieee80211_crypto_set_deftxkey(struct ieee80211vap *vap, ieee80211_keyix kid)
+{
+
+ /* XXX TODO: assert we're in a key update block */
+
+ vap->iv_update_deftxkey(vap, kid);
+}
diff --git a/freebsd/sys/net80211/ieee80211_crypto.h b/freebsd/sys/net80211/ieee80211_crypto.h
index d7ac436f..3cbceec3 100644
--- a/freebsd/sys/net80211/ieee80211_crypto.h
+++ b/freebsd/sys/net80211/ieee80211_crypto.h
@@ -73,19 +73,25 @@ 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_NOREPLAY 0x0008 /* ignore replay failures */
-#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 */
+ uint8_t wk_pad; /* .. some drivers use this. Fix that. */
+ uint8_t wk_pad1[2];
+ uint32_t wk_flags;
+#define IEEE80211_KEY_XMIT 0x00000001 /* key used for xmit */
+#define IEEE80211_KEY_RECV 0x00000002 /* key used for recv */
+#define IEEE80211_KEY_GROUP 0x00000004 /* key used for WPA group operation */
+#define IEEE80211_KEY_NOREPLAY 0x00000008 /* ignore replay failures */
+#define IEEE80211_KEY_SWENCRYPT 0x00000010 /* host-based encrypt */
+#define IEEE80211_KEY_SWDECRYPT 0x00000020 /* host-based decrypt */
+#define IEEE80211_KEY_SWENMIC 0x00000040 /* host-based enmic */
+#define IEEE80211_KEY_SWDEMIC 0x00000080 /* host-based demic */
+#define IEEE80211_KEY_DEVKEY 0x00000100 /* device key request completed */
+#define IEEE80211_KEY_CIPHER0 0x00001000 /* cipher-specific action 0 */
+#define IEEE80211_KEY_CIPHER1 0x00002000 /* cipher-specific action 1 */
+#define IEEE80211_KEY_NOIV 0x00004000 /* don't insert IV/MIC for !mgmt */
+#define IEEE80211_KEY_NOIVMGT 0x00008000 /* don't insert IV/MIC for mgmt */
+#define IEEE80211_KEY_NOMIC 0x00010000 /* don't insert MIC for !mgmt */
+#define IEEE80211_KEY_NOMICMGT 0x00020000 /* don't insert MIC for mgmt */
+
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];
@@ -101,13 +107,16 @@ struct ieee80211_key {
#define IEEE80211_KEY_COMMON /* common flags passed in by apps */\
(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP | \
IEEE80211_KEY_NOREPLAY)
-#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_DEVICE /* flags owned by device driver */\
+ (IEEE80211_KEY_DEVKEY|IEEE80211_KEY_CIPHER0|IEEE80211_KEY_CIPHER1| \
+ IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC|IEEE80211_KEY_NOIV | \
+ IEEE80211_KEY_NOIVMGT|IEEE80211_KEY_NOMIC|IEEE80211_KEY_NOMICMGT)
+
#define IEEE80211_KEY_BITS \
"\20\1XMIT\2RECV\3GROUP\4SWENCRYPT\5SWDECRYPT\6SWENMIC\7SWDEMIC" \
"\10DEVKEY\11CIPHER0\12CIPHER1"
@@ -162,6 +171,8 @@ int ieee80211_crypto_delkey(struct ieee80211vap *,
int ieee80211_crypto_setkey(struct ieee80211vap *, struct ieee80211_key *);
void ieee80211_crypto_delglobalkeys(struct ieee80211vap *);
void ieee80211_crypto_reload_keys(struct ieee80211com *);
+void ieee80211_crypto_set_deftxkey(struct ieee80211vap *,
+ ieee80211_keyix kid);
/*
* Template for a supported cipher. Ciphers register with the
@@ -178,8 +189,8 @@ struct ieee80211_cipher {
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);
+ void (*ic_setiv)(struct ieee80211_key *, uint8_t *);
+ int (*ic_encap)(struct ieee80211_key *, struct mbuf *);
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);
@@ -193,22 +204,18 @@ void ieee80211_crypto_register(const struct ieee80211_cipher *);
void ieee80211_crypto_unregister(const struct ieee80211_cipher *);
int ieee80211_crypto_available(u_int cipher);
+int ieee80211_crypto_get_key_wepidx(const struct ieee80211vap *,
+ const struct ieee80211_key *k);
+uint8_t ieee80211_crypto_get_keyid(struct ieee80211vap *vap,
+ struct ieee80211_key *k);
+struct ieee80211_key *ieee80211_crypto_get_txkey(struct ieee80211_node *,
+ struct mbuf *);
struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211_node *,
struct mbuf *);
-struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211_node *,
+int ieee80211_crypto_decap(struct ieee80211_node *,
+ struct mbuf *, int, struct ieee80211_key **);
+int ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k,
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.
*/
diff --git a/freebsd/sys/net80211/ieee80211_crypto_ccmp.c b/freebsd/sys/net80211/ieee80211_crypto_ccmp.c
index 13843744..bc3929e5 100644
--- a/freebsd/sys/net80211/ieee80211_crypto_ccmp.c
+++ b/freebsd/sys/net80211/ieee80211_crypto_ccmp.c
@@ -65,7 +65,8 @@ struct ccmp_ctx {
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 void ccmp_setiv(struct ieee80211_key *, uint8_t *);
+static int ccmp_encap(struct ieee80211_key *, struct mbuf *);
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);
@@ -80,6 +81,7 @@ static const struct ieee80211_cipher ccmp = {
.ic_attach = ccmp_attach,
.ic_detach = ccmp_detach,
.ic_setkey = ccmp_setkey,
+ .ic_setiv = ccmp_setiv,
.ic_encap = ccmp_encap,
.ic_decap = ccmp_decap,
.ic_enmic = ccmp_enmic,
@@ -98,8 +100,8 @@ 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);
+ ctx = (struct ccmp_ctx *) IEEE80211_MALLOC(sizeof(struct ccmp_ctx),
+ M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (ctx == NULL) {
vap->iv_stats.is_crypto_nomem++;
return NULL;
@@ -115,7 +117,7 @@ ccmp_detach(struct ieee80211_key *k)
{
struct ccmp_ctx *ctx = k->wk_private;
- free(ctx, M_80211_CRYPTO);
+ IEEE80211_FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -136,18 +138,53 @@ ccmp_setkey(struct ieee80211_key *k)
return 1;
}
+static void
+ccmp_setiv(struct ieee80211_key *k, uint8_t *ivp)
+{
+ struct ccmp_ctx *ctx = k->wk_private;
+ struct ieee80211vap *vap = ctx->cc_vap;
+ uint8_t keyid;
+
+ keyid = ieee80211_crypto_get_keyid(vap, k) << 6;
+
+ k->wk_keytsc++;
+ 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 */
+}
+
/*
* Add privacy headers appropriate for the specified key.
*/
static int
-ccmp_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
+ccmp_encap(struct ieee80211_key *k, struct mbuf *m)
{
+ const struct ieee80211_frame *wh;
struct ccmp_ctx *ctx = k->wk_private;
struct ieee80211com *ic = ctx->cc_ic;
uint8_t *ivp;
int hdrlen;
+ int is_mgmt;
hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
+ wh = mtod(m, const struct ieee80211_frame *);
+ is_mgmt = IEEE80211_IS_MGMT(wh);
+
+ /*
+ * Check to see if we need to insert IV/MIC.
+ *
+ * Some offload devices don't require the IV to be inserted
+ * as part of the hardware encryption.
+ */
+ if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT))
+ return 1;
+ if ((! is_mgmt) && (k->wk_flags & IEEE80211_KEY_NOIV))
+ return 1;
/*
* Copy down 802.11 header and add the IV, KeyID, and ExtIV.
@@ -159,18 +196,10 @@ ccmp_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
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 */
+ ccmp_setiv(k, ivp);
/*
- * Finally, do software encrypt if neeed.
+ * Finally, do software encrypt if needed.
*/
if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&
!ccmp_encrypt(k, m, hdrlen))
@@ -205,12 +234,18 @@ READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
static int
ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
+ const struct ieee80211_rx_stats *rxs;
struct ccmp_ctx *ctx = k->wk_private;
struct ieee80211vap *vap = ctx->cc_vap;
struct ieee80211_frame *wh;
uint8_t *ivp, tid;
uint64_t pn;
+ rxs = ieee80211_get_rx_params_ptr(m);
+
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))
+ goto finish;
+
/*
* Header should have extended IV and sequence number;
* verify the former and validate the latter.
@@ -249,17 +284,28 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
!ccmp_decrypt(k, pn, m, hdrlen))
return 0;
+finish:
/*
* 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);
+ if (! ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))) {
+ ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header,
+ hdrlen);
+ m_adj(m, ccmp.ic_header);
+ }
+
+ /*
+ * XXX TODO: see if MMIC_STRIP also covers CCMP MIC trailer.
+ */
+ if (! ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MMIC_STRIP)))
+ m_adj(m, -ccmp.ic_trailer);
/*
* Ok to update rsc now.
*/
- k->wk_keyrsc[tid] = pn;
+ if (! ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))) {
+ k->wk_keyrsc[tid] = pn;
+ }
return 1;
}
diff --git a/freebsd/sys/net80211/ieee80211_crypto_none.c b/freebsd/sys/net80211/ieee80211_crypto_none.c
index c172c5ce..625b4b2b 100644
--- a/freebsd/sys/net80211/ieee80211_crypto_none.c
+++ b/freebsd/sys/net80211/ieee80211_crypto_none.c
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/module.h>
@@ -50,7 +51,8 @@ __FBSDID("$FreeBSD$");
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 void none_setiv(struct ieee80211_key *, uint8_t *);
+static int none_encap(struct ieee80211_key *, struct mbuf *);
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);
@@ -64,6 +66,7 @@ const struct ieee80211_cipher ieee80211_cipher_none = {
.ic_attach = none_attach,
.ic_detach = none_detach,
.ic_setkey = none_setkey,
+ .ic_setiv = none_setiv,
.ic_encap = none_encap,
.ic_decap = none_decap,
.ic_enmic = none_enmic,
@@ -89,20 +92,28 @@ none_setkey(struct ieee80211_key *k)
return 1;
}
+static void
+none_setiv(struct ieee80211_key *k, uint8_t *ivp)
+{
+}
+
static int
-none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
+none_encap(struct ieee80211_key *k, struct mbuf *m)
{
struct ieee80211vap *vap = k->wk_private;
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
-#endif
+ uint8_t keyid;
+
+ keyid = ieee80211_crypto_get_keyid(vap, k);
/*
* 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);
+ "key id %u is not set (encap)", keyid);
+#endif
vap->iv_stats.is_tx_badcipher++;
return 0;
}
diff --git a/freebsd/sys/net80211/ieee80211_crypto_tkip.c b/freebsd/sys/net80211/ieee80211_crypto_tkip.c
index 9bc51743..b4eb8838 100644
--- a/freebsd/sys/net80211/ieee80211_crypto_tkip.c
+++ b/freebsd/sys/net80211/ieee80211_crypto_tkip.c
@@ -56,7 +56,8 @@ __FBSDID("$FreeBSD$");
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 void tkip_setiv(struct ieee80211_key *, uint8_t *);
+static int tkip_encap(struct ieee80211_key *, struct mbuf *);
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);
@@ -71,6 +72,7 @@ static const struct ieee80211_cipher tkip = {
.ic_attach = tkip_attach,
.ic_detach = tkip_detach,
.ic_setkey = tkip_setkey,
+ .ic_setiv = tkip_setiv,
.ic_encap = tkip_encap,
.ic_decap = tkip_decap,
.ic_enmic = tkip_enmic,
@@ -86,7 +88,6 @@ 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];
@@ -111,8 +112,8 @@ 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);
+ ctx = (struct tkip_ctx *) IEEE80211_MALLOC(sizeof(struct tkip_ctx),
+ M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (ctx == NULL) {
vap->iv_stats.is_crypto_nomem++;
return NULL;
@@ -128,7 +129,7 @@ tkip_detach(struct ieee80211_key *k)
{
struct tkip_ctx *ctx = k->wk_private;
- free(ctx, M_80211_CRYPTO);
+ IEEE80211_FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -145,22 +146,46 @@ tkip_setkey(struct ieee80211_key *k)
__func__, k->wk_keylen, 128/NBBY);
return 0;
}
- k->wk_keytsc = 1; /* TSC starts at 1 */
ctx->rx_phase1_done = 0;
return 1;
}
+static void
+tkip_setiv(struct ieee80211_key *k, uint8_t *ivp)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+ struct ieee80211vap *vap = ctx->tc_vap;
+ uint8_t keyid;
+
+ keyid = ieee80211_crypto_get_keyid(vap, k) << 6;
+
+ k->wk_keytsc++;
+ 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 */
+}
+
/*
* Add privacy headers and do any s/w encryption required.
*/
static int
-tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
+tkip_encap(struct ieee80211_key *k, struct mbuf *m)
{
struct tkip_ctx *ctx = k->wk_private;
struct ieee80211vap *vap = ctx->tc_vap;
struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_frame *wh;
uint8_t *ivp;
int hdrlen;
+ int is_mgmt;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ is_mgmt = IEEE80211_IS_MGMT(wh);
/*
* Handle TKIP counter measures requirement.
@@ -175,6 +200,16 @@ tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
vap->iv_stats.is_crypto_tkipcm++;
return 0;
}
+
+ /*
+ * Check to see whether IV needs to be included.
+ */
+ if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT))
+ return 1;
+ if ((! is_mgmt) && (k->wk_flags & IEEE80211_KEY_NOIV))
+ return 1;
+
+
hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
/*
@@ -187,24 +222,14 @@ tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
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 */
+ tkip_setiv(k, ivp);
/*
- * Finally, do software encrypt if neeed.
+ * Finally, do software encrypt if needed.
*/
- 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++;
+ if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&
+ !tkip_encrypt(ctx, k, m, hdrlen))
+ return 0;
return 1;
}
@@ -216,6 +241,19 @@ static int
tkip_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
{
struct tkip_ctx *ctx = k->wk_private;
+ struct ieee80211_frame *wh;
+ int is_mgmt;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ is_mgmt = IEEE80211_IS_MGMT(wh);
+
+ /*
+ * Check to see whether MIC needs to be included.
+ */
+ if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOMICMGT))
+ return 1;
+ if ((! is_mgmt) && (k->wk_flags & IEEE80211_KEY_NOMIC))
+ return 1;
if (force || (k->wk_flags & IEEE80211_KEY_SWENMIC)) {
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
@@ -251,11 +289,20 @@ READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
static int
tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
+ const struct ieee80211_rx_stats *rxs;
struct tkip_ctx *ctx = k->wk_private;
struct ieee80211vap *vap = ctx->tc_vap;
struct ieee80211_frame *wh;
uint8_t *ivp, tid;
+ rxs = ieee80211_get_rx_params_ptr(m);
+
+ /*
+ * If IV has been stripped, we skip most of the below.
+ */
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))
+ goto finish;
+
/*
* Header should have extended IV and sequence number;
* verify the former and validate the latter.
@@ -310,11 +357,22 @@ tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
!tkip_decrypt(ctx, k, m, hdrlen))
return 0;
+finish:
+
+ /*
+ * Copy up 802.11 header and strip crypto bits - but only if we
+ * are required to.
+ */
+ if (! ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))) {
+ memmove(mtod(m, uint8_t *) + tkip.ic_header, mtod(m, void *),
+ hdrlen);
+ m_adj(m, tkip.ic_header);
+ }
+
/*
- * Copy up 802.11 header and strip crypto bits.
+ * XXX TODO: do we need an option to potentially not strip the
+ * WEP trailer? Does "MMIC_STRIP" also mean this? Or?
*/
- 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;
@@ -326,11 +384,33 @@ tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
static int
tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
{
+ const struct ieee80211_rx_stats *rxs;
struct tkip_ctx *ctx = k->wk_private;
struct ieee80211_frame *wh;
uint8_t tid;
wh = mtod(m, struct ieee80211_frame *);
+ rxs = ieee80211_get_rx_params_ptr(m);
+
+ /*
+ * If we are told about a MIC failure from the driver,
+ * directly notify as a michael failure to the upper
+ * layers.
+ */
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_FAIL_MIC)) {
+ struct ieee80211vap *vap = ctx->tc_vap;
+ ieee80211_notify_michael_failure(vap, wh,
+ k->wk_rxkeyix != IEEE80211_KEYIX_NONE ?
+ k->wk_rxkeyix : k->wk_keyix);
+ return 0;
+ }
+
+ /*
+ * If IV has been stripped, we skip most of the below.
+ */
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MMIC_STRIP))
+ goto finish;
+
if ((k->wk_flags & IEEE80211_KEY_SWDEMIC) || force) {
struct ieee80211vap *vap = ctx->tc_vap;
int hdrlen = ieee80211_hdrspace(vap->iv_ic, wh);
@@ -363,6 +443,7 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
tid = ieee80211_gettid(wh);
k->wk_keyrsc[tid] = ctx->rx_rsc;
+finish:
return 1;
}
@@ -933,10 +1014,9 @@ tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
ctx->tc_vap->iv_stats.is_crypto_tkip++;
wh = mtod(m, struct ieee80211_frame *);
- if (!ctx->tx_phase1_done) {
+ if ((u16)(key->wk_keytsc) == 0 || key->wk_keytsc == 1) {
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);
@@ -947,9 +1027,6 @@ tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
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;
}
diff --git a/freebsd/sys/net80211/ieee80211_crypto_wep.c b/freebsd/sys/net80211/ieee80211_crypto_wep.c
index 5d268b28..4b3b67ab 100644
--- a/freebsd/sys/net80211/ieee80211_crypto_wep.c
+++ b/freebsd/sys/net80211/ieee80211_crypto_wep.c
@@ -52,8 +52,9 @@ __FBSDID("$FreeBSD$");
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 void wep_setiv(struct ieee80211_key *, uint8_t *);
+static int wep_encap(struct ieee80211_key *, struct mbuf *);
+static int wep_decap(struct ieee80211_key *, struct mbuf *, int);
static int wep_enmic(struct ieee80211_key *, struct mbuf *, int);
static int wep_demic(struct ieee80211_key *, struct mbuf *, int);
@@ -66,6 +67,7 @@ static const struct ieee80211_cipher wep = {
.ic_attach = wep_attach,
.ic_detach = wep_detach,
.ic_setkey = wep_setkey,
+ .ic_setiv = wep_setiv,
.ic_encap = wep_encap,
.ic_decap = wep_decap,
.ic_enmic = wep_enmic,
@@ -89,8 +91,8 @@ 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);
+ ctx = (struct wep_ctx *) IEEE80211_MALLOC(sizeof(struct wep_ctx),
+ M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (ctx == NULL) {
vap->iv_stats.is_crypto_nomem++;
return NULL;
@@ -108,7 +110,7 @@ wep_detach(struct ieee80211_key *k)
{
struct wep_ctx *ctx = k->wk_private;
- free(ctx, M_80211_CRYPTO);
+ IEEE80211_FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -119,29 +121,15 @@ 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)
+static void
+wep_setiv(struct ieee80211_key *k, uint8_t *ivp)
{
struct wep_ctx *ctx = k->wk_private;
- struct ieee80211com *ic = ctx->wc_ic;
+ struct ieee80211vap *vap = ctx->wc_vap;
uint32_t iv;
- uint8_t *ivp;
- int hdrlen;
-
- hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
+ uint8_t keyid;
- /*
- * 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;
+ keyid = ieee80211_crypto_get_keyid(vap, k) << 6;
/*
* XXX
@@ -184,9 +172,47 @@ wep_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
ivp[0] = iv >> 16;
#endif
ivp[3] = keyid;
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+wep_encap(struct ieee80211_key *k, struct mbuf *m)
+{
+ struct wep_ctx *ctx = k->wk_private;
+ struct ieee80211com *ic = ctx->wc_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *ivp;
+ int hdrlen;
+ int is_mgmt;
+
+ hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
+ wh = mtod(m, struct ieee80211_frame *);
+ is_mgmt = IEEE80211_IS_MGMT(wh);
+
+ /*
+ * Check to see if IV is required.
+ */
+ if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT))
+ return 1;
+ if ((! is_mgmt) && (k->wk_flags & IEEE80211_KEY_NOIV))
+ return 1;
+
+ /*
+ * 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;
+
+ wep_setiv(k, ivp);
/*
- * Finally, do software encrypt if neeed.
+ * Finally, do software encrypt if needed.
*/
if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&
!wep_encrypt(k, m, hdrlen))
@@ -216,9 +242,15 @@ 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;
+ const struct ieee80211_rx_stats *rxs;
wh = mtod(m, struct ieee80211_frame *);
+ rxs = ieee80211_get_rx_params_ptr(m);
+
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))
+ goto finish;
+
/*
* Check if the device handled the decrypt in hardware.
* If so we just strip the header; otherwise we need to
@@ -237,6 +269,9 @@ wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
*/
ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + wep.ic_header, hdrlen);
m_adj(m, wep.ic_header);
+
+finish:
+ /* XXX TODO: do we have to strip this for offload devices? */
m_adj(m, -wep.ic_trailer);
return 1;
@@ -421,7 +456,7 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
}
off = hdrlen + wep.ic_header;
- data_len = m->m_pkthdr.len - (off + wep.ic_trailer),
+ data_len = m->m_pkthdr.len - (off + wep.ic_trailer);
/* Compute CRC32 over unencrypted data and apply RC4 to data */
crc = ~0;
diff --git a/freebsd/sys/net80211/ieee80211_ddb.c b/freebsd/sys/net80211/ieee80211_ddb.c
index 25b10a6e..25c1c93a 100644
--- a/freebsd/sys/net80211/ieee80211_ddb.c
+++ b/freebsd/sys/net80211/ieee80211_ddb.c
@@ -38,9 +38,11 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
@@ -65,9 +67,11 @@ __FBSDID("$FreeBSD$");
} while (0)
static void _db_show_sta(const struct ieee80211_node *);
-static void _db_show_vap(const struct ieee80211vap *, int);
+static void _db_show_vap(const struct ieee80211vap *, int, int);
static void _db_show_com(const struct ieee80211com *,
- int showvaps, int showsta, int showprocs);
+ int showvaps, int showsta, int showmesh, int showprocs);
+
+static void _db_show_all_vaps(void *, struct ieee80211com *);
static void _db_show_node_table(const char *tag,
const struct ieee80211_node_table *);
@@ -105,7 +109,7 @@ DB_SHOW_COMMAND(statab, db_show_statab)
DB_SHOW_COMMAND(vap, db_show_vap)
{
- int i, showprocs = 0;
+ int i, showmesh = 0, showprocs = 0;
if (!have_addr) {
db_printf("usage: show vap <addr>\n");
@@ -115,18 +119,22 @@ DB_SHOW_COMMAND(vap, db_show_vap)
switch (modif[i]) {
case 'a':
showprocs = 1;
+ showmesh = 1;
+ break;
+ case 'm':
+ showmesh = 1;
break;
case 'p':
showprocs = 1;
break;
}
- _db_show_vap((const struct ieee80211vap *) addr, showprocs);
+ _db_show_vap((const struct ieee80211vap *) addr, showmesh, showprocs);
}
DB_SHOW_COMMAND(com, db_show_com)
{
const struct ieee80211com *ic;
- int i, showprocs = 0, showvaps = 0, showsta = 0;
+ int i, showprocs = 0, showvaps = 0, showsta = 0, showmesh = 0;
if (!have_addr) {
db_printf("usage: show com <addr>\n");
@@ -135,11 +143,14 @@ DB_SHOW_COMMAND(com, db_show_com)
for (i = 0; modif[i] != '\0'; i++)
switch (modif[i]) {
case 'a':
- showsta = showvaps = showprocs = 1;
+ showsta = showmesh = showvaps = showprocs = 1;
break;
case 's':
showsta = 1;
break;
+ case 'm':
+ showmesh = 1;
+ break;
case 'v':
showvaps = 1;
break;
@@ -149,13 +160,11 @@ DB_SHOW_COMMAND(com, db_show_com)
}
ic = (const struct ieee80211com *) addr;
- _db_show_com(ic, showvaps, showsta, showprocs);
+ _db_show_com(ic, showvaps, showsta, showmesh, 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++)
@@ -165,24 +174,7 @@ DB_SHOW_ALL_COMMAND(vaps, db_show_all_vaps)
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);
- }
- }
+ ieee80211_iterate_coms(_db_show_all_vaps, &showall);
}
#ifdef IEEE80211_SUPPORT_MESH
@@ -204,7 +196,7 @@ _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]);
+ ieee80211_wme_acnames[TID_TO_WME_AC(tap->txa_tid)]);
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);
@@ -243,9 +235,8 @@ _db_show_sta(const struct ieee80211_node *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("\tauthmode %u ath_flags 0x%x ath_defkeyix %u\n",
+ 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",
@@ -295,7 +286,7 @@ _db_show_sta(const struct ieee80211_node *ni)
ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw);
/* XXX ampdu state */
- for (i = 0; i < WME_NUM_AC; i++)
+ for (i = 0; i < WME_NUM_TID; 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++)
@@ -332,7 +323,7 @@ _db_show_tdma(const char *sep, const struct ieee80211_tdma_state *ts, int showpr
#endif /* IEEE80211_SUPPORT_TDMA */
static void
-_db_show_vap(const struct ieee80211vap *vap, int showprocs)
+_db_show_vap(const struct ieee80211vap *vap, int showmesh, int showprocs)
{
const struct ieee80211com *ic = vap->iv_ic;
int i;
@@ -343,6 +334,10 @@ _db_show_vap(const struct ieee80211vap *vap, int showprocs)
db_printf("\n");
db_printf("\topmode %s", ieee80211_opmode_name[vap->iv_opmode]);
+#ifdef IEEE80211_SUPPORT_MESH
+ if (vap->iv_opmode == IEEE80211_M_MBSS)
+ db_printf("(%p)", vap->iv_mesh);
+#endif
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");
@@ -474,6 +469,10 @@ _db_show_vap(const struct ieee80211vap *vap, int showprocs)
db_printf(" acl %p", vap->iv_acl);
db_printf(" as %p", vap->iv_as);
db_printf("\n");
+#ifdef IEEE80211_SUPPORT_MESH
+ if (showmesh && vap->iv_mesh != NULL)
+ _db_show_mesh(vap->iv_mesh);
+#endif
#ifdef IEEE80211_SUPPORT_TDMA
if (vap->iv_tdma != NULL)
_db_show_tdma("\t", vap->iv_tdma, showprocs);
@@ -497,7 +496,8 @@ _db_show_vap(const struct ieee80211vap *vap, int showprocs)
}
static void
-_db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showprocs)
+_db_show_com(const struct ieee80211com *ic, int showvaps, int showsta,
+ int showmesh, int showprocs)
{
struct ieee80211vap *vap;
@@ -505,14 +505,16 @@ _db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showp
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("\tsoftc %p", ic->ic_softc);
+ db_printf("\tname %s", ic->ic_name);
db_printf(" comlock %p", &ic->ic_comlock);
+ db_printf(" txlock %p", &ic->ic_txlock);
+ db_printf(" fflock %p", &ic->ic_fflock);
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");
@@ -653,7 +655,7 @@ _db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showp
if (showvaps && !TAILQ_EMPTY(&ic->ic_vaps)) {
db_printf("\n");
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
- _db_show_vap(vap, showprocs);
+ _db_show_vap(vap, showmesh, showprocs);
}
if (showsta && !TAILQ_EMPTY(&ic->ic_sta.nt_node)) {
const struct ieee80211_node_table *nt = &ic->ic_sta;
@@ -667,6 +669,21 @@ _db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showp
}
static void
+_db_show_all_vaps(void *arg, struct ieee80211com *ic)
+{
+ int showall = *(int *)arg;
+
+ if (!showall) {
+ const struct ieee80211vap *vap;
+ db_printf("%s: com %p vaps:", ic->ic_name, 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, 1);
+}
+
+static void
_db_show_node_table(const char *tag, const struct ieee80211_node_table *nt)
{
int i;
@@ -674,8 +691,6 @@ _db_show_node_table(const char *tag, const struct ieee80211_node_table *nt)
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++) {
@@ -872,8 +887,10 @@ _db_show_mesh(const struct ieee80211_mesh_state *ms)
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);
+ ieee80211_mesh_rt_update(rt, 0),
+ rt->rt_lastmseq, rt->rt_priv);
i++;
}
}
diff --git a/freebsd/sys/net80211/ieee80211_dfs.c b/freebsd/sys/net80211/ieee80211_dfs.c
index 708cfc90..5fccefaf 100644
--- a/freebsd/sys/net80211/ieee80211_dfs.c
+++ b/freebsd/sys/net80211/ieee80211_dfs.c
@@ -50,7 +50,9 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
+#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
@@ -66,6 +68,34 @@ 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)
+/*
+ DFS* In order to facilitate debugging, a couple of operating
+ * modes aside from the default are needed.
+ *
+ * 0 - default CAC/NOL behaviour - ie, start CAC, place
+ * channel on NOL list.
+ * 1 - send CAC, but don't change channel or add the channel
+ * to the NOL list.
+ * 2 - just match on radar, don't send CAC or place channel in
+ * the NOL list.
+ */
+static int ieee80211_dfs_debug = DFS_DBG_NONE;
+
+/*
+ * This option must not be included in the default kernel
+ * as it allows users to plainly disable CAC/NOL handling.
+ */
+#ifdef IEEE80211_DFS_DEBUG
+SYSCTL_INT(_net_wlan, OID_AUTO, dfs_debug, CTLFLAG_RW,
+ &ieee80211_dfs_debug, 0, "DFS debug behaviour");
+#endif
+
+static int
+null_set_quiet(struct ieee80211_node *ni, u_int8_t *quiet_elm)
+{
+ return ENOSYS;
+}
+
void
ieee80211_dfs_attach(struct ieee80211com *ic)
{
@@ -73,6 +103,8 @@ ieee80211_dfs_attach(struct ieee80211com *ic)
callout_init_mtx(&dfs->nol_timer, IEEE80211_LOCK_OBJ(ic), 0);
callout_init_mtx(&dfs->cac_timer, IEEE80211_LOCK_OBJ(ic), 0);
+
+ ic->ic_set_quiet = null_set_quiet;
}
void
@@ -215,7 +247,7 @@ dfs_timeout(void *arg)
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)) {
+ if (ieee80211_time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) {
c->ic_state &= ~IEEE80211_CHANSTATE_RADAR;
if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) {
/*
@@ -223,8 +255,8 @@ dfs_timeout(void *arg)
* msg instead of one for every channel
* table entry.
*/
- if_printf(ic->ic_ifp, "radar on channel"
- " %u (%u MHz) cleared after timeout\n",
+ ic_printf(ic, "radar on channel %u "
+ "(%u MHz) cleared after timeout\n",
c->ic_ieee, c->ic_freq);
/* notify user space */
c->ic_state &=
@@ -242,14 +274,14 @@ dfs_timeout(void *arg)
}
static void
-announce_radar(struct ifnet *ifp, const struct ieee80211_channel *curchan,
+announce_radar(struct ieee80211com *ic, const struct ieee80211_channel *curchan,
const struct ieee80211_channel *newchan)
{
if (newchan == NULL)
- if_printf(ifp, "radar detected on channel %u (%u MHz)\n",
+ ic_printf(ic, "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), "
+ ic_printf(ic, "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);
@@ -272,24 +304,44 @@ ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *ch
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.
+ * If doing DFS debugging (mode 2), don't bother
+ * running the rest of this function.
+ *
+ * Simply announce the presence of the radar and continue
+ * along merrily.
*/
- 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;
+ if (ieee80211_dfs_debug == DFS_DBG_NOCSANOL) {
+ announce_radar(ic, chan, chan);
+ ieee80211_notify_radar(ic, chan);
+ return;
+ }
+
+ /*
+ * Don't mark the channel and don't put it into NOL
+ * if we're doing DFS debugging.
+ */
+ if (ieee80211_dfs_debug == DFS_DBG_NONE) {
+ /*
+ * 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);
}
- 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
@@ -304,9 +356,17 @@ ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *ch
*/
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);
+ /*
+ * Don't flip over to a new channel if
+ * we are currently doing DFS debugging.
+ */
+ if (ieee80211_dfs_debug == DFS_DBG_NONE)
+ dfs->newchan = ieee80211_dfs_pickchannel(ic);
+ else
+ dfs->newchan = chan;
+
+ announce_radar(ic, chan, dfs->newchan);
if (callout_pending(&dfs->cac_timer))
callout_schedule(&dfs->cac_timer, 0);
@@ -322,7 +382,7 @@ ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *ch
* on the NOL to expire.
*/
/*XXX*/
- if_printf(ic->ic_ifp, "%s: No free channels; waiting for entry "
+ ic_printf(ic, "%s: No free channels; waiting for entry "
"on NOL to expire\n", __func__);
}
} else {
@@ -332,9 +392,9 @@ ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *ch
if (dfs->lastchan != chan) {
dfs->lastchan = chan;
dfs->cureps = 0;
- announce_radar(ic->ic_ifp, chan, NULL);
+ announce_radar(ic, chan, NULL);
} else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) {
- announce_radar(ic->ic_ifp, chan, NULL);
+ announce_radar(ic, chan, NULL);
}
}
}
@@ -376,6 +436,6 @@ ieee80211_dfs_pickchannel(struct ieee80211com *ic)
(c->ic_flags & flags) == flags)
return c;
}
- if_printf(ic->ic_ifp, "HELP, no channel located to switch to!\n");
+ ic_printf(ic, "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
index 90760777..a5688e3b 100644
--- a/freebsd/sys/net80211/ieee80211_dfs.h
+++ b/freebsd/sys/net80211/ieee80211_dfs.h
@@ -31,6 +31,12 @@
* 802.11h/DFS definitions.
*/
+typedef enum {
+ DFS_DBG_NONE = 0,
+ DFS_DBG_NONOL = 1,
+ DFS_DBG_NOCSANOL = 2
+} dfs_debug_t;
+
struct ieee80211_dfs_state {
int nol_event[IEEE80211_CHAN_MAX];
struct callout nol_timer; /* NOL list processing */
diff --git a/freebsd/sys/net80211/ieee80211_freebsd.c b/freebsd/sys/net80211/ieee80211_freebsd.c
index 5e2abf8a..8c90c2f3 100644
--- a/freebsd/sys/net80211/ieee80211_freebsd.c
+++ b/freebsd/sys/net80211/ieee80211_freebsd.c
@@ -34,9 +34,11 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/local/opt_wlan.h>
#include <rtems/bsd/sys/param.h>
-#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
#include <sys/linker.h>
+#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/proc.h>
@@ -46,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include <net/bpf.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_clone.h>
#include <net/if_media.h>
@@ -60,35 +63,15 @@ __FBSDID("$FreeBSD$");
SYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters");
#ifdef IEEE80211_DEBUG
-int ieee80211_debug = 0;
+static int ieee80211_debug = 0;
SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
0, "debugging printfs");
#endif
static 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 const char wlanname[] = "wlan";
+static struct if_clone *wlan_cloner;
static int
wlan_clone_create(struct if_clone *ifc, int unit, caddr_t params)
@@ -96,28 +79,21 @@ 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)
+ ic = ieee80211_find_com(cp.icp_parent);
+ if (ic == 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);
+ ic_printf(ic, "%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",
+ ic_printf(ic, "%s mode not supported\n",
ieee80211_opmode_name[cp.icp_opmode]);
return EOPNOTSUPP;
}
@@ -128,13 +104,14 @@ wlan_clone_create(struct if_clone *ifc, int unit, caddr_t params)
(1)
#endif
) {
- if_printf(ifp, "TDMA not supported\n");
+ ic_printf(ic, "TDMA not supported\n");
return EOPNOTSUPP;
}
- vap = ic->ic_vap_create(ic, ifc->ifc_name, unit,
+ vap = ic->ic_vap_create(ic, wlanname, 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));
+ cp.icp_macaddr : ic->ic_macaddr);
+
return (vap == NULL ? EIO : 0);
}
@@ -146,12 +123,13 @@ wlan_clone_destroy(struct ifnet *ifp)
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);
+ CURVNET_SET(vap->iv_ifp->if_vnet);
+ if_clone_destroyif(wlan_cloner, vap->iv_ifp);
+ CURVNET_RESTORE();
}
int
@@ -185,9 +163,8 @@ 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));
+ return SYSCTL_OUT_STR(req, ic->ic_name);
}
static int
@@ -223,8 +200,8 @@ ieee80211_sysctl_vattach(struct ieee80211vap *vap)
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);
+ ctx = (struct sysctl_ctx_list *) IEEE80211_MALLOC(sizeof(struct sysctl_ctx_list),
+ M_DEVBUF, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (ctx == NULL) {
if_printf(ifp, "%s: cannot allocate sysctl context!\n",
__func__);
@@ -299,7 +276,7 @@ ieee80211_sysctl_vdetach(struct ieee80211vap *vap)
if (vap->iv_sysctl != NULL) {
sysctl_ctx_free(vap->iv_sysctl);
- free(vap->iv_sysctl, M_DEVBUF);
+ IEEE80211_FREE(vap->iv_sysctl, M_DEVBUF);
vap->iv_sysctl = NULL;
}
}
@@ -365,7 +342,7 @@ ieee80211_flush_ifq(struct ifqueue *ifq, struct ieee80211vap *vap)
*/
#define MC_ALIGN(m, len) \
do { \
- (m)->m_data += (MCLBYTES - (len)) &~ (sizeof(long) - 1); \
+ (m)->m_data += rounddown2(MCLBYTES - (len), sizeof(long)); \
} while (/* CONSTCOND */ 0)
/*
@@ -398,7 +375,7 @@ ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen)
* frames which all fit in MHLEN.
*/
if (m != NULL)
- MH_ALIGN(m, len);
+ M_ALIGN(m, len);
} else {
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
if (m != NULL)
@@ -411,6 +388,7 @@ ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen)
return m;
}
+#ifndef __NO_STRICT_ALIGNMENT
/*
* Re-align the payload in the mbuf. This is mainly used (right now)
* to handle IP header alignment requirements on certain architectures.
@@ -424,9 +402,9 @@ ieee80211_realign(struct ieee80211vap *vap, struct mbuf *m, size_t align)
pktlen = m->m_pkthdr.len;
space = pktlen + align;
if (space < MINCLSIZE)
- n = m_gethdr(M_DONTWAIT, MT_DATA);
+ n = m_gethdr(M_NOWAIT, MT_DATA);
else {
- n = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR,
+ n = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
space <= MCLBYTES ? MCLBYTES :
#if MJUMPAGESIZE != MCLBYTES
space <= MJUMPAGESIZE ? MJUMPAGESIZE :
@@ -447,6 +425,7 @@ ieee80211_realign(struct ieee80211vap *vap, struct mbuf *m, size_t align)
m_freem(m);
return n;
}
+#endif /* !__NO_STRICT_ALIGNMENT */
int
ieee80211_add_callback(struct mbuf *m,
@@ -468,6 +447,40 @@ ieee80211_add_callback(struct mbuf *m,
return 1;
}
+int
+ieee80211_add_xmit_params(struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct m_tag *mtag;
+ struct ieee80211_tx_params *tx;
+
+ mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_XMIT_PARAMS,
+ sizeof(struct ieee80211_tx_params), M_NOWAIT);
+ if (mtag == NULL)
+ return (0);
+
+ tx = (struct ieee80211_tx_params *)(mtag+1);
+ memcpy(&tx->params, params, sizeof(struct ieee80211_bpf_params));
+ m_tag_prepend(m, mtag);
+ return (1);
+}
+
+int
+ieee80211_get_xmit_params(struct mbuf *m,
+ struct ieee80211_bpf_params *params)
+{
+ struct m_tag *mtag;
+ struct ieee80211_tx_params *tx;
+
+ mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_XMIT_PARAMS,
+ NULL);
+ if (mtag == NULL)
+ return (-1);
+ tx = (struct ieee80211_tx_params *)(mtag + 1);
+ memcpy(params, &tx->params, sizeof(struct ieee80211_bpf_params));
+ return (0);
+}
+
void
ieee80211_process_callback(struct ieee80211_node *ni,
struct mbuf *m, int status)
@@ -481,6 +494,139 @@ ieee80211_process_callback(struct ieee80211_node *ni,
}
}
+/*
+ * Add RX parameters to the given mbuf.
+ *
+ * Returns 1 if OK, 0 on error.
+ */
+int
+ieee80211_add_rx_params(struct mbuf *m, const struct ieee80211_rx_stats *rxs)
+{
+ struct m_tag *mtag;
+ struct ieee80211_rx_params *rx;
+
+ mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_RECV_PARAMS,
+ sizeof(struct ieee80211_rx_stats), M_NOWAIT);
+ if (mtag == NULL)
+ return (0);
+
+ rx = (struct ieee80211_rx_params *)(mtag + 1);
+ memcpy(&rx->params, rxs, sizeof(*rxs));
+ m_tag_prepend(m, mtag);
+ return (1);
+}
+
+int
+ieee80211_get_rx_params(struct mbuf *m, struct ieee80211_rx_stats *rxs)
+{
+ struct m_tag *mtag;
+ struct ieee80211_rx_params *rx;
+
+ mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_RECV_PARAMS,
+ NULL);
+ if (mtag == NULL)
+ return (-1);
+ rx = (struct ieee80211_rx_params *)(mtag + 1);
+ memcpy(rxs, &rx->params, sizeof(*rxs));
+ return (0);
+}
+
+const struct ieee80211_rx_stats *
+ieee80211_get_rx_params_ptr(struct mbuf *m)
+{
+ struct m_tag *mtag;
+ struct ieee80211_rx_params *rx;
+
+ mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_RECV_PARAMS,
+ NULL);
+ if (mtag == NULL)
+ return (NULL);
+ rx = (struct ieee80211_rx_params *)(mtag + 1);
+ return (&rx->params);
+}
+
+
+/*
+ * Add TOA parameters to the given mbuf.
+ */
+int
+ieee80211_add_toa_params(struct mbuf *m, const struct ieee80211_toa_params *p)
+{
+ struct m_tag *mtag;
+ struct ieee80211_toa_params *rp;
+
+ mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_TOA_PARAMS,
+ sizeof(struct ieee80211_toa_params), M_NOWAIT);
+ if (mtag == NULL)
+ return (0);
+
+ rp = (struct ieee80211_toa_params *)(mtag + 1);
+ memcpy(rp, p, sizeof(*rp));
+ m_tag_prepend(m, mtag);
+ return (1);
+}
+
+int
+ieee80211_get_toa_params(struct mbuf *m, struct ieee80211_toa_params *p)
+{
+ struct m_tag *mtag;
+ struct ieee80211_toa_params *rp;
+
+ mtag = m_tag_locate(m, MTAG_ABI_NET80211, NET80211_TAG_TOA_PARAMS,
+ NULL);
+ if (mtag == NULL)
+ return (0);
+ rp = (struct ieee80211_toa_params *)(mtag + 1);
+ if (p != NULL)
+ memcpy(p, rp, sizeof(*p));
+ return (1);
+}
+
+/*
+ * Transmit a frame to the parent interface.
+ */
+int
+ieee80211_parent_xmitpkt(struct ieee80211com *ic, struct mbuf *m)
+{
+ int error;
+
+ /*
+ * Assert the IC TX lock is held - this enforces the
+ * processing -> queuing order is maintained
+ */
+ IEEE80211_TX_LOCK_ASSERT(ic);
+ error = ic->ic_transmit(ic, m);
+ if (error) {
+ struct ieee80211_node *ni;
+
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+
+ /* XXX number of fragments */
+ if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1);
+ ieee80211_free_node(ni);
+ ieee80211_free_mbuf(m);
+ }
+ return (error);
+}
+
+/*
+ * Transmit a frame to the VAP interface.
+ */
+int
+ieee80211_vap_xmitpkt(struct ieee80211vap *vap, struct mbuf *m)
+{
+ struct ifnet *ifp = vap->iv_ifp;
+
+ /*
+ * When transmitting via the VAP, we shouldn't hold
+ * any IC TX lock as the VAP TX path will acquire it.
+ */
+ IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
+
+ return (ifp->if_transmit(ifp, m));
+
+}
+
#include <sys/libkern.h>
void
@@ -573,8 +719,8 @@ ieee80211_notify_replay_failure(struct ieee80211vap *vap,
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,
+ "%s replay detected tid %d <rsc %ju, csc %ju, keyix %u rxkeyix %u>",
+ k->wk_cipher->ic_name, tid, (intmax_t) rsc,
(intmax_t) k->wk_keyrsc[tid],
k->wk_keyix, k->wk_rxkeyix);
@@ -632,8 +778,9 @@ 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;
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
memset(&iev, 0, sizeof(iev));
iev.iev_flags = c->ic_flags;
@@ -641,36 +788,53 @@ ieee80211_notify_csa(struct ieee80211com *ic,
iev.iev_ieee = c->ic_ieee;
iev.iev_mode = mode;
iev.iev_count = count;
- rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev));
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ ifp = vap->iv_ifp;
+ CURVNET_SET(ifp->if_vnet);
+ rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev));
+ CURVNET_RESTORE();
+ }
}
void
ieee80211_notify_radar(struct ieee80211com *ic,
const struct ieee80211_channel *c)
{
- struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_radar_event iev;
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
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));
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ ifp = vap->iv_ifp;
+ CURVNET_SET(ifp->if_vnet);
+ rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev));
+ CURVNET_RESTORE();
+ }
}
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;
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
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));
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ ifp = vap->iv_ifp;
+ CURVNET_SET(ifp->if_vnet);
+ rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev));
+ CURVNET_RESTORE();
+ }
}
void
@@ -706,18 +870,26 @@ ieee80211_notify_country(struct ieee80211vap *vap,
IEEE80211_ADDR_COPY(iev.iev_addr, bssid);
iev.iev_cc[0] = cc[0];
iev.iev_cc[1] = cc[1];
+ CURVNET_SET(ifp->if_vnet);
rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev));
+ CURVNET_RESTORE();
}
void
ieee80211_notify_radio(struct ieee80211com *ic, int state)
{
- struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_radio_event iev;
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
memset(&iev, 0, sizeof(iev));
iev.iev_state = state;
- rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev));
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ ifp = vap->iv_ifp;
+ CURVNET_SET(ifp->if_vnet);
+ rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev));
+ CURVNET_RESTORE();
+ }
}
void
@@ -737,8 +909,9 @@ 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) {
+ /* NB: identify vap's by if_init */
+ if (dlt == DLT_IEEE802_11_RADIO &&
+ ifp->if_init == ieee80211_init) {
struct ieee80211vap *vap = ifp->if_softc;
/*
* Track bpf radiotap listener state. We mark the vap
@@ -759,31 +932,19 @@ bpf_track(void *arg, struct ifnet *ifp, int dlt, int attach)
}
}
+/*
+ * Change MAC address on the vap (if was not started).
+ */
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;
+ /* NB: identify vap's by if_init */
+ if (ifp->if_init == ieee80211_init &&
+ (ifp->if_flags & IFF_UP) == 0) {
+ struct ieee80211vap *vap = ifp->if_softc;
- 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_ADDR_COPY(vap->iv_myaddr, IF_LLADDR(ifp));
}
- IEEE80211_UNLOCK(ic);
}
/*
@@ -800,20 +961,13 @@ wlan_modevent(module_t mod, int type, void *unused)
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);
+ wlan_cloner = if_clone_simple(wlanname, wlan_clone_create,
+ wlan_clone_destroy, 0);
return 0;
case MOD_UNLOAD:
- if_deregister_com_alloc(IFT_IEEE80211);
- if_clone_detach(&wlan_cloner);
+ if_clone_detach(wlan_cloner);
EVENTHANDLER_DEREGISTER(bpf_track, wlan_bpfevent);
EVENTHANDLER_DEREGISTER(iflladdr_event, wlan_ifllevent);
return 0;
@@ -822,10 +976,14 @@ wlan_modevent(module_t mod, int type, void *unused)
}
static moduledata_t wlan_mod = {
- "wlan",
+ wlanname,
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);
+#ifdef IEEE80211_ALQ
+MODULE_DEPEND(wlan, alq, 1, 1, 1);
+#endif /* IEEE80211_ALQ */
+
diff --git a/freebsd/sys/net80211/ieee80211_freebsd.h b/freebsd/sys/net80211/ieee80211_freebsd.h
index 3cdc643e..49549e7b 100644
--- a/freebsd/sys/net80211/ieee80211_freebsd.h
+++ b/freebsd/sys/net80211/ieee80211_freebsd.h
@@ -29,6 +29,8 @@
#ifdef _KERNEL
#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/counter.h>
#include <rtems/bsd/sys/lock.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
@@ -53,6 +55,51 @@ typedef struct {
#define IEEE80211_UNLOCK(_ic) mtx_unlock(IEEE80211_LOCK_OBJ(_ic))
#define IEEE80211_LOCK_ASSERT(_ic) \
mtx_assert(IEEE80211_LOCK_OBJ(_ic), MA_OWNED)
+#define IEEE80211_UNLOCK_ASSERT(_ic) \
+ mtx_assert(IEEE80211_LOCK_OBJ(_ic), MA_NOTOWNED)
+
+/*
+ * Transmit lock.
+ *
+ * This is a (mostly) temporary lock designed to serialise all of the
+ * transmission operations throughout the stack.
+ */
+typedef struct {
+ char name[16]; /* e.g. "ath0_tx_lock" */
+ struct mtx mtx;
+} ieee80211_tx_lock_t;
+#define IEEE80211_TX_LOCK_INIT(_ic, _name) do { \
+ ieee80211_tx_lock_t *cl = &(_ic)->ic_txlock; \
+ snprintf(cl->name, sizeof(cl->name), "%s_tx_lock", _name); \
+ mtx_init(&cl->mtx, cl->name, NULL, MTX_DEF); \
+} while (0)
+#define IEEE80211_TX_LOCK_OBJ(_ic) (&(_ic)->ic_txlock.mtx)
+#define IEEE80211_TX_LOCK_DESTROY(_ic) mtx_destroy(IEEE80211_TX_LOCK_OBJ(_ic))
+#define IEEE80211_TX_LOCK(_ic) mtx_lock(IEEE80211_TX_LOCK_OBJ(_ic))
+#define IEEE80211_TX_UNLOCK(_ic) mtx_unlock(IEEE80211_TX_LOCK_OBJ(_ic))
+#define IEEE80211_TX_LOCK_ASSERT(_ic) \
+ mtx_assert(IEEE80211_TX_LOCK_OBJ(_ic), MA_OWNED)
+#define IEEE80211_TX_UNLOCK_ASSERT(_ic) \
+ mtx_assert(IEEE80211_TX_LOCK_OBJ(_ic), MA_NOTOWNED)
+
+/*
+ * Stageq / ni_tx_superg lock
+ */
+typedef struct {
+ char name[16]; /* e.g. "ath0_ff_lock" */
+ struct mtx mtx;
+} ieee80211_ff_lock_t;
+#define IEEE80211_FF_LOCK_INIT(_ic, _name) do { \
+ ieee80211_ff_lock_t *fl = &(_ic)->ic_fflock; \
+ snprintf(fl->name, sizeof(fl->name), "%s_ff_lock", _name); \
+ mtx_init(&fl->mtx, fl->name, NULL, MTX_DEF); \
+} while (0)
+#define IEEE80211_FF_LOCK_OBJ(_ic) (&(_ic)->ic_fflock.mtx)
+#define IEEE80211_FF_LOCK_DESTROY(_ic) mtx_destroy(IEEE80211_FF_LOCK_OBJ(_ic))
+#define IEEE80211_FF_LOCK(_ic) mtx_lock(IEEE80211_FF_LOCK_OBJ(_ic))
+#define IEEE80211_FF_UNLOCK(_ic) mtx_unlock(IEEE80211_FF_LOCK_OBJ(_ic))
+#define IEEE80211_FF_LOCK_ASSERT(_ic) \
+ mtx_assert(IEEE80211_FF_LOCK_OBJ(_ic), MA_OWNED)
/*
* Node locking definitions.
@@ -79,28 +126,6 @@ typedef struct {
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;
@@ -157,6 +182,34 @@ typedef struct mtx ieee80211_scan_table_lock_t;
#define IEEE80211_SCAN_TABLE_LOCK(_st) mtx_lock(&(_st)->st_lock)
#define IEEE80211_SCAN_TABLE_UNLOCK(_st) mtx_unlock(&(_st)->st_lock)
+typedef struct mtx ieee80211_scan_iter_lock_t;
+#define IEEE80211_SCAN_ITER_LOCK_INIT(_st, _name) \
+ mtx_init(&(_st)->st_scanlock, _name, "802.11 scangen", MTX_DEF)
+#define IEEE80211_SCAN_ITER_LOCK_DESTROY(_st) mtx_destroy(&(_st)->st_scanlock)
+#define IEEE80211_SCAN_ITER_LOCK(_st) mtx_lock(&(_st)->st_scanlock)
+#define IEEE80211_SCAN_ITER_UNLOCK(_st) mtx_unlock(&(_st)->st_scanlock)
+
+/*
+ * Mesh node/routing definitions.
+ */
+typedef struct mtx ieee80211_rte_lock_t;
+#define MESH_RT_ENTRY_LOCK_INIT(_rt, _name) \
+ mtx_init(&(rt)->rt_lock, _name, "802.11s route entry", MTX_DEF)
+#define MESH_RT_ENTRY_LOCK_DESTROY(_rt) \
+ mtx_destroy(&(_rt)->rt_lock)
+#define MESH_RT_ENTRY_LOCK(rt) mtx_lock(&(rt)->rt_lock)
+#define MESH_RT_ENTRY_LOCK_ASSERT(rt) mtx_assert(&(rt)->rt_lock, MA_OWNED)
+#define MESH_RT_ENTRY_UNLOCK(rt) mtx_unlock(&(rt)->rt_lock)
+
+typedef struct mtx ieee80211_rt_lock_t;
+#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)
+#define MESH_RT_LOCK_INIT(ms, name) \
+ mtx_init(&(ms)->ms_rt_lock, name, "802.11s routing table", MTX_DEF)
+#define MESH_RT_LOCK_DESTROY(ms) \
+ mtx_destroy(&(ms)->ms_rt_lock)
+
/*
* Node reference counting definitions.
*
@@ -190,13 +243,15 @@ void ieee80211_vap_destroy(struct ieee80211vap *);
(((_ifp)->if_flags & IFF_UP) && \
((_ifp)->if_drv_flags & IFF_DRV_RUNNING))
+/* XXX TODO: cap these at 1, as hz may not be 1000 */
#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)
+
+#define ieee80211_time_after(a,b) ((long)(b) - (long)(a) < 0)
+#define ieee80211_time_before(a,b) ieee80211_time_after(b,a)
+#define ieee80211_time_after_eq(a,b) ((long)(a) - (long)(b) >= 0)
+#define ieee80211_time_before_eq(a,b) ieee80211_time_after_eq(b,a)
struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
@@ -205,12 +260,16 @@ struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
#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_FF M_PROTO6 /* fast frame / A-MSDU */
#define M_TXCB M_PROTO7 /* do tx complete callback */
#define M_AMPDU_MPDU M_PROTO8 /* ok for A-MPDU aggregation */
+#define M_FRAG M_PROTO9 /* frame fragmentation */
+#define M_FIRSTFRAG M_PROTO10 /* first frame fragment */
+#define M_LASTFRAG M_PROTO11 /* last frame fragment */
+
#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)
+ (M_ENCAP|M_EAPOL|M_PWR_SAV|M_MORE_DATA|M_FF|M_TXCB| \
+ M_AMPDU_MPDU|M_FRAG|M_FIRSTFRAG|M_LASTFRAG)
/* rx path usage */
#define M_AMPDU M_PROTO1 /* A-MPDU subframe */
@@ -221,16 +280,13 @@ struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
#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"
+ M_FLAG_BITS \
+ "\15M_ENCAP\17M_EAPOL\20M_PWR_SAV\21M_MORE_DATA\22M_FF\23M_TXCB" \
+ "\24M_AMPDU_MPDU\25M_FRAG\26M_FIRSTFRAG\27M_LASTFRAG"
#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"
+ M_FLAG_BITS \
+ "\15M_AMPDU\16M_WEP\24M_AMPDU_MPDU"
/*
* Store WME access control bits in the vlan tag.
@@ -270,9 +326,18 @@ 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);
+#define NET80211_TAG_XMIT_PARAMS 1
+/* See below; this is after the bpf_params definition */
+
+#define NET80211_TAG_RECV_PARAMS 2
+
+#define NET80211_TAG_TOA_PARAMS 3
struct ieee80211com;
+int ieee80211_parent_xmitpkt(struct ieee80211com *, struct mbuf *);
+int ieee80211_vap_xmitpkt(struct ieee80211vap *, struct mbuf *);
+
+void get_random_bytes(void *, size_t);
void ieee80211_sysctl_attach(struct ieee80211com *);
void ieee80211_sysctl_detach(struct ieee80211com *);
@@ -306,8 +371,8 @@ wlan_##name##_modevent(module_t mod, int type, void *unused) \
case MOD_UNLOAD: \
case MOD_QUIESCE: \
if (nrefs) { \
- printf("wlan_##name: still in use (%u dynamic refs)\n",\
- nrefs); \
+ printf("wlan_" #name ": still in use " \
+ "(%u dynamic refs)\n", nrefs); \
return EBUSY; \
} \
if (type == MOD_UNLOAD) { \
@@ -547,4 +612,148 @@ struct ieee80211_bpf_params {
uint8_t ibp_try3; /* series 4 try count */
uint8_t ibp_rate3; /* series 4 IEEE tx rate */
};
+
+#ifdef _KERNEL
+struct ieee80211_tx_params {
+ struct ieee80211_bpf_params params;
+};
+int ieee80211_add_xmit_params(struct mbuf *m,
+ const struct ieee80211_bpf_params *);
+int ieee80211_get_xmit_params(struct mbuf *m,
+ struct ieee80211_bpf_params *);
+
+/*
+ * Note: this is fine for 3x3 (and 4x4) 11n HT40;
+ * but getting EVM information for VHT80, VHT160
+ * will involve more than 6 EVM pilots.
+ */
+#define IEEE80211_MAX_CHAINS 4
+#define IEEE80211_MAX_EVM_PILOTS 6
+
+#define IEEE80211_R_NF 0x00000001 /* global NF value valid */
+#define IEEE80211_R_RSSI 0x00000002 /* global RSSI value valid */
+#define IEEE80211_R_C_CHAIN 0x00000004 /* RX chain count valid */
+#define IEEE80211_R_C_NF 0x00000008 /* per-chain NF value valid */
+#define IEEE80211_R_C_RSSI 0x00000010 /* per-chain RSSI value valid */
+#define IEEE80211_R_C_EVM 0x00000020 /* per-chain EVM valid */
+#define IEEE80211_R_C_HT40 0x00000040 /* RX'ed packet is 40mhz, pilots 4,5 valid */
+#define IEEE80211_R_FREQ 0x00000080 /* Freq value populated, MHz */
+#define IEEE80211_R_IEEE 0x00000100 /* IEEE value populated */
+#define IEEE80211_R_BAND 0x00000200 /* Frequency band populated */
+#define IEEE80211_R_TSF32 0x00004000 /* 32 bit TSF */
+#define IEEE80211_R_TSF64 0x00008000 /* 64 bit TSF */
+#define IEEE80211_R_TSF_START 0x00010000 /* TSF is sampled at start of frame */
+#define IEEE80211_R_TSF_END 0x00020000 /* TSF is sampled at end of frame */
+
+/* RX packet flags - describe the kind of frame */
+#define IEEE80211_RX_F_STBC 0x00000001
+#define IEEE80211_RX_F_LDPC 0x00000002
+#define IEEE80211_RX_F_AMSDU 0x00000004 /* This is the start of an decap AMSDU list */
+#define IEEE80211_RX_F_AMSDU_MORE 0x00000008 /* This is another decap AMSDU frame in the batch */
+#define IEEE80211_RX_F_AMPDU 0x00000010 /* This is the start of an decap AMPDU list */
+#define IEEE80211_RX_F_AMPDU_MORE 0x00000020 /* This is another decap AMPDU frame in the batch */
+#define IEEE80211_RX_F_FAIL_FCSCRC 0x00000040 /* Failed CRC/FCS */
+#define IEEE80211_RX_F_FAIL_MIC 0x00000080 /* Failed MIC check */
+#define IEEE80211_RX_F_DECRYPTED 0x00000100 /* Hardware decrypted */
+#define IEEE80211_RX_F_IV_STRIP 0x00000200 /* Decrypted; IV stripped */
+#define IEEE80211_RX_F_MMIC_STRIP 0x00000400 /* Decrypted; MMIC stripped */
+#define IEEE80211_RX_F_SHORTGI 0x00000800 /* This is a short-GI frame */
+#define IEEE80211_RX_F_CCK 0x00001000
+#define IEEE80211_RX_F_OFDM 0x00002000
+#define IEEE80211_RX_F_HT 0x00004000
+#define IEEE80211_RX_F_VHT 0x00008000
+
+/* Channel width */
+#define IEEE80211_RX_FW_20MHZ 1
+#define IEEE80211_RX_FW_40MHZ 2
+#define IEEE80211_RX_FW_80MHZ 3
+
+/* PHY type */
+#define IEEE80211_RX_FP_11B 1
+#define IEEE80211_RX_FP_11G 2
+#define IEEE80211_RX_FP_11A 3
+#define IEEE80211_RX_FP_11NA 4
+#define IEEE80211_RX_FP_11NG 5
+
+struct ieee80211_rx_stats {
+ uint32_t r_flags; /* IEEE80211_R_* flags */
+ uint32_t c_pktflags; /* IEEE80211_RX_F_* flags */
+
+ uint64_t c_rx_tsf; /* 32 or 64 bit TSF */
+
+ /* All DWORD aligned */
+ int16_t c_nf_ctl[IEEE80211_MAX_CHAINS]; /* per-chain NF */
+ int16_t c_nf_ext[IEEE80211_MAX_CHAINS]; /* per-chain NF */
+ int16_t c_rssi_ctl[IEEE80211_MAX_CHAINS]; /* per-chain RSSI */
+ int16_t c_rssi_ext[IEEE80211_MAX_CHAINS]; /* per-chain RSSI */
+
+ /* 32 bits */
+ uint8_t c_nf; /* global NF */
+ uint8_t c_rssi; /* global RSSI */
+ uint8_t c_chain; /* number of RX chains involved */
+ uint8_t c_rate; /* legacy; 11n rate code; VHT MCS */
+
+ /* 32 bits */
+ uint16_t c_freq; /* Frequency, MHz */
+ uint8_t c_ieee; /* Channel */
+ uint8_t c_width; /* channel width, FW flags above */
+
+ /* Force alignment to DWORD */
+ union {
+ uint8_t evm[IEEE80211_MAX_CHAINS][IEEE80211_MAX_EVM_PILOTS];
+ /* per-chain, per-pilot EVM values */
+ uint32_t __aln[8];
+ } evm;
+
+ /* 32 bits */
+ uint8_t c_phytype; /* PHY type, FW flags above */
+ uint8_t c_vhtnss; /* VHT - number of spatial streams */
+ uint8_t c_pad2[2];
+};
+
+struct ieee80211_rx_params {
+ struct ieee80211_rx_stats params;
+};
+int ieee80211_add_rx_params(struct mbuf *m,
+ const struct ieee80211_rx_stats *rxs);
+int ieee80211_get_rx_params(struct mbuf *m,
+ struct ieee80211_rx_stats *rxs);
+const struct ieee80211_rx_stats * ieee80211_get_rx_params_ptr(struct mbuf *m);
+
+struct ieee80211_toa_params {
+ int request_id;
+};
+int ieee80211_add_toa_params(struct mbuf *m,
+ const struct ieee80211_toa_params *p);
+int ieee80211_get_toa_params(struct mbuf *m,
+ struct ieee80211_toa_params *p);
+
+#define IEEE80211_F_SURVEY_TIME 0x00000001
+#define IEEE80211_F_SURVEY_TIME_BUSY 0x00000002
+#define IEEE80211_F_SURVEY_NOISE_DBM 0x00000004
+#define IEEE80211_F_SURVEY_TSC 0x00000008
+struct ieee80211_channel_survey {
+ uint32_t s_flags;
+ uint32_t s_time;
+ uint32_t s_time_busy;
+ int32_t s_noise;
+ uint64_t s_tsc;
+};
+
+#endif /* _KERNEL */
+
+/*
+ * Malloc API. Other BSD operating systems have slightly
+ * different malloc/free namings (eg DragonflyBSD.)
+ */
+#define IEEE80211_MALLOC malloc
+#define IEEE80211_FREE free
+
+/* XXX TODO: get rid of WAITOK, fix all the users of it? */
+#define IEEE80211_M_NOWAIT M_NOWAIT
+#define IEEE80211_M_WAITOK M_WAITOK
+#define IEEE80211_M_ZERO M_ZERO
+
+/* XXX TODO: the type fields */
+
#endif /* _NET80211_IEEE80211_FREEBSD_H_ */
diff --git a/freebsd/sys/net80211/ieee80211_hostap.c b/freebsd/sys/net80211/ieee80211_hostap.c
index 63809ea3..8905c53c 100644
--- a/freebsd/sys/net80211/ieee80211_hostap.c
+++ b/freebsd/sys/net80211/ieee80211_hostap.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/if_llc.h>
#include <net/ethernet.h>
@@ -69,13 +70,13 @@ __FBSDID("$FreeBSD$");
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,
+ const struct ieee80211_rx_stats *,
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);
+ int subtype, const struct ieee80211_rx_stats *rxs, 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)
@@ -102,14 +103,14 @@ hostap_vattach(struct ieee80211vap *vap)
vap->iv_recv_ctl = hostap_recv_ctl;
vap->iv_opdetach = hostap_vdetach;
vap->iv_deliver_data = hostap_deliver_data;
+ vap->iv_recv_pspoll = ieee80211_recv_pspoll;
}
static void
sta_disassoc(void *arg, struct ieee80211_node *ni)
{
- struct ieee80211vap *vap = arg;
- if (ni->ni_vap == vap && ni->ni_associd != 0) {
+ if (ni->ni_associd != 0) {
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
IEEE80211_REASON_ASSOC_LEAVE);
ieee80211_node_leave(ni);
@@ -119,9 +120,9 @@ sta_disassoc(void *arg, struct ieee80211_node *ni)
static void
sta_csa(void *arg, struct ieee80211_node *ni)
{
- struct ieee80211vap *vap = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
- if (ni->ni_vap == vap && ni->ni_associd != 0)
+ if (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,
@@ -132,9 +133,8 @@ sta_csa(void *arg, struct ieee80211_node *ni)
static void
sta_drop(void *arg, struct ieee80211_node *ni)
{
- struct ieee80211vap *vap = arg;
- if (ni->ni_vap == vap && ni->ni_associd != 0)
+ if (ni->ni_associd != 0)
ieee80211_node_leave(ni);
}
@@ -179,7 +179,8 @@ hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
ieee80211_dfs_cac_stop(vap);
break;
case IEEE80211_S_RUN:
- ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap);
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap,
+ sta_disassoc, NULL);
break;
default:
break;
@@ -195,7 +196,8 @@ hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
switch (ostate) {
case IEEE80211_S_CSA:
case IEEE80211_S_RUN:
- ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap);
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap,
+ sta_disassoc, NULL);
/*
* Clear overlapping BSS state; the beacon frame
* will be reconstructed on transition to the RUN
@@ -289,7 +291,8 @@ hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* 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);
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap,
+ sta_csa, NULL);
/*
* Update bss node channel to reflect where
* we landed after CSA.
@@ -340,7 +343,8 @@ hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* such as capabilities and the negotiated rate
* set may/will be wrong).
*/
- ieee80211_iterate_nodes(&ic->ic_sta, sta_drop, vap);
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap,
+ sta_drop, NULL);
}
break;
default:
@@ -357,14 +361,15 @@ hostap_deliver_data(struct ieee80211vap *vap,
struct ifnet *ifp = vap->iv_ifp;
/* clear driver/net80211 flags before passing up */
- m->m_flags &= ~(M_80211_RX | M_MCAST | M_BCAST);
+ m->m_flags &= ~(M_MCAST | M_BCAST);
+ m_clrprotoflags(m);
KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP,
("gack, opmode %d", vap->iv_opmode));
/*
* Do accounting.
*/
- ifp->if_ipackets++;
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
IEEE80211_NODE_STAT(ni, rx_data);
IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
@@ -378,9 +383,9 @@ hostap_deliver_data(struct ieee80211vap *vap,
struct mbuf *mcopy = NULL;
if (m->m_flags & M_MCAST) {
- mcopy = m_dup(m, M_DONTWAIT);
+ mcopy = m_dup(m, M_NOWAIT);
if (mcopy == NULL)
- ifp->if_oerrors++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
else
mcopy->m_flags |= M_MCAST;
} else {
@@ -411,16 +416,8 @@ hostap_deliver_data(struct ieee80211vap *vap,
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 (mcopy != NULL)
+ (void) ieee80211_vap_xmitpkt(vap, mcopy);
}
if (m != NULL) {
/*
@@ -472,9 +469,9 @@ doprint(struct ieee80211vap *vap, int subtype)
* by the 802.11 layer.
*/
static int
-hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
+hostap_input(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
-#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;
@@ -484,7 +481,16 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
int hdrspace, need_tap = 1; /* mbuf need to be tapped. */
uint8_t dir, type, subtype, qos;
uint8_t *bssid;
- uint16_t rxseq;
+ int is_hw_decrypted = 0;
+ int has_decrypted = 0;
+
+ /*
+ * Some devices do hardware decryption all the way through
+ * to pretending the frame wasn't encrypted in the first place.
+ * So, tag it appropriately so it isn't discarded inappropriately.
+ */
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED))
+ is_hw_decrypted = 1;
if (m->m_flags & M_AMPDU_MPDU) {
/*
@@ -567,29 +573,13 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
ni->ni_noise = nf;
- if (HAS_SEQ(type)) {
+ if (IEEE80211_HAS_SEQ(type, subtype)) {
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 (! ieee80211_check_rxseq(ni, wh)) {
- /* 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);
+ if (! ieee80211_check_rxseq(ni, wh, bssid))
goto out;
- }
- ni->ni_rxseqs[tid] = rxseq;
}
}
@@ -647,7 +637,7 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
*/
if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
(ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
- ieee80211_node_pwrsave(ni,
+ vap->iv_node_ps(ni,
wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
/*
* For 4-address packets handle WDS discovery
@@ -690,7 +680,7 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
* crypto cipher modules used to do delayed update
* of replay sequence numbers.
*/
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (is_hw_decrypted || wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
/*
* Discard encrypted frames when privacy is off.
@@ -701,14 +691,14 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
IEEE80211_NODE_STAT(ni, rx_noprivacy);
goto out;
}
- key = ieee80211_crypto_decap(ni, m, hdrspace);
- if (key == NULL) {
+ if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
/* 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;
+ wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+ has_decrypted = 1;
} else {
/* XXX M_WEP and IEEE80211_F_PRIVACY */
key = NULL;
@@ -791,7 +781,8 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
* any non-PAE frames received without encryption.
*/
if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
- (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) &&
+ (is_hw_decrypted == 0) &&
eh->ether_type != htons(ETHERTYPE_PAE)) {
/*
* Drop unencrypted frames.
@@ -847,12 +838,11 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
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],
+ ieee80211_mgt_subtype_name(subtype),
ether_sprintf(wh->i_addr2), rssi);
}
#endif
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
/*
* Only shared key auth frames with a challenge
@@ -874,13 +864,13 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
goto out;
}
hdrspace = ieee80211_hdrspace(ic, wh);
- key = ieee80211_crypto_decap(ni, m, hdrspace);
- if (key == NULL) {
+ if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
/* NB: stats+msgs handled in crypto_decap */
goto out;
}
wh = mtod(m, struct ieee80211_frame *);
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+ has_decrypted = 1;
}
/*
* Pass the packet to radiotap before calling iv_recv_mgmt().
@@ -890,7 +880,7 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
if (ieee80211_radiotap_active_vap(vap))
ieee80211_radiotap_rx(vap, m);
need_tap = 0;
- vap->iv_recv_mgmt(ni, m, subtype, rssi, nf);
+ vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
goto out;
case IEEE80211_FC0_TYPE_CTL:
@@ -905,7 +895,7 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
break;
}
err:
- ifp->if_ierrors++;
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
out:
if (m != NULL) {
if (need_tap && ieee80211_radiotap_active_vap(vap))
@@ -934,7 +924,7 @@ hostap_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
* open auth is attempted.
*/
if (ni->ni_challenge != NULL) {
- free(ni->ni_challenge, M_80211_NODE);
+ IEEE80211_FREE(ni->ni_challenge, M_80211_NODE);
ni->ni_challenge = NULL;
}
/* XXX hack to workaround calling convention */
@@ -1092,7 +1082,7 @@ hostap_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
*/
ni->ni_flags |= IEEE80211_NODE_AREF;
/*
- * Mark the node as requiring a valid associatio id
+ * Mark the node as requiring a valid association id
* before outbound traffic is permitted.
*/
ni->ni_flags |= IEEE80211_NODE_ASSOCID;
@@ -1178,28 +1168,36 @@ bad:
* record any key length.
*/
static int
-wpa_cipher(const uint8_t *sel, uint8_t *keylen)
+wpa_cipher(const uint8_t *sel, uint8_t *keylen, uint8_t *cipher)
{
#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
- uint32_t w = LE_READ_4(sel);
+ uint32_t w = le32dec(sel);
switch (w) {
case WPA_SEL(WPA_CSE_NULL):
- return IEEE80211_CIPHER_NONE;
+ *cipher = IEEE80211_CIPHER_NONE;
+ break;
case WPA_SEL(WPA_CSE_WEP40):
if (keylen)
*keylen = 40 / NBBY;
- return IEEE80211_CIPHER_WEP;
+ *cipher = IEEE80211_CIPHER_WEP;
+ break;
case WPA_SEL(WPA_CSE_WEP104):
if (keylen)
*keylen = 104 / NBBY;
- return IEEE80211_CIPHER_WEP;
+ *cipher = IEEE80211_CIPHER_WEP;
+ break;
case WPA_SEL(WPA_CSE_TKIP):
- return IEEE80211_CIPHER_TKIP;
+ *cipher = IEEE80211_CIPHER_TKIP;
+ break;
case WPA_SEL(WPA_CSE_CCMP):
- return IEEE80211_CIPHER_AES_CCM;
+ *cipher = IEEE80211_CIPHER_AES_CCM;
+ break;
+ default:
+ return (EINVAL);
}
- return 32; /* NB: so 1<< is discarded */
+
+ return (0);
#undef WPA_SEL
}
@@ -1211,7 +1209,7 @@ static int
wpa_keymgmt(const uint8_t *sel)
{
#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
- uint32_t w = LE_READ_4(sel);
+ uint32_t w = le32dec(sel);
switch (w) {
case WPA_SEL(WPA_ASE_8021X_UNSPEC):
@@ -1237,7 +1235,7 @@ ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm,
{
uint8_t len = frm[1];
uint32_t w;
- int n;
+ int error, n;
/*
* Check the length once for fixed parts: OUI, type,
@@ -1258,7 +1256,7 @@ ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm,
}
frm += 6, len -= 4; /* NB: len is payload only */
/* NB: iswpaoui already validated the OUI and type */
- w = LE_READ_2(frm);
+ w = le16dec(frm);
if (w != WPA_VERSION) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
@@ -1270,11 +1268,18 @@ ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm,
memset(rsn, 0, sizeof(*rsn));
/* multicast/group cipher */
- rsn->rsn_mcastcipher = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
+ error = wpa_cipher(frm, &rsn->rsn_mcastkeylen, &rsn->rsn_mcastcipher);
+ if (error != 0) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "unknown mcast cipher suite %08X",
+ le32dec(frm));
+ return IEEE80211_REASON_GROUP_CIPHER_INVALID;
+ }
frm += 4, len -= 4;
/* unicast ciphers */
- n = LE_READ_2(frm);
+ n = le16dec(frm);
frm += 2, len -= 2;
if (len < n*4+2) {
IEEE80211_DISCARD_IE(vap,
@@ -1285,16 +1290,29 @@ ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm,
}
w = 0;
for (; n > 0; n--) {
- w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
+ uint8_t cipher;
+
+ error = wpa_cipher(frm, &rsn->rsn_ucastkeylen, &cipher);
+ if (error == 0)
+ w |= 1 << cipher;
+
frm += 4, len -= 4;
}
- if (w & (1<<IEEE80211_CIPHER_TKIP))
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
- else
+ if (w == 0) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "no usable pairwise cipher suite found (w=%d)",
+ w);
+ return IEEE80211_REASON_PAIRWISE_CIPHER_INVALID;
+ }
+ /* XXX other? */
+ if (w & (1 << IEEE80211_CIPHER_AES_CCM))
rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+ else
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
/* key management algorithms */
- n = LE_READ_2(frm);
+ n = le16dec(frm);
frm += 2, len -= 2;
if (len < n*4) {
IEEE80211_DISCARD_IE(vap,
@@ -1314,7 +1332,7 @@ ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm,
rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
if (len > 2) /* optional capabilities */
- rsn->rsn_caps = LE_READ_2(frm);
+ rsn->rsn_caps = le16dec(frm);
return 0;
}
@@ -1325,30 +1343,39 @@ ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm,
* record any key length.
*/
static int
-rsn_cipher(const uint8_t *sel, uint8_t *keylen)
+rsn_cipher(const uint8_t *sel, uint8_t *keylen, uint8_t *cipher)
{
#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
- uint32_t w = LE_READ_4(sel);
+ uint32_t w = le32dec(sel);
switch (w) {
case RSN_SEL(RSN_CSE_NULL):
- return IEEE80211_CIPHER_NONE;
+ *cipher = IEEE80211_CIPHER_NONE;
+ break;
case RSN_SEL(RSN_CSE_WEP40):
if (keylen)
*keylen = 40 / NBBY;
- return IEEE80211_CIPHER_WEP;
+ *cipher = IEEE80211_CIPHER_WEP;
+ break;
case RSN_SEL(RSN_CSE_WEP104):
if (keylen)
*keylen = 104 / NBBY;
- return IEEE80211_CIPHER_WEP;
+ *cipher = IEEE80211_CIPHER_WEP;
+ break;
case RSN_SEL(RSN_CSE_TKIP):
- return IEEE80211_CIPHER_TKIP;
+ *cipher = IEEE80211_CIPHER_TKIP;
+ break;
case RSN_SEL(RSN_CSE_CCMP):
- return IEEE80211_CIPHER_AES_CCM;
+ *cipher = IEEE80211_CIPHER_AES_CCM;
+ break;
case RSN_SEL(RSN_CSE_WRAP):
- return IEEE80211_CIPHER_AES_OCB;
+ *cipher = IEEE80211_CIPHER_AES_OCB;
+ break;
+ default:
+ return (EINVAL);
}
- return 32; /* NB: so 1<< is discarded */
+
+ return (0);
#undef WPA_SEL
}
@@ -1360,7 +1387,7 @@ static int
rsn_keymgmt(const uint8_t *sel)
{
#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
- uint32_t w = LE_READ_4(sel);
+ uint32_t w = le32dec(sel);
switch (w) {
case RSN_SEL(RSN_ASE_8021X_UNSPEC):
@@ -1385,7 +1412,7 @@ ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm,
{
uint8_t len = frm[1];
uint32_t w;
- int n;
+ int error, n;
/*
* Check the length once for fixed parts:
@@ -1398,6 +1425,7 @@ ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm,
wh, "WPA", "not RSN, flags 0x%x", vap->iv_flags);
return IEEE80211_REASON_IE_INVALID;
}
+ /* XXX may be shorter */
if (len < 10) {
IEEE80211_DISCARD_IE(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
@@ -1405,23 +1433,37 @@ ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm,
return IEEE80211_REASON_IE_INVALID;
}
frm += 2;
- w = LE_READ_2(frm);
+ w = le16dec(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;
+ return IEEE80211_REASON_UNSUPP_RSN_IE_VERSION;
}
frm += 2, len -= 2;
memset(rsn, 0, sizeof(*rsn));
/* multicast/group cipher */
- rsn->rsn_mcastcipher = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
+ error = rsn_cipher(frm, &rsn->rsn_mcastkeylen, &rsn->rsn_mcastcipher);
+ if (error != 0) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "unknown mcast cipher suite %08X",
+ le32dec(frm));
+ return IEEE80211_REASON_GROUP_CIPHER_INVALID;
+ }
+ if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_NONE) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "invalid mcast cipher suite %d",
+ rsn->rsn_mcastcipher);
+ return IEEE80211_REASON_GROUP_CIPHER_INVALID;
+ }
frm += 4, len -= 4;
/* unicast ciphers */
- n = LE_READ_2(frm);
+ n = le16dec(frm);
frm += 2, len -= 2;
if (len < n*4+2) {
IEEE80211_DISCARD_IE(vap,
@@ -1431,17 +1473,36 @@ ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm,
return IEEE80211_REASON_IE_INVALID;
}
w = 0;
+
for (; n > 0; n--) {
- w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
+ uint8_t cipher;
+
+ error = rsn_cipher(frm, &rsn->rsn_ucastkeylen, &cipher);
+ if (error == 0)
+ w |= 1 << cipher;
+
frm += 4, len -= 4;
}
- if (w & (1<<IEEE80211_CIPHER_TKIP))
+ if (w & (1 << IEEE80211_CIPHER_AES_CCM))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+ else if (w & (1 << IEEE80211_CIPHER_AES_OCB))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_OCB;
+ else if (w & (1 << IEEE80211_CIPHER_TKIP))
rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
- else
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+ else if ((w & (1 << IEEE80211_CIPHER_NONE)) &&
+ (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP ||
+ rsn->rsn_mcastcipher == IEEE80211_CIPHER_TKIP))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_NONE;
+ else {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "no usable pairwise cipher suite found (w=%d)",
+ w);
+ return IEEE80211_REASON_PAIRWISE_CIPHER_INVALID;
+ }
/* key management algorithms */
- n = LE_READ_2(frm);
+ n = le16dec(frm);
frm += 2, len -= 2;
if (len < n*4) {
IEEE80211_DISCARD_IE(vap,
@@ -1462,14 +1523,14 @@ ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm,
/* optional RSN capabilities */
if (len > 2)
- rsn->rsn_caps = LE_READ_2(frm);
+ rsn->rsn_caps = le16dec(frm);
/* XXXPMKID */
return 0;
}
/*
- * WPA/802.11i assocation request processing.
+ * WPA/802.11i association request processing.
*/
static int
wpa_assocreq(struct ieee80211_node *ni, struct ieee80211_rsnparms *rsnparms,
@@ -1535,6 +1596,7 @@ wpa_assocreq(struct ieee80211_node *ni, struct ieee80211_rsnparms *rsnparms,
else
reason = ieee80211_parse_rsn(vap, rsn, rsnparms, wh);
if (reason != 0) {
+ /* XXX wpa->rsn fallback? */
/* XXX distinguish WPA/RSN? */
vap->iv_stats.is_rx_assoc_badwpaie++;
goto bad;
@@ -1678,7 +1740,7 @@ is11bclient(const uint8_t *rates, const uint8_t *xrates)
static void
hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
- int subtype, int rssi, int nf)
+ int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
@@ -1693,20 +1755,22 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
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) {
+ */
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
vap->iv_stats.is_rx_mgtdiscard++;
return;
}
+ /* FALLTHROUGH */
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ struct ieee80211_scanparams scan;
+
/* NB: accept off-channel frames */
- if (ieee80211_parse_beacon(ni, m0, &scan) &~ IEEE80211_BPARSE_OFFCHAN)
+ /* XXX TODO: use rxstatus to determine off-channel details */
+ if (ieee80211_parse_beacon(ni, m0, ic->ic_curchan, &scan) &~ IEEE80211_BPARSE_OFFCHAN)
return;
/*
* Count frame now that we know it's to be processed.
@@ -1733,7 +1797,8 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
ieee80211_probe_curchan(vap, 1);
ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
}
- ieee80211_add_scan(vap, &scan, wh, subtype, rssi, nf);
+ ieee80211_add_scan(vap, ic->ic_curchan, &scan, wh,
+ subtype, rssi, nf);
return;
}
/*
@@ -1797,13 +1862,21 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
return;
}
/*
+ * Consult the ACL policy module if setup.
+ */
+ if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
+ wh, NULL, "%s", "disallowed by ACL");
+ vap->iv_stats.is_rx_acl++;
+ 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) {
@@ -1875,8 +1948,7 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
/*
* Consult the ACL policy module if setup.
*/
- if (vap->iv_acl != NULL &&
- !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+ if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
wh, NULL, "%s", "disallowed by ACL");
vap->iv_stats.is_rx_acl++;
@@ -2027,7 +2099,7 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
return;
/* discard challenge after association */
if (ni->ni_challenge != NULL) {
- free(ni->ni_challenge, M_80211_NODE);
+ IEEE80211_FREE(ni->ni_challenge, M_80211_NODE);
ni->ni_challenge = NULL;
}
/* NB: 802.11 spec says to ignore station's privacy bit */
@@ -2084,8 +2156,8 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
} 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);
+ /* Always do ff node cleanup; for A-MSDU */
+ ieee80211_ff_node_cleanup(ni);
#endif
/*
* Allow AMPDU operation only with unencrypted traffic
@@ -2103,6 +2175,10 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
"capinfo 0x%x ucastcipher %d", capinfo,
rsnparms.rsn_ucastcipher);
ieee80211_ht_node_cleanup(ni);
+#ifdef IEEE80211_SUPPORT_SUPERG
+ /* Always do ff node cleanup; for A-MSDU */
+ ieee80211_ff_node_cleanup(ni);
+#endif
vap->iv_stats.is_ht_assoc_downgrade++;
}
/*
@@ -2184,8 +2260,9 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
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);
+ "recv %s (reason: %d (%s))",
+ ieee80211_mgt_subtype_name(subtype),
+ reason, ieee80211_reason_to_string(reason));
if (ni != vap->iv_bss)
ieee80211_node_leave(ni);
break;
@@ -2215,6 +2292,7 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
case IEEE80211_FC0_SUBTYPE_ATIM:
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, NULL, "%s", "not handled");
@@ -2234,7 +2312,7 @@ 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);
+ ni->ni_vap->iv_recv_pspoll(ni, m);
break;
case IEEE80211_FC0_SUBTYPE_BAR:
ieee80211_recv_bar(ni, m);
@@ -2245,12 +2323,12 @@ hostap_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
/*
* Process a received ps-poll frame.
*/
-static void
-hostap_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0)
+void
+ieee80211_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0)
{
struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_frame_min *wh;
- struct ifnet *ifp;
struct mbuf *m;
uint16_t aid;
int qlen;
@@ -2314,10 +2392,14 @@ hostap_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0)
}
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);
+ /*
+ * Do the right thing; if it's an encap'ed frame then
+ * call ieee80211_parent_xmitpkt() else
+ * call ieee80211_vap_xmitpkt().
+ */
+ if (m->m_flags & M_ENCAP) {
+ (void) ieee80211_parent_xmitpkt(ic, m);
+ } else {
+ (void) ieee80211_vap_xmitpkt(vap, m);
+ }
}
diff --git a/freebsd/sys/net80211/ieee80211_hostap.h b/freebsd/sys/net80211/ieee80211_hostap.h
index 87f858d1..e08c4a04 100644
--- a/freebsd/sys/net80211/ieee80211_hostap.h
+++ b/freebsd/sys/net80211/ieee80211_hostap.h
@@ -32,4 +32,10 @@
*/
void ieee80211_hostap_attach(struct ieee80211com *);
void ieee80211_hostap_detach(struct ieee80211com *);
+
+/*
+ * This method can be overridden
+ */
+void ieee80211_recv_pspoll(struct ieee80211_node *, struct mbuf *);
+
#endif /* !_NET80211_IEEE80211_HOSTAP_H_ */
diff --git a/freebsd/sys/net80211/ieee80211_ht.c b/freebsd/sys/net80211/ieee80211_ht.c
index 69e64a02..beeddda4 100644
--- a/freebsd/sys/net80211/ieee80211_ht.c
+++ b/freebsd/sys/net80211/ieee80211_ht.c
@@ -39,12 +39,14 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
@@ -136,12 +138,10 @@ const struct ieee80211_mcs_rates ieee80211_htrates[IEEE80211_HTRATE_MAXSIZE] = {
{ 429, 477, 891, 990 }, /* MCS 76 */
};
-#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,
@@ -156,7 +156,7 @@ 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,
+SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW,
&ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff");
static int ieee80211_bar_timeout = -1; /* timeout waiting for BAR response */
@@ -178,9 +178,7 @@ 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);
@@ -297,6 +295,11 @@ ieee80211_ht_vattach(struct ieee80211vap *vap)
vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_RX;
if (vap->iv_htcaps & IEEE80211_HTC_AMSDU)
vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_TX;
+
+ if (vap->iv_htcaps & IEEE80211_HTCAP_TXSTBC)
+ vap->iv_flags_ht |= IEEE80211_FHT_STBC_TX;
+ if (vap->iv_htcaps & IEEE80211_HTCAP_RXSTBC)
+ vap->iv_flags_ht |= IEEE80211_FHT_STBC_RX;
}
/* NB: disable default legacy WDS, too many issues right now */
if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)
@@ -355,7 +358,6 @@ static struct printranges {
static void
ht_rateprint(struct ieee80211com *ic, enum ieee80211_phymode mode, int ratetype)
{
- struct ifnet *ifp = ic->ic_ifp;
int minrate, maxrate;
struct printranges *range;
@@ -370,12 +372,12 @@ ht_rateprint(struct ieee80211com *ic, enum ieee80211_phymode mode, int ratetype)
minrate = ht_getrate(ic, range->minmcs, mode, ratetype);
maxrate = ht_getrate(ic, range->maxmcs, mode, ratetype);
if (range->maxmcs) {
- if_printf(ifp, "MCS %d-%d: %d%sMbps - %d%sMbps\n",
+ ic_printf(ic, "MCS %d-%d: %d%sMbps - %d%sMbps\n",
range->minmcs, range->maxmcs,
minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""),
maxrate/2, ((maxrate & 0x1) != 0 ? ".5" : ""));
} else {
- if_printf(ifp, "MCS %d: %d%sMbps\n", range->minmcs,
+ ic_printf(ic, "MCS %d: %d%sMbps\n", range->minmcs,
minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""));
}
}
@@ -384,22 +386,21 @@ ht_rateprint(struct ieee80211com *ic, enum ieee80211_phymode mode, int ratetype)
static void
ht_announce(struct ieee80211com *ic, enum ieee80211_phymode mode)
{
- struct ifnet *ifp = ic->ic_ifp;
const char *modestr = ieee80211_phymode_name[mode];
- if_printf(ifp, "%s MCS 20MHz\n", modestr);
+ ic_printf(ic, "%s MCS 20MHz\n", modestr);
ht_rateprint(ic, mode, 0);
if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) {
- if_printf(ifp, "%s MCS 20MHz SGI\n", modestr);
+ ic_printf(ic, "%s MCS 20MHz SGI\n", modestr);
ht_rateprint(ic, mode, 1);
}
if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
- if_printf(ifp, "%s MCS 40MHz:\n", modestr);
+ ic_printf(ic, "%s MCS 40MHz:\n", modestr);
ht_rateprint(ic, mode, 2);
}
if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
(ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) {
- if_printf(ifp, "%s MCS 40MHz SGI:\n", modestr);
+ ic_printf(ic, "%s MCS 40MHz SGI:\n", modestr);
ht_rateprint(ic, mode, 3);
}
}
@@ -407,11 +408,10 @@ ht_announce(struct ieee80211com *ic, enum ieee80211_phymode mode)
void
ieee80211_ht_announce(struct ieee80211com *ic)
{
- struct ifnet *ifp = ic->ic_ifp;
if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
- if_printf(ifp, "%dT%dR\n", ic->ic_txstream, ic->ic_rxstream);
+ ic_printf(ic, "%dT%dR\n", ic->ic_txstream, ic->ic_rxstream);
if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA))
ht_announce(ic, IEEE80211_MODE_11NA);
if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
@@ -562,6 +562,62 @@ ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
}
/*
+ * Public function; manually setup the RX ampdu state.
+ */
+int
+ieee80211_ampdu_rx_start_ext(struct ieee80211_node *ni, int tid, int seq, int baw)
+{
+ struct ieee80211_rx_ampdu *rap;
+
+ /* XXX TODO: sanity check tid, seq, baw */
+
+ rap = &ni->ni_rx_ampdu[tid];
+
+ 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 = (baw== 0) ?
+ IEEE80211_AGGR_BAWMAX : min(baw, IEEE80211_AGGR_BAWMAX);
+ if (seq == -1) {
+ /* Wait for the first RX frame, use that as BAW */
+ rap->rxa_start = 0;
+ rap->rxa_flags |= IEEE80211_AGGR_WAITRX;
+ } else {
+ rap->rxa_start = seq;
+ }
+ rap->rxa_flags |= IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni,
+ "%s: tid=%d, start=%d, wnd=%d, flags=0x%08x",
+ __func__,
+ tid,
+ seq,
+ rap->rxa_wnd,
+ rap->rxa_flags);
+
+ return 0;
+}
+
+/*
+ * Public function; manually stop the RX AMPDU state.
+ */
+void
+ieee80211_ampdu_rx_stop_ext(struct ieee80211_node *ni, int tid)
+{
+ struct ieee80211_rx_ampdu *rap;
+
+ /* XXX TODO: sanity check tid, seq, baw */
+ rap = &ni->ni_rx_ampdu[tid];
+ ampdu_rx_stop(ni, rap);
+}
+
+/*
* Stop A-MPDU rx processing for the specified TID.
*/
static void
@@ -569,7 +625,9 @@ 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);
+ rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING
+ | IEEE80211_AGGR_XCHGPEND
+ | IEEE80211_AGGR_WAITRX);
}
/*
@@ -637,7 +695,6 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
vap->iv_stats.is_ampdu_rx_oor += i;
}
-#ifdef IEEE80211_AMPDU_AGE
/*
* Dispatch all frames in the A-MPDU re-order queue.
*/
@@ -662,7 +719,6 @@ ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
break;
}
}
-#endif /* IEEE80211_AMPDU_AGE */
/*
* Dispatch all frames in the A-MPDU re-order queue
@@ -746,8 +802,6 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
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;
@@ -798,6 +852,16 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
}
rxseq >>= IEEE80211_SEQ_SEQ_SHIFT;
rap->rxa_nframes++;
+
+ /*
+ * Handle waiting for the first frame to define the BAW.
+ * Some firmware doesn't provide the RX of the starting point
+ * of the BAW and we have to cope.
+ */
+ if (rap->rxa_flags & IEEE80211_AGGR_WAITRX) {
+ rap->rxa_flags &= ~IEEE80211_AGGR_WAITRX;
+ rap->rxa_start = rxseq;
+ }
again:
if (rxseq == rap->rxa_start) {
/*
@@ -830,7 +894,7 @@ again:
* Common case (hopefully): in the BA window.
* Sec 9.10.7.6.2 a) (p.137)
*/
-#ifdef IEEE80211_AMPDU_AGE
+
/*
* Check for frames sitting too long in the reorder queue.
* This should only ever happen if frames are not delivered
@@ -869,7 +933,7 @@ again:
*/
rap->rxa_age = ticks;
}
-#endif /* IEEE80211_AMPDU_AGE */
+
/* save packet */
if (rap->rxa_m[off] == NULL) {
rap->rxa_m[off] = m;
@@ -933,7 +997,6 @@ again:
}
#undef CONSUMED
#undef PROCESS
-#undef IEEE80211_FC0_QOSDATA
}
/*
@@ -1025,7 +1088,13 @@ void
ieee80211_ht_node_init(struct ieee80211_node *ni)
{
struct ieee80211_tx_ampdu *tap;
- int ac;
+ int tid;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
+ ni,
+ "%s: called (%p)",
+ __func__,
+ ni);
if (ni->ni_flags & IEEE80211_NODE_HT) {
/*
@@ -1033,12 +1102,17 @@ ieee80211_ht_node_init(struct ieee80211_node *ni)
* where a station leaves w/o notifying us and then returns
* before node is reaped for inactivity.
*/
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
+ ni,
+ "%s: calling cleanup (%p)",
+ __func__, ni);
ieee80211_ht_node_cleanup(ni);
}
- for (ac = 0; ac < WME_NUM_AC; ac++) {
- tap = &ni->ni_tx_ampdu[ac];
- tap->txa_ac = ac;
+ for (tid = 0; tid < WME_NUM_TID; tid++) {
+ tap = &ni->ni_tx_ampdu[tid];
+ tap->txa_tid = tid;
tap->txa_ni = ni;
+ ieee80211_txampdu_init_pps(tap);
/* NB: further initialization deferred */
}
ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
@@ -1054,10 +1128,15 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
struct ieee80211com *ic = ni->ni_ic;
int i;
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
+ ni,
+ "%s: called (%p)",
+ __func__, ni);
+
KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node"));
/* XXX optimize this */
- for (i = 0; i < WME_NUM_AC; i++) {
+ for (i = 0; i < WME_NUM_TID; i++) {
struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i];
if (tap->txa_flags & IEEE80211_AGGR_SETUP)
ampdu_tx_stop(tap);
@@ -1075,14 +1154,11 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
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;
@@ -1105,7 +1181,6 @@ ieee80211_ht_node_age(struct ieee80211_node *ni)
ampdu_rx_flush(ni, rap);
}
}
-#endif /* IEEE80211_AMPDU_AGE */
}
static struct ieee80211_channel *
@@ -1162,7 +1237,7 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_tx_ampdu *tap;
- int ac;
+ int tid;
KASSERT(vap->iv_flags_ht & IEEE80211_FHT_HT, ("no HT requested"));
@@ -1200,9 +1275,10 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
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;
+ for (tid = 0; tid < WME_NUM_TID; tid++) {
+ tap = &ni->ni_tx_ampdu[tid];
+ tap->txa_tid = tid;
+ ieee80211_txampdu_init_pps(tap);
}
/* NB: AMPDU tx/rx governed by IEEE80211_FHT_AMPDU_{TX,RX} */
ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
@@ -1356,7 +1432,7 @@ 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)) {
+ ieee80211_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");
@@ -1366,12 +1442,6 @@ ieee80211_ht_timeout(struct ieee80211com *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.
*/
@@ -1389,7 +1459,7 @@ ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
} else
ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT;
- ni->ni_htcap = LE_READ_2(ie +
+ ni->ni_htcap = le16dec(ie +
__offsetof(struct ieee80211_ie_htcap, hc_cap));
ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)];
}
@@ -1402,9 +1472,9 @@ htinfo_parse(struct ieee80211_node *ni,
ni->ni_htctlchan = htinfo->hi_ctrlchannel;
ni->ni_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN);
- w = LE_READ_2(&htinfo->hi_byte2);
+ w = le16dec(&htinfo->hi_byte2);
ni->ni_htopmode = SM(w, IEEE80211_HTINFO_OPMODE);
- w = LE_READ_2(&htinfo->hi_byte45);
+ w = le16dec(&htinfo->hi_byte45);
ni->ni_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS);
}
@@ -1430,12 +1500,13 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
* required channel change is done (e.g. in sta mode when
* parsing the contents of a beacon frame).
*/
-static void
+static int
htinfo_update_chw(struct ieee80211_node *ni, int htflags)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_channel *c;
int chanflags;
+ int ret = 0;
chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags;
if (chanflags != ni->ni_chan->ic_flags) {
@@ -1462,11 +1533,13 @@ htinfo_update_chw(struct ieee80211_node *ni, int htflags)
IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
c->ic_freq, c->ic_flags);
ni->ni_chan = c;
+ ret = 1;
}
/* 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;
+ return (ret);
}
/*
@@ -1517,13 +1590,14 @@ htcap_update_shortgi(struct ieee80211_node *ni)
* Parse and update HT-related state extracted from
* the HT cap and info ie's.
*/
-void
+int
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;
+ int ret = 0;
ieee80211_parse_htcap(ni, htcapie);
if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS)
@@ -1545,13 +1619,16 @@ ieee80211_ht_updateparams(struct ieee80211_node *ni,
else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
htflags = IEEE80211_CHAN_HT40D;
}
- htinfo_update_chw(ni, htflags);
+ if (htinfo_update_chw(ni, htflags))
+ ret = 1;
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;
+
+ return (ret);
}
/*
@@ -1580,7 +1657,7 @@ ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie)
else if (IEEE80211_IS_CHAN_HT40D(vap->iv_bss->ni_chan))
htflags = IEEE80211_CHAN_HT40D;
}
- htinfo_update_chw(ni, htflags);
+ (void) htinfo_update_chw(ni, htflags);
}
/*
@@ -1596,6 +1673,7 @@ ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
int i, maxequalmcs, maxunequalmcs;
maxequalmcs = ic->ic_txstream * 8 - 1;
+ maxunequalmcs = 0;
if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) {
if (ic->ic_txstream >= 2)
maxunequalmcs = 38;
@@ -1603,8 +1681,7 @@ ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
maxunequalmcs = 52;
if (ic->ic_txstream >= 4)
maxunequalmcs = 76;
- } else
- maxunequalmcs = 0;
+ }
rs = &ni->ni_htrates;
memset(rs, 0, sizeof(*rs));
@@ -1669,8 +1746,9 @@ ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie)
static void
ampdu_tx_setup(struct ieee80211_tx_ampdu *tap)
{
- callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
+ callout_init(&tap->txa_timer, 1);
tap->txa_flags |= IEEE80211_AGGR_SETUP;
+ tap->txa_lastsample = ticks;
}
static void
@@ -1679,8 +1757,14 @@ ampdu_tx_stop(struct ieee80211_tx_ampdu *tap)
struct ieee80211_node *ni = tap->txa_ni;
struct ieee80211com *ic = ni->ni_ic;
+ IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+ tap->txa_ni,
+ "%s: called",
+ __func__);
+
KASSERT(tap->txa_flags & IEEE80211_AGGR_SETUP,
- ("txa_flags 0x%x ac %d", tap->txa_flags, tap->txa_ac));
+ ("txa_flags 0x%x tid %d ac %d", tap->txa_flags, tap->txa_tid,
+ TID_TO_WME_AC(tap->txa_tid)));
/*
* Stop BA stream if setup so driver has a chance
@@ -1692,8 +1776,11 @@ ampdu_tx_stop(struct ieee80211_tx_ampdu *tap)
*/
bar_stop_timer(tap);
- tap->txa_lastsample = 0;
- tap->txa_avgpps = 0;
+ /*
+ * Reset packet estimate.
+ */
+ ieee80211_txampdu_init_pps(tap);
+
/* NB: clearing NAK means we may re-send ADDBA */
tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
}
@@ -1766,6 +1853,55 @@ ieee80211_addba_request(struct ieee80211_node *ni,
}
/*
+ * Called by drivers that wish to request an ADDBA session be
+ * setup. This brings it up and starts the request timer.
+ */
+int
+ieee80211_ampdu_tx_request_ext(struct ieee80211_node *ni, int tid)
+{
+ struct ieee80211_tx_ampdu *tap;
+
+ if (tid < 0 || tid > 15)
+ return (0);
+ tap = &ni->ni_tx_ampdu[tid];
+
+ /* 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;
+ addba_start_timeout(tap);
+ return (1);
+}
+
+/*
+ * Called by drivers that have marked a session as active.
+ */
+int
+ieee80211_ampdu_tx_request_active_ext(struct ieee80211_node *ni, int tid,
+ int status)
+{
+ struct ieee80211_tx_ampdu *tap;
+
+ if (tid < 0 || tid > 15)
+ return (0);
+ tap = &ni->ni_tx_ampdu[tid];
+
+ /* XXX locking */
+ addba_stop_timeout(tap);
+ if (status == 1) {
+ 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 processing an A-MPDU tx aggregation
* response. We shutdown any pending timer and update the
* state block according to the reply.
@@ -1831,9 +1967,9 @@ ht_recv_action_ba_addba_request(struct ieee80211_node *ni,
int tid;
dialogtoken = frm[2];
- baparamset = LE_READ_2(frm+3);
- batimeout = LE_READ_2(frm+5);
- baseqctl = LE_READ_2(frm+7);
+ baparamset = le16dec(frm+3);
+ batimeout = le16dec(frm+5);
+ baseqctl = le16dec(frm+7);
tid = MS(baparamset, IEEE80211_BAPS_TID);
@@ -1893,18 +2029,17 @@ ht_recv_action_ba_addba_response(struct ieee80211_node *ni,
struct ieee80211_tx_ampdu *tap;
uint8_t dialogtoken, policy;
uint16_t baparamset, batimeout, code;
- int tid, ac, bufsiz;
+ int tid, bufsiz;
dialogtoken = frm[2];
- code = LE_READ_2(frm+3);
- baparamset = LE_READ_2(frm+5);
+ code = le16dec(frm+3);
+ baparamset = le16dec(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);
+ batimeout = le16dec(frm+7);
- ac = TID_TO_WME_AC(tid);
- tap = &ni->ni_tx_ampdu[ac];
+ tap = &ni->ni_tx_ampdu[tid];
if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
@@ -1967,10 +2102,10 @@ ht_recv_action_ba_delba(struct ieee80211_node *ni,
struct ieee80211_rx_ampdu *rap;
struct ieee80211_tx_ampdu *tap;
uint16_t baparamset, code;
- int tid, ac;
+ int tid;
- baparamset = LE_READ_2(frm+2);
- code = LE_READ_2(frm+4);
+ baparamset = le16dec(frm+2);
+ code = le16dec(frm+4);
tid = MS(baparamset, IEEE80211_DELBAPS_TID);
@@ -1980,8 +2115,7 @@ ht_recv_action_ba_delba(struct ieee80211_node *ni,
MS(baparamset, IEEE80211_DELBAPS_INIT), code);
if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
- ac = TID_TO_WME_AC(tid);
- tap = &ni->ni_tx_ampdu[ac];
+ tap = &ni->ni_tx_ampdu[tid];
ic->ic_addba_stop(ni, tap);
} else {
rap = &ni->ni_rx_ampdu[tid];
@@ -2053,11 +2187,12 @@ ieee80211_ampdu_enable(struct ieee80211_node *ni,
{
struct ieee80211vap *vap = ni->ni_vap;
- if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[tap->txa_ac])
+ if (tap->txa_avgpps <
+ vap->iv_ampdu_mintraffic[TID_TO_WME_AC(tap->txa_tid)])
return 0;
/* XXX check rssi? */
if (tap->txa_attempts >= ieee80211_addba_maxtries &&
- ticks < tap->txa_nextrequest) {
+ ieee80211_time_after(ticks, tap->txa_nextrequest)) {
/*
* Don't retry too often; txa_nextrequest is set
* to the minimum interval we'll retry after
@@ -2066,8 +2201,9 @@ ieee80211_ampdu_enable(struct ieee80211_node *ni,
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);
+ "enable AMPDU on tid %d (%s), avgpps %d pkts %d attempt %d",
+ tap->txa_tid, ieee80211_wme_acnames[TID_TO_WME_AC(tap->txa_tid)],
+ tap->txa_avgpps, tap->txa_pkts, tap->txa_attempts);
return 1;
}
@@ -2094,7 +2230,7 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
tap->txa_flags &= ~IEEE80211_AGGR_NAK;
dialogtoken = (tokens+1) % 63; /* XXX */
- tid = WME_AC_TO_TID(tap->txa_ac);
+ tid = tap->txa_tid;
tap->txa_start = ni->ni_txseqs[tid];
args[0] = dialogtoken;
@@ -2108,8 +2244,8 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
if (!ic->ic_addba_request(ni, tap, dialogtoken, args[2], args[3])) {
/* 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);
+ ni, "%s: could not setup BA stream for TID %d AC %d",
+ __func__, tap->txa_tid, TID_TO_WME_AC(tap->txa_tid));
/* 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 */
@@ -2142,24 +2278,29 @@ ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
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);
+ ni, "%s: stop BA stream for TID %d (reason: %d (%s))",
+ __func__, tap->txa_tid, reason,
+ ieee80211_reason_to_string(reason));
vap->iv_stats.is_ampdu_stop++;
ic->ic_addba_stop(ni, tap);
- args[0] = WME_AC_TO_TID(tap->txa_ac);
+ args[0] = tap->txa_tid;
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);
+ ni, "%s: BA stream for TID %d not running "
+ "(reason: %d (%s))", __func__, tap->txa_tid, reason,
+ ieee80211_reason_to_string(reason));
vap->iv_stats.is_ampdu_stop_failed++;
}
}
+/* XXX */
+static void bar_start_timer(struct ieee80211_tx_ampdu *tap);
+
static void
bar_timeout(void *arg)
{
@@ -2171,27 +2312,64 @@ bar_timeout(void *arg)
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);
+ tap->txa_tid, 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)
+ if (tap->txa_attempts >= ieee80211_bar_maxtries) {
+ struct ieee80211com *ic = ni->ni_ic;
+
+ ni->ni_vap->iv_stats.is_ampdu_bar_tx_fail++;
+ /*
+ * If (at least) the last BAR TX timeout was due to
+ * an ieee80211_send_bar() failures, then we need
+ * to make sure we notify the driver that a BAR
+ * TX did occur and fail. This gives the driver
+ * a chance to undo any queue pause that may
+ * have occurred.
+ */
+ ic->ic_bar_response(ni, tap, 1);
ieee80211_ampdu_stop(ni, tap, IEEE80211_REASON_TIMEOUT);
- else
- ieee80211_send_bar(ni, tap, tap->txa_seqpending);
+ } else {
+ ni->ni_vap->iv_stats.is_ampdu_bar_tx_retry++;
+ if (ieee80211_send_bar(ni, tap, tap->txa_seqpending) != 0) {
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
+ ni, "%s: failed to TX, starting timer\n",
+ __func__);
+ /*
+ * If ieee80211_send_bar() fails here, the
+ * timer may have stopped and/or the pending
+ * flag may be clear. Because of this,
+ * fake the BARPEND and reset the timer.
+ * A retransmission attempt will then occur
+ * during the next timeout.
+ */
+ /* XXX locking */
+ tap->txa_flags |= IEEE80211_AGGR_BARPEND;
+ bar_start_timer(tap);
+ }
+ }
}
static void
bar_start_timer(struct ieee80211_tx_ampdu *tap)
{
+ IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+ tap->txa_ni,
+ "%s: called",
+ __func__);
callout_reset(&tap->txa_timer, ieee80211_bar_timeout, bar_timeout, tap);
}
static void
bar_stop_timer(struct ieee80211_tx_ampdu *tap)
{
+ IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+ tap->txa_ni,
+ "%s: called",
+ __func__);
callout_stop(&tap->txa_timer);
}
@@ -2202,9 +2380,10 @@ bar_tx_complete(struct ieee80211_node *ni, void *arg, int status)
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,
+ __func__, tap->txa_tid, tap->txa_flags,
callout_pending(&tap->txa_timer), status);
+ ni->ni_vap->iv_stats.is_ampdu_bar_tx++;
/* XXX locking */
if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) &&
callout_pending(&tap->txa_timer)) {
@@ -2222,13 +2401,17 @@ ieee80211_bar_response(struct ieee80211_node *ni,
struct ieee80211_tx_ampdu *tap, int status)
{
+ IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+ tap->txa_ni,
+ "%s: called",
+ __func__);
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));
+ tap->txa_tid);
/* NB: timer already stopped in bar_tx_complete */
tap->txa_start = tap->txa_seqpending;
@@ -2256,6 +2439,12 @@ ieee80211_send_bar(struct ieee80211_node *ni,
uint8_t *frm;
int tid, ret;
+
+ IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+ tap->txa_ni,
+ "%s: called",
+ __func__);
+
if ((tap->txa_flags & IEEE80211_AGGR_RUNNING) == 0) {
/* no ADDBA response, should not happen */
/* XXX stat+msg */
@@ -2283,7 +2472,7 @@ ieee80211_send_bar(struct ieee80211_node *ni,
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);
+ tid = tap->txa_tid;
barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
0 : IEEE80211_BAR_NOACK)
| IEEE80211_BAR_COMP
@@ -2316,10 +2505,16 @@ ieee80211_send_bar(struct ieee80211_node *ni,
* ic_raw_xmit will free the node reference
* regardless of queue/TX success or failure.
*/
- ret = ic->ic_raw_xmit(ni, m, NULL);
+ IEEE80211_TX_LOCK(ic);
+ ret = ieee80211_raw_output(vap, ni, m, NULL);
+ IEEE80211_TX_UNLOCK(ic);
if (ret != 0) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N,
+ ni, "send BAR: failed: (ret = %d)\n",
+ ret);
/* xmit failed, clear state flag */
tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
+ vap->iv_stats.is_ampdu_bar_tx_fail++;
return ret;
}
/* XXX hack against tx complete happening before timer is started */
@@ -2327,6 +2522,11 @@ ieee80211_send_bar(struct ieee80211_node *ni,
bar_start_timer(tap);
return 0;
bad:
+ IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N,
+ tap->txa_ni,
+ "%s: bad! ret=%d",
+ __func__, ret);
+ vap->iv_stats.is_ampdu_bar_tx_fail++;
ieee80211_free_node(ni);
return ret;
#undef senderr
@@ -2421,8 +2621,8 @@ ht_send_action_ba_delba(struct ieee80211_node *ni,
| 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]);
+ "send DELBA action: tid %d, initiator %d reason %d (%s)",
+ args[0], args[1], args[2], ieee80211_reason_to_string(args[2]));
IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
@@ -2567,10 +2767,32 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
caps |= IEEE80211_HTCAP_CHWIDTH40;
else
caps &= ~IEEE80211_HTCAP_CHWIDTH40;
- /* use advertised setting (XXX locally constraint) */
+
+ /* Start by using the advertised settings */
rxmax = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
+ "%s: advertised rxmax=%d, density=%d, vap rxmax=%d, density=%d\n",
+ __func__,
+ rxmax,
+ density,
+ vap->iv_ampdu_rxmax,
+ vap->iv_ampdu_density);
+
+ /* Cap at VAP rxmax */
+ if (rxmax > vap->iv_ampdu_rxmax)
+ rxmax = vap->iv_ampdu_rxmax;
+
+ /*
+ * If the VAP ampdu density value greater, use that.
+ *
+ * (Larger density value == larger minimum gap between A-MPDU
+ * subframes.)
+ */
+ if (vap->iv_ampdu_density > density)
+ density = vap->iv_ampdu_density;
+
/*
* NB: Hardware might support HT40 on some but not all
* channels. We can't determine this earlier because only
@@ -2587,15 +2809,25 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
caps |= IEEE80211_HTCAP_CHWIDTH40;
else
caps &= ~IEEE80211_HTCAP_CHWIDTH40;
+
+ /* XXX TODO should it start by using advertised settings? */
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;
+
+ /* adjust STBC based on receive capabilities */
+ if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_RX) == 0)
+ caps &= ~IEEE80211_HTCAP_RXSTBC;
+
+ /* XXX TODO: adjust LDPC based on receive capabilities */
+
ADDSHORT(frm, caps);
/* HT parameters */
@@ -2644,6 +2876,96 @@ ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni)
}
/*
+ * Non-associated probe request - add HT capabilities based on
+ * the current channel configuration.
+ */
+static uint8_t *
+ieee80211_add_htcap_body_ch(uint8_t *frm, struct ieee80211vap *vap,
+ struct ieee80211_channel *c)
+{
+#define ADDSHORT(frm, v) do { \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+} while (0)
+ struct ieee80211com *ic = vap->iv_ic;
+ uint16_t caps, extcaps;
+ int rxmax, density;
+
+ /* HT capabilities */
+ caps = vap->iv_htcaps & 0xffff;
+
+ /*
+ * We don't use this in STA mode; only in IBSS mode.
+ * So in IBSS mode we base our HTCAP flags on the
+ * given channel.
+ */
+
+ /* override 20/40 use based on current channel */
+ if (IEEE80211_IS_CHAN_HT40(c))
+ caps |= IEEE80211_HTCAP_CHWIDTH40;
+ else
+ caps &= ~IEEE80211_HTCAP_CHWIDTH40;
+
+ /* Use the currently configured values */
+ 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: For sta mode the rate set should be restricted based
+ * on the AP's capabilities, but ni_htrates isn't setup when
+ * we're called to form an AssocReq frame so for now we're
+ * restricted to the device capabilities.
+ */
+ ieee80211_set_mcsset(ic, frm);
+
+ frm += __offsetof(struct ieee80211_ie_htcap, hc_extcap) -
+ __offsetof(struct ieee80211_ie_htcap, hc_mcsset);
+
+ /* HT extended capabilities */
+ extcaps = vap->iv_htextcaps & 0xffff;
+
+ ADDSHORT(frm, extcaps);
+
+ frm += sizeof(struct ieee80211_ie_htcap) -
+ __offsetof(struct ieee80211_ie_htcap, hc_txbf);
+
+ return frm;
+#undef ADDSHORT
+}
+
+/*
+ * Add 802.11n HT capabilities information element
+ */
+uint8_t *
+ieee80211_add_htcap_ch(uint8_t *frm, struct ieee80211vap *vap,
+ struct ieee80211_channel *c)
+{
+ frm[0] = IEEE80211_ELEMID_HTCAP;
+ frm[1] = sizeof(struct ieee80211_ie_htcap) - 2;
+ return ieee80211_add_htcap_body_ch(frm + 2, vap, c);
+}
+
+/*
* Add Broadcom OUI wrapped standard HTCAP ie; this is
* used for compatibility w/ pre-draft implementations.
*/
@@ -2686,11 +3008,15 @@ 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 ieee80211_node *ni;
+ const struct ieee80211_channel *bsschan;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_ie_htinfo *ht =
(struct ieee80211_ie_htinfo *) bo->bo_htinfo;
+ ni = ieee80211_ref_node(vap->iv_bss);
+ bsschan = ni->ni_chan;
+
/* XXX only update on channel change */
ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan);
if (vap->iv_flags_ht & IEEE80211_FHT_RIFS)
@@ -2709,6 +3035,8 @@ ieee80211_ht_update_beacon(struct ieee80211vap *vap,
/* protection mode */
ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
+ ieee80211_free_node(ni);
+
/* XXX propagate to vendor ie's */
#undef PROTMODE
}
diff --git a/freebsd/sys/net80211/ieee80211_ht.h b/freebsd/sys/net80211/ieee80211_ht.h
index 249ddd2c..dfc7d1c3 100644
--- a/freebsd/sys/net80211/ieee80211_ht.h
+++ b/freebsd/sys/net80211/ieee80211_ht.h
@@ -44,7 +44,8 @@ struct ieee80211_tx_ampdu {
#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;
+#define IEEE80211_AGGR_WAITRX 0x0040 /* Wait for first RX frame to define BAW */
+ uint8_t txa_tid;
uint8_t txa_token; /* dialog token */
int txa_lastsample; /* ticks @ last traffic sample */
int txa_pkts; /* packets over last sample interval */
@@ -65,6 +66,10 @@ struct ieee80211_tx_ampdu {
#define IEEE80211_AMPDU_RUNNING(tap) \
(((tap)->txa_flags & IEEE80211_AGGR_RUNNING) != 0)
+/* return non-zero if AMPDU tx for the TID was NACKed */
+#define IEEE80211_AMPDU_NACKED(tap)\
+ (!! ((tap)->txa_flags & IEEE80211_AGGR_NAK))
+
/* return non-zero if AMPDU tx for the TID is running or started */
#define IEEE80211_AMPDU_REQUESTED(tap) \
(((tap)->txa_flags & \
@@ -84,8 +89,19 @@ struct ieee80211_tx_ampdu {
*/
static __inline void
+ieee80211_txampdu_init_pps(struct ieee80211_tx_ampdu *tap)
+{
+ /*
+ * Reset packet estimate.
+ */
+ tap->txa_lastsample = ticks;
+ tap->txa_avgpps = 0;
+}
+
+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;
@@ -97,6 +113,7 @@ ieee80211_txampdu_update_pps(struct ieee80211_tx_ampdu *tap)
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);
@@ -184,7 +201,7 @@ 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 *,
+int 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 *,
@@ -194,10 +211,19 @@ void ieee80211_ampdu_stop(struct ieee80211_node *,
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_ch(uint8_t *, struct ieee80211vap *,
+ struct ieee80211_channel *);
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 *);
+int ieee80211_ampdu_rx_start_ext(struct ieee80211_node *ni, int tid,
+ int seq, int baw);
+void ieee80211_ampdu_rx_stop_ext(struct ieee80211_node *ni, int tid);
+int ieee80211_ampdu_tx_request_ext(struct ieee80211_node *ni, int tid);
+int ieee80211_ampdu_tx_request_active_ext(struct ieee80211_node *ni,
+ int tid, int status);
+
#endif /* _NET80211_IEEE80211_HT_H_ */
diff --git a/freebsd/sys/net80211/ieee80211_hwmp.c b/freebsd/sys/net80211/ieee80211_hwmp.c
index 36cdc313..820b29d2 100644
--- a/freebsd/sys/net80211/ieee80211_hwmp.c
+++ b/freebsd/sys/net80211/ieee80211_hwmp.c
@@ -35,15 +35,15 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP.
- *
+ *
* Based on March 2009, D3.0 802.11s draft spec.
*/
#include <rtems/bsd/local/opt_inet.h>
#include <rtems/bsd/local/opt_wlan.h>
#include <rtems/bsd/sys/param.h>
-#include <sys/systm.h>
-#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
@@ -70,8 +70,7 @@ 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],
+static int hwmp_send_action(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN],
uint8_t *, size_t);
static uint8_t * hwmp_add_meshpreq(uint8_t *,
@@ -88,29 +87,29 @@ 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 *,
+static int hwmp_send_preq(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN],
- const uint8_t [IEEE80211_ADDR_LEN],
- struct ieee80211_meshpreq_ie *);
+ struct ieee80211_meshpreq_ie *,
+ struct timeval *, struct timeval *);
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],
+static int hwmp_send_prep(struct ieee80211vap *,
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],
+static int hwmp_send_perr(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN],
struct ieee80211_meshperr_ie *);
+static void hwmp_senderror(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ struct ieee80211_mesh_route *, int);
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],
+static int hwmp_send_rann(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN],
struct ieee80211_meshrann_ie *);
static struct ieee80211_node *
@@ -121,18 +120,6 @@ 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] =
@@ -141,8 +128,10 @@ static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] =
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_EQ(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)
+
+#define HWMP_SEQ_MAX(a, b) (a > b ? a : b)
/*
* Private extension of ieee80211_mesh_route.
@@ -151,14 +140,16 @@ 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 timeval hr_lastpreq; /* last time we sent a PREQ */
+ struct timeval hr_lastrootconf; /* last sent PREQ root conf */
+ int hr_preqretries; /* number of discoveries */
+ int hr_lastdiscovery; /* last discovery in ticks */
};
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 timeval hs_lastperr; /* last time we sent a PERR */
struct callout hs_roottimer;
uint8_t hs_maxhops; /* max hop count */
};
@@ -166,15 +157,21 @@ struct ieee80211_hwmp_state {
static 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,
+SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, 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_maxpreq_retries = -1;
+SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, maxpreq_retries, CTLTYPE_INT | CTLFLAG_RW,
+ &ieee80211_hwmp_maxpreq_retries, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "maximum number of preq retries");
+static int ieee80211_hwmp_net_diameter_traversaltime = -1;
+SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, net_diameter_traversal_time,
+ CTLTYPE_INT | CTLFLAG_RW, &ieee80211_hwmp_net_diameter_traversaltime, 0,
+ ieee80211_sysctl_msecs_ticks, "I",
+ "estimate travelse time across the MBSS (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",
@@ -187,6 +184,11 @@ 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)");
+static struct timeval ieee80211_hwmp_rootconfint = { 0, 0 };
+static int ieee80211_hwmp_rootconfint_internal = -1;
+SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootconfint, CTLTYPE_INT | CTLFLAG_RD,
+ &ieee80211_hwmp_rootconfint_internal, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "root confirmation interval (ms) (read-only)");
#define IEEE80211_HWMP_DEFAULT_MAXHOPS 31
@@ -197,6 +199,7 @@ static struct ieee80211_mesh_proto_path mesh_proto_hwmp = {
.mpp_ie = IEEE80211_MESHCONF_PATH_HWMP,
.mpp_discover = hwmp_discover,
.mpp_peerdown = hwmp_peerdown,
+ .mpp_senderror = hwmp_senderror,
.mpp_vattach = hwmp_vattach,
.mpp_vdetach = hwmp_vdetach,
.mpp_newstate = hwmp_newstate,
@@ -210,16 +213,31 @@ SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, inact, CTLTYPE_INT | CTLFLAG_RW,
static void
ieee80211_hwmp_init(void)
{
+ /* Default values as per amendment */
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);
+ ieee80211_hwmp_rootconfint_internal = msecs_to_ticks(2*1000);
+ ieee80211_hwmp_maxpreq_retries = 3;
+ /*
+ * (TU): A measurement of time equal to 1024 μs,
+ * 500 TU is 512 ms.
+ */
+ ieee80211_hwmp_net_diameter_traversaltime = msecs_to_ticks(512);
+
+ /*
+ * NB: I dont know how to make SYSCTL_PROC that calls ms to ticks
+ * and return a struct timeval...
+ */
+ ieee80211_hwmp_rootconfint.tv_usec =
+ ieee80211_hwmp_rootconfint_internal * 1000;
/*
* Register action frame handler.
*/
- ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
- IEEE80211_ACTION_MESHPATH_SEL, hwmp_recv_action_meshpath);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
+ IEEE80211_ACTION_MESH_HWMP, hwmp_recv_action_meshpath);
/* NB: default is 5 secs per spec */
mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000);
@@ -231,7 +249,7 @@ ieee80211_hwmp_init(void)
}
SYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL);
-void
+static void
hwmp_vattach(struct ieee80211vap *vap)
{
struct ieee80211_hwmp_state *hs;
@@ -239,28 +257,28 @@ hwmp_vattach(struct ieee80211vap *vap)
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);
+ hs = IEEE80211_MALLOC(sizeof(struct ieee80211_hwmp_state), M_80211_VAP,
+ IEEE80211_M_NOWAIT | IEEE80211_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);
+ callout_init(&hs->hs_roottimer, 1);
vap->iv_hwmp = hs;
}
-void
+static 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);
+ IEEE80211_FREE(vap->iv_hwmp, M_80211_VAP);
vap->iv_hwmp = NULL;
}
-int
+static int
hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg)
{
enum ieee80211_state nstate = vap->iv_state;
@@ -277,17 +295,114 @@ hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg)
return 0;
}
+/*
+ * Verify the length of an HWMP PREQ and return the number
+ * of destinations >= 1, if verification fails -1 is returned.
+ */
+static int
+verify_mesh_preq_len(struct ieee80211vap *vap,
+ const struct ieee80211_frame *wh, const uint8_t *iefrm)
+{
+ int alloc_sz = -1;
+ int ndest = -1;
+ if (iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE) {
+ /* Originator External Address present */
+ alloc_sz = IEEE80211_MESHPREQ_BASE_SZ_AE;
+ ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET_AE];
+ } else {
+ /* w/o Originator External Address */
+ alloc_sz = IEEE80211_MESHPREQ_BASE_SZ;
+ ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET];
+ }
+ alloc_sz += ndest * IEEE80211_MESHPREQ_TRGT_SZ;
+
+ if(iefrm[1] != (alloc_sz)) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "PREQ (AE=%s) with wrong len",
+ iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE ? "1" : "0");
+ return (-1);
+ }
+ return ndest;
+}
+
+/*
+ * Verify the length of an HWMP PREP and returns 1 on success,
+ * otherwise -1.
+ */
+static int
+verify_mesh_prep_len(struct ieee80211vap *vap,
+ const struct ieee80211_frame *wh, const uint8_t *iefrm)
+{
+ int alloc_sz = -1;
+ if (iefrm[2] & IEEE80211_MESHPREP_FLAGS_AE) {
+ if (iefrm[1] == IEEE80211_MESHPREP_BASE_SZ_AE)
+ alloc_sz = IEEE80211_MESHPREP_BASE_SZ_AE;
+ } else if (iefrm[1] == IEEE80211_MESHPREP_BASE_SZ)
+ alloc_sz = IEEE80211_MESHPREP_BASE_SZ;
+ if(alloc_sz < 0) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "PREP (AE=%s) with wrong len",
+ iefrm[2] & IEEE80211_MESHPREP_FLAGS_AE ? "1" : "0");
+ return (-1);
+ }
+ return (1);
+}
+
+/*
+ * Verify the length of an HWMP PERR and return the number
+ * of destinations >= 1, if verification fails -1 is returned.
+ */
+static int
+verify_mesh_perr_len(struct ieee80211vap *vap,
+ const struct ieee80211_frame *wh, const uint8_t *iefrm)
+{
+ int alloc_sz = -1;
+ const uint8_t *iefrm_t = iefrm;
+ uint8_t ndest = iefrm_t[IEEE80211_MESHPERR_NDEST_OFFSET];
+ int i;
+
+ if(ndest > IEEE80211_MESHPERR_MAXDEST) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "PERR with wrong number of destionat (>19), %u",
+ ndest);
+ return (-1);
+ }
+
+ iefrm_t += IEEE80211_MESHPERR_NDEST_OFFSET + 1; /* flag is next field */
+ /* We need to check each destionation flag to know size */
+ for(i = 0; i<ndest; i++) {
+ if ((*iefrm_t) & IEEE80211_MESHPERR_FLAGS_AE)
+ iefrm_t += IEEE80211_MESHPERR_DEST_SZ_AE;
+ else
+ iefrm_t += IEEE80211_MESHPERR_DEST_SZ;
+ }
+
+ alloc_sz = (iefrm_t - iefrm) - 2; /* action + code */
+ if(alloc_sz != iefrm[1]) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
+ wh, NULL, "%s", "PERR with wrong len");
+ return (-1);
+ }
+ return ndest;
+}
+
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_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 */
+ const uint8_t *iefrm_t = iefrm; /* temporary pointer */
+ int ndest = -1;
int found = 0;
while (efrm - iefrm > 1) {
@@ -295,66 +410,135 @@ hwmp_recv_action_meshpath(struct ieee80211_node *ni,
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");
+ int i = 0;
+
+ iefrm_t = iefrm;
+ ndest = verify_mesh_preq_len(vap, wh, iefrm_t);
+ if (ndest < 0) {
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);
+ preq = IEEE80211_MALLOC(sizeof(*preq) +
+ (ndest - 1) * sizeof(*preq->preq_targets),
+ M_80211_MESH_PREQ,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ KASSERT(preq != NULL, ("preq == NULL"));
+
+ preq->preq_ie = *iefrm_t++;
+ preq->preq_len = *iefrm_t++;
+ preq->preq_flags = *iefrm_t++;
+ preq->preq_hopcount = *iefrm_t++;
+ preq->preq_ttl = *iefrm_t++;
+ preq->preq_id = le32dec(iefrm_t); iefrm_t += 4;
+ IEEE80211_ADDR_COPY(preq->preq_origaddr, iefrm_t);
+ iefrm_t += 6;
+ preq->preq_origseq = le32dec(iefrm_t); iefrm_t += 4;
+ /* NB: may have Originator Proxied Address */
+ if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE) {
+ IEEE80211_ADDR_COPY(
+ preq->preq_orig_ext_addr, iefrm_t);
+ iefrm_t += 6;
+ }
+ preq->preq_lifetime = le32dec(iefrm_t); iefrm_t += 4;
+ preq->preq_metric = le32dec(iefrm_t); iefrm_t += 4;
+ preq->preq_tcount = *iefrm_t++;
+
+ for (i = 0; i < preq->preq_tcount; i++) {
+ preq->preq_targets[i].target_flags = *iefrm_t++;
+ IEEE80211_ADDR_COPY(
+ preq->preq_targets[i].target_addr, iefrm_t);
+ iefrm_t += 6;
+ preq->preq_targets[i].target_seq =
+ le32dec(iefrm_t);
+ iefrm_t += 4;
+ }
+
+ hwmp_recv_preq(vap, ni, wh, preq);
+ IEEE80211_FREE(preq, M_80211_MESH_PREQ);
found++;
- break;
+ 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");
+ iefrm_t = iefrm;
+ ndest = verify_mesh_prep_len(vap, wh, iefrm_t);
+ if (ndest < 0) {
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);
+ prep = IEEE80211_MALLOC(sizeof(*prep),
+ M_80211_MESH_PREP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ KASSERT(prep != NULL, ("prep == NULL"));
+
+ prep->prep_ie = *iefrm_t++;
+ prep->prep_len = *iefrm_t++;
+ prep->prep_flags = *iefrm_t++;
+ prep->prep_hopcount = *iefrm_t++;
+ prep->prep_ttl = *iefrm_t++;
+ IEEE80211_ADDR_COPY(prep->prep_targetaddr, iefrm_t);
+ iefrm_t += 6;
+ prep->prep_targetseq = le32dec(iefrm_t); iefrm_t += 4;
+ /* NB: May have Target Proxied Address */
+ if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE) {
+ IEEE80211_ADDR_COPY(
+ prep->prep_target_ext_addr, iefrm_t);
+ iefrm_t += 6;
+ }
+ prep->prep_lifetime = le32dec(iefrm_t); iefrm_t += 4;
+ prep->prep_metric = le32dec(iefrm_t); iefrm_t += 4;
+ IEEE80211_ADDR_COPY(prep->prep_origaddr, iefrm_t);
+ iefrm_t += 6;
+ prep->prep_origseq = le32dec(iefrm_t); iefrm_t += 4;
+
+ hwmp_recv_prep(vap, ni, wh, prep);
+ IEEE80211_FREE(prep, M_80211_MESH_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");
+ int i = 0;
+
+ iefrm_t = iefrm;
+ ndest = verify_mesh_perr_len(vap, wh, iefrm_t);
+ if (ndest < 0) {
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);
+ perr = IEEE80211_MALLOC(sizeof(*perr) +
+ (ndest - 1) * sizeof(*perr->perr_dests),
+ M_80211_MESH_PERR,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ KASSERT(perr != NULL, ("perr == NULL"));
+
+ perr->perr_ie = *iefrm_t++;
+ perr->perr_len = *iefrm_t++;
+ perr->perr_ttl = *iefrm_t++;
+ perr->perr_ndests = *iefrm_t++;
+
+ for (i = 0; i<perr->perr_ndests; i++) {
+ perr->perr_dests[i].dest_flags = *iefrm_t++;
+ IEEE80211_ADDR_COPY(
+ perr->perr_dests[i].dest_addr, iefrm_t);
+ iefrm_t += 6;
+ perr->perr_dests[i].dest_seq = le32dec(iefrm_t);
+ iefrm_t += 4;
+ /* NB: May have Target Proxied Address */
+ if (perr->perr_dests[i].dest_flags &
+ IEEE80211_MESHPERR_FLAGS_AE) {
+ IEEE80211_ADDR_COPY(
+ perr->perr_dests[i].dest_ext_addr,
+ iefrm_t);
+ iefrm_t += 6;
+ }
+ perr->perr_dests[i].dest_rcode =
+ le16dec(iefrm_t);
+ iefrm_t += 2;
+ }
+
+ hwmp_recv_perr(vap, ni, wh, perr);
+ IEEE80211_FREE(perr, M_80211_MESH_PERR);
found++;
break;
}
@@ -367,12 +551,13 @@ hwmp_recv_action_meshpath(struct ieee80211_node *ni,
IEEE80211_DISCARD(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
wh, NULL, "%s", "RAN with wrong len");
- vap->iv_stats.is_rx_mgtdiscard++;
+ 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);
+ rann.rann_seq = le32dec(&mrann->rann_seq);
+ rann.rann_interval = le32dec(&mrann->rann_interval);
+ rann.rann_metric = le32dec(&mrann->rann_metric);
hwmp_recv_rann(vap, ni, wh, &rann);
found++;
break;
@@ -390,16 +575,30 @@ hwmp_recv_action_meshpath(struct ieee80211_node *ni,
}
static int
-hwmp_send_action(struct ieee80211_node *ni,
- const uint8_t sa[IEEE80211_ADDR_LEN],
+hwmp_send_action(struct ieee80211vap *vap,
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_node *ni;
+ struct ieee80211com *ic;
struct ieee80211_bpf_params params;
struct mbuf *m;
uint8_t *frm;
+ int ret;
+
+ if (IEEE80211_IS_MULTICAST(da)) {
+ ni = ieee80211_ref_node(vap->iv_bss);
+#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);
+ }
+ else
+ ni = ieee80211_mesh_find_txnode(vap, da);
if (vap->iv_state == IEEE80211_S_CAC) {
IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
@@ -409,19 +608,7 @@ hwmp_send_action(struct ieee80211_node *ni,
}
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);
+ ic = ni->ni_ic;
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
@@ -432,8 +619,8 @@ hwmp_send_action(struct ieee80211_node *ni,
vap->iv_stats.is_tx_nobuf++;
return ENOMEM;
}
- *frm++ = IEEE80211_ACTION_CAT_MESHPATH;
- *frm++ = IEEE80211_ACTION_MESHPATH_SEL;
+ *frm++ = IEEE80211_ACTION_CAT_MESH;
+ *frm++ = IEEE80211_ACTION_MESH_HWMP;
switch (*ie) {
case IEEE80211_ELEMID_MESHPREQ:
frm = hwmp_add_meshpreq(frm,
@@ -454,15 +641,18 @@ hwmp_send_action(struct ieee80211_node *ni,
}
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
if (m == NULL) {
ieee80211_free_node(ni);
vap->iv_stats.is_tx_nobuf++;
return ENOMEM;
}
+
+ IEEE80211_TX_LOCK(ic);
+
ieee80211_send_setup(ni, m,
IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
- IEEE80211_NONQOS_TID, sa, da, sa);
+ IEEE80211_NONQOS_TID, vap->iv_myaddr, da, vap->iv_myaddr);
m->m_flags |= M_ENCAP; /* mark encapsulated */
IEEE80211_NODE_STAT(ni, tx_mgmt);
@@ -475,46 +665,56 @@ hwmp_send_action(struct ieee80211_node *ni,
else
params.ibp_try0 = ni->ni_txparms->maxretry;
params.ibp_power = ni->ni_txpower;
- return ic->ic_raw_xmit(ni, m, &params);
+ ret = ieee80211_raw_output(vap, ni, m, &params);
+ IEEE80211_TX_UNLOCK(ic);
+ return (ret);
}
#define ADDSHORT(frm, v) do { \
- frm[0] = (v) & 0xff; \
- frm[1] = (v) >> 8; \
+ le16enc(frm, v); \
frm += 2; \
} while (0)
#define ADDWORD(frm, v) do { \
- LE_WRITE_4(frm, v); \
+ le32enc(frm, v); \
frm += 4; \
} while (0)
/*
* Add a Mesh Path Request IE to a frame.
*/
+#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 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_len; /* len already calculated */
*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);
+ if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE) {
+ IEEE80211_ADDR_COPY(frm, preq->preq_orig_ext_addr);
+ frm += 6;
+ }
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++ = PREQ_TFLAGS(i);
+ IEEE80211_ADDR_COPY(frm, PREQ_TADDR(i));
frm += 6;
- ADDWORD(frm, preq->preq_targets[i].target_seq);
+ ADDWORD(frm, PREQ_TSEQ(i));
}
return frm;
}
+#undef PREQ_TFLAGS
+#undef PREQ_TADDR
+#undef PREQ_TSEQ
/*
* Add a Mesh Path Reply IE to a frame.
@@ -523,12 +723,16 @@ 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_len; /* len already calculated */
*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);
+ if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE) {
+ IEEE80211_ADDR_COPY(frm, prep->prep_target_ext_addr);
+ frm += 6;
+ }
ADDWORD(frm, prep->prep_lifetime);
ADDWORD(frm, prep->prep_metric);
IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6;
@@ -539,25 +743,38 @@ hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep)
/*
* Add a Mesh Path Error IE to a frame.
*/
+#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_EXTADDR(n) perr->perr_dests[n].dest_ext_addr
+#define PERR_DRCODE(n) perr->perr_dests[n].dest_rcode
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_len; /* len already calculated */
*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++ = PERR_DFLAGS(i);
+ IEEE80211_ADDR_COPY(frm, PERR_DADDR(i));
frm += 6;
- ADDWORD(frm, perr->perr_dests[i].dest_seq);
- ADDSHORT(frm, perr->perr_dests[i].dest_rcode);
+ ADDWORD(frm, PERR_DSEQ(i));
+ if (PERR_DFLAGS(i) & IEEE80211_MESHPERR_FLAGS_AE) {
+ IEEE80211_ADDR_COPY(frm, PERR_EXTADDR(i));
+ frm += 6;
+ }
+ ADDSHORT(frm, PERR_DRCODE(i));
}
return frm;
}
+#undef PERR_DFLAGS
+#undef PERR_DADDR
+#undef PERR_DSEQ
+#undef PERR_EXTADDR
+#undef PERR_DRCODE
/*
* Add a Root Annoucement IE to a frame.
@@ -566,12 +783,13 @@ 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_len;
*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_interval);
ADDWORD(frm, rann->rann_metric);
return frm;
}
@@ -580,19 +798,23 @@ static void
hwmp_rootmode_setup(struct ieee80211vap *vap)
{
struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
switch (hs->hs_rootmode) {
case IEEE80211_HWMP_ROOTMODE_DISABLED:
callout_drain(&hs->hs_roottimer);
+ ms->ms_flags &= ~IEEE80211_MESHFLAGS_ROOT;
break;
case IEEE80211_HWMP_ROOTMODE_NORMAL:
case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint,
hwmp_rootmode_cb, vap);
+ ms->ms_flags |= IEEE80211_MESHFLAGS_ROOT;
break;
case IEEE80211_HWMP_ROOTMODE_RANN:
callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint,
hwmp_rootmode_rann_cb, vap);
+ ms->ms_flags |= IEEE80211_MESHFLAGS_ROOT;
break;
}
}
@@ -615,9 +837,9 @@ hwmp_rootmode_cb(void *arg)
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;
+ preq.preq_flags = 0;
+ if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
+ preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_GATE;
if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
preq.preq_hopcount = 0;
@@ -630,10 +852,11 @@ hwmp_rootmode_cb(void *arg)
preq.preq_tcount = 1;
IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
- IEEE80211_MESHPREQ_TFLAGS_RF;
+ IEEE80211_MESHPREQ_TFLAGS_USN;
PREQ_TSEQ(0) = 0;
vap->iv_stats.is_hwmp_rootreqs++;
- hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq);
+ /* NB: we enforce rate check ourself */
+ hwmp_send_preq(vap, broadcastaddr, &preq, NULL, NULL);
hwmp_rootmode_setup(vap);
}
#undef PREQ_TFLAGS
@@ -656,19 +879,59 @@ hwmp_rootmode_rann_cb(void *arg)
"%s", "send broadcast RANN");
rann.rann_flags = 0;
- if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
- rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR;
+ if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
+ rann.rann_flags |= IEEE80211_MESHFLAGS_GATE;
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_interval = ieee80211_hwmp_rannint;
rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
vap->iv_stats.is_hwmp_rootrann++;
- hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann);
+ hwmp_send_rann(vap, broadcastaddr, &rann);
hwmp_rootmode_setup(vap);
}
+/*
+ * Update forwarding information to TA if metric improves.
+ */
+static void
+hwmp_update_transmitter(struct ieee80211vap *vap, struct ieee80211_node *ni,
+ const char *hwmp_frame)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rttran = NULL; /* Transmitter */
+ int metric = 0;
+
+ rttran = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
+ if (rttran == NULL) {
+ rttran = ieee80211_mesh_rt_add(vap, ni->ni_macaddr);
+ if (rttran == NULL) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "unable to add path to transmitter %6D of %s",
+ ni->ni_macaddr, ":", hwmp_frame);
+ vap->iv_stats.is_mesh_rtaddfailed++;
+ return;
+ }
+ }
+ metric = ms->ms_pmetric->mpm_metric(ni);
+ if (!(rttran->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) ||
+ rttran->rt_metric > metric)
+ {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "%s path to transmiter %6D of %s, metric %d:%d",
+ rttran->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
+ "prefer" : "update", ni->ni_macaddr, ":", hwmp_frame,
+ rttran->rt_metric, metric);
+ IEEE80211_ADDR_COPY(rttran->rt_nexthop, ni->ni_macaddr);
+ rttran->rt_metric = metric;
+ rttran->rt_nhops = 1;
+ ieee80211_mesh_rt_update(rttran, ms->ms_ppath->mpp_inact);
+ rttran->rt_flags = IEEE80211_MESHRT_FLAGS_VALID;
+ }
+}
+
#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
@@ -677,15 +940,15 @@ 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_mesh_route *rtorig_ext = NULL;
+ struct ieee80211_mesh_route *rttarg = NULL;
+ struct ieee80211_hwmp_route *hrorig = NULL;
+ struct ieee80211_hwmp_route *hrtarg = NULL;
struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
- struct ieee80211_meshprep_ie prep;
+ ieee80211_hwmp_seq preqid; /* last seen preqid for orig */
+ uint32_t metric = 0;
- 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.
@@ -694,371 +957,484 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
return;
IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
- "received PREQ, source %s", ether_sprintf(preq->preq_origaddr));
+ "received PREQ, orig %6D, targ(0) %6D", preq->preq_origaddr, ":",
+ PREQ_TADDR(0), ":");
/*
- * Acceptance criteria: if the PREQ is not for us and
- * forwarding is disabled, discard this PREQ.
+ * Acceptance criteria: (if the PREQ is not for us or not broadcast,
+ * or an external mac address not proxied by us),
+ * AND forwarding is disabled, discard this PREQ.
*/
- if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
- !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
+ rttarg = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
+ if (!(ms->ms_flags & IEEE80211_MESHFLAGS_FWD) &&
+ (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
+ !IEEE80211_IS_MULTICAST(PREQ_TADDR(0)) ||
+ (rttarg != NULL &&
+ rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY &&
+ IEEE80211_ADDR_EQ(vap->iv_myaddr, rttarg->rt_mesh_gate)))) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
return;
}
+ /*
+ * Acceptance criteria: if unicast addressed
+ * AND no valid forwarding for Target of PREQ, discard this PREQ.
+ */
+ if(rttarg != NULL)
+ hrtarg = IEEE80211_MESH_ROUTE_PRIV(rttarg,
+ struct ieee80211_hwmp_route);
+ /* Address mode: ucast */
+ if(preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AM &&
+ rttarg == NULL &&
+ !IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
+ preq->preq_origaddr, NULL,
+ "unicast addressed PREQ of unknown target %6D",
+ PREQ_TADDR(0), ":");
+ return;
+ }
+
+ /* PREQ ACCEPTED */
+
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;
+ rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
+ if (rtorig == NULL) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "unable to add orig path to %6D",
+ preq->preq_origaddr, ":");
+ vap->iv_stats.is_mesh_rtaddfailed++;
+ return;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "adding originator %6D", preq->preq_origaddr, ":");
}
hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
- /*
- * Sequence number validation.
+
+ /* record last seen preqid */
+ preqid = hrorig->hr_preqid;
+ hrorig->hr_preqid = HWMP_SEQ_MAX(hrorig->hr_preqid, preq->preq_id);
+
+ /* Data creation and update of forwarding information
+ * according to Table 11C-8 for originator mesh STA.
*/
- if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) &&
- HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) {
+ metric = preq->preq_metric + ms->ms_pmetric->mpm_metric(ni);
+ if (HWMP_SEQ_GT(preq->preq_origseq, hrorig->hr_seq) ||
+ (HWMP_SEQ_EQ(preq->preq_origseq, hrorig->hr_seq) &&
+ metric < rtorig->rt_metric)) {
+ hrorig->hr_seq = preq->preq_origseq;
+ IEEE80211_ADDR_COPY(rtorig->rt_nexthop, wh->i_addr2);
+ rtorig->rt_metric = metric;
+ rtorig->rt_nhops = preq->preq_hopcount + 1;
+ ieee80211_mesh_rt_update(rtorig, preq->preq_lifetime);
+ /* Path to orig is valid now.
+ * NB: we know it can't be Proxy, and if it is GATE
+ * it will be marked below.
+ */
+ rtorig->rt_flags = IEEE80211_MESHRT_FLAGS_VALID;
+ } else if ((hrtarg != NULL &&
+ !HWMP_SEQ_EQ(hrtarg->hr_seq, PREQ_TSEQ(0))) ||
+ (rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
+ preqid >= preq->preq_id)) {
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);
+ "discard PREQ from %6D, old seqno %u <= %u,"
+ " or old preqid %u < %u",
+ preq->preq_origaddr, ":",
+ preq->preq_origseq, hrorig->hr_seq,
+ preq->preq_id, preqid);
return;
}
- hrorig->hr_preqid = preq->preq_id;
- hrorig->hr_seq = preq->preq_origseq;
+
+ /* Update forwarding information to TA if metric improves. */
+ hwmp_update_transmitter(vap, ni, "PREQ");
/*
* Check if the PREQ is addressed to us.
+ * or a Proxy currently gated by 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));
+ if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) ||
+ (ms->ms_flags & IEEE80211_MESHFLAGS_GATE &&
+ rttarg != NULL &&
+ IEEE80211_ADDR_EQ(vap->iv_myaddr, rttarg->rt_mesh_gate) &&
+ rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY &&
+ rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
+ struct ieee80211_meshprep_ie prep;
+
/*
- * Build and send a PREP frame.
+ * When we are the target we shall update our own HWMP seq
+ * number with max of (current and preq->seq) + 1
*/
+ hs->hs_seq = HWMP_SEQ_MAX(hs->hs_seq, PREQ_TSEQ(0)) + 1;
+
prep.prep_flags = 0;
prep.prep_hopcount = 0;
- prep.prep_ttl = ms->ms_ttl;
+ prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
- prep.prep_targetseq = ++hs->hs_seq;
+ if (rttarg != NULL && /* if NULL it means we are the target */
+ rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "reply for proxy %6D", rttarg->rt_dest, ":");
+ prep.prep_flags |= IEEE80211_MESHPREP_FLAGS_AE;
+ IEEE80211_ADDR_COPY(prep.prep_target_ext_addr,
+ rttarg->rt_dest);
+ /* update proxy seqno to HWMP seqno */
+ rttarg->rt_ext_seq = hs->hs_seq;
+ prep.prep_hopcount = rttarg->rt_nhops;
+ prep.prep_metric = rttarg->rt_metric;
+ IEEE80211_ADDR_COPY(prep.prep_targetaddr, rttarg->rt_mesh_gate);
+ }
+ /*
+ * Build and send a PREP frame.
+ */
+ prep.prep_ttl = ms->ms_ttl;
+ 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);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "reply to %6D", preq->preq_origaddr, ":");
+ hwmp_send_prep(vap, wh->i_addr2, &prep);
return;
}
+ /* we may update our proxy information for the orig external */
+ else if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE) {
+ rtorig_ext =
+ ieee80211_mesh_rt_find(vap, preq->preq_orig_ext_addr);
+ if (rtorig_ext == NULL) {
+ rtorig_ext = ieee80211_mesh_rt_add(vap,
+ preq->preq_orig_ext_addr);
+ if (rtorig_ext == NULL) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "unable to add orig ext proxy to %6D",
+ preq->preq_orig_ext_addr, ":");
+ vap->iv_stats.is_mesh_rtaddfailed++;
+ return;
+ }
+ IEEE80211_ADDR_COPY(rtorig_ext->rt_mesh_gate,
+ preq->preq_origaddr);
+ }
+ rtorig_ext->rt_ext_seq = preq->preq_origseq;
+ ieee80211_mesh_rt_update(rtorig_ext, preq->preq_lifetime);
+ }
/*
* 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];
+ (PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "root mesh station @ %6D", preq->preq_origaddr, ":");
- 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;
- }
+ /* Check if root is a mesh gate, mark it */
+ if (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_GATE) {
+ struct ieee80211_mesh_gate_route *gr;
+
+ rtorig->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
+ gr = ieee80211_mesh_mark_gate(vap, preq->preq_origaddr,
+ rtorig);
+ gr->gr_lastseq = 0; /* NOT GANN */
}
- 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 ||
+ if ((rtorig->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
(preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
+ struct ieee80211_meshprep_ie prep;
+
prep.prep_flags = 0;
prep.prep_hopcount = 0;
prep.prep_ttl = ms->ms_ttl;
- IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac);
+ IEEE80211_ADDR_COPY(prep.prep_origaddr,
+ preq->preq_origaddr);
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_send_prep(vap, rtorig->rt_nexthop, &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) {
+ if ((preq->preq_tcount == 1) && (preq->preq_ttl > 1) &&
+ (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
memcpy(&ppreq, preq, sizeof(ppreq));
+
/*
* We have a valid route to this node.
+ * NB: if target is proxy dont reply.
*/
- 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);
- }
+ if (rttarg != NULL &&
+ rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
+ !(rttarg->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)) {
/*
* Check if we can send an intermediate Path Reply,
- * i.e., Target Only bit is not set.
+ * i.e., Target Only bit is not set and target is not
+ * the MAC broadcast address.
*/
- if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
+ if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO) &&
+ !IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr)) {
struct ieee80211_meshprep_ie prep;
IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
- "intermediate reply for PREQ from %s",
- ether_sprintf(preq->preq_origaddr));
+ "intermediate reply for PREQ from %6D",
+ preq->preq_origaddr, ":");
prep.prep_flags = 0;
- prep.prep_hopcount = rt->rt_nhops + 1;
+ prep.prep_hopcount = rttarg->rt_nhops;
prep.prep_ttl = ms->ms_ttl;
IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
PREQ_TADDR(0));
- prep.prep_targetseq = hrorig->hr_seq;
+ prep.prep_targetseq = hrtarg->hr_seq;
prep.prep_lifetime = preq->preq_lifetime;
- prep.prep_metric = rt->rt_metric +
- ms->ms_pmetric->mpm_metric(ni);
+ prep.prep_metric =rttarg->rt_metric;
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;
+ hwmp_send_prep(vap, rtorig->rt_nexthop, &prep);
- 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);
+ /*
+ * Set TO and unset RF bits because we have
+ * sent a PREP.
+ */
+ ppreq.preq_targets[0].target_flags |=
+ IEEE80211_MESHPREQ_TFLAGS_TO;
+ }
}
- }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "forward PREQ from %6D",
+ preq->preq_origaddr, ":");
+ ppreq.preq_hopcount += 1;
+ ppreq.preq_ttl -= 1;
+ ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
+
+ /* don't do PREQ ratecheck when we propagate */
+ hwmp_send_preq(vap, broadcastaddr, &ppreq, NULL, NULL);
+ }
}
#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],
+hwmp_send_preq(struct ieee80211vap *vap,
const uint8_t da[IEEE80211_ADDR_LEN],
- struct ieee80211_meshpreq_ie *preq)
+ struct ieee80211_meshpreq_ie *preq,
+ struct timeval *last, struct timeval *minint)
{
- struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
/*
* Enforce PREQ interval.
+ * NB: Proactive ROOT PREQs rate is handled by cb task.
*/
- if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
- return EALREADY;
- getmicrouptime(&hs->hs_lastpreq);
+ if (last != NULL && minint != NULL) {
+ if (ratecheck(last, minint) == 0)
+ return EALREADY; /* XXX: we should postpone */
+ getmicrouptime(last);
+ }
/*
* mesh preq action frame format
* [6] da
- * [6] sa
+ * [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));
+ preq->preq_len = (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_AE ?
+ IEEE80211_MESHPREQ_BASE_SZ_AE : IEEE80211_MESHPREQ_BASE_SZ) +
+ preq->preq_tcount * IEEE80211_MESHPREQ_TRGT_SZ;
+ return hwmp_send_action(vap, da, (uint8_t *)preq, preq->preq_len+2);
}
static void
hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
{
+#define IS_PROXY(rt) (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)
+#define PROXIED_BY_US(rt) \
+ (IEEE80211_ADDR_EQ(vap->iv_myaddr, rt->rt_mesh_gate))
struct ieee80211_mesh_state *ms = vap->iv_mesh;
struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
struct ieee80211_mesh_route *rt = NULL;
+ struct ieee80211_mesh_route *rtorig = NULL;
+ struct ieee80211_mesh_route *rtext = NULL;
struct ieee80211_hwmp_route *hr;
struct ieee80211com *ic = vap->iv_ic;
- struct ifnet *ifp = vap->iv_ifp;
struct mbuf *m, *next;
+ uint32_t metric = 0;
+ const uint8_t *addr;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "received PREP, orig %6D, targ %6D", prep->prep_origaddr, ":",
+ prep->prep_targetaddr, ":");
/*
- * Acceptance criteria: if the corresponding PREQ was not generated
- * by us and forwarding is disabled, discard this PREP.
+ * Acceptance criteria: (If the corresponding PREP was not generated
+ * by us OR not generated by an external mac that is not proxied 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))
+ rtorig = ieee80211_mesh_rt_find(vap, prep->prep_origaddr);
+ if ((!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) ||
+ (rtorig != NULL && IS_PROXY(rtorig) && !PROXIED_BY_US(rtorig))) &&
+ !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)){
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "discard PREP, orig(%6D) not proxied or generated by us",
+ prep->prep_origaddr, ":");
return;
+ }
- IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
- "received PREP from %s", ether_sprintf(prep->prep_targetaddr));
+ /* PREP ACCEPTED */
+ /*
+ * If accepted shall create or update the active forwarding information
+ * it maintains for the target mesh STA of the PREP (according to the
+ * rules defined in 13.10.8.4). If the conditions for creating or
+ * updating the forwarding information have not been met in those
+ * rules, no further steps are applied to the PREP.
+ */
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;
+ rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
+ if (rt == NULL) {
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);
+ "unable to add PREP path to %6D",
+ prep->prep_targetaddr, ":");
+ vap->iv_stats.is_mesh_rtaddfailed++;
return;
- }
- return;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "adding target %6D", prep->prep_targetaddr, ":");
}
- /*
- * 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;
+ /* update path metric */
+ metric = prep->prep_metric + ms->ms_pmetric->mpm_metric(ni);
+ if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
+ if (HWMP_SEQ_LT(prep->prep_targetseq, hr->hr_seq)) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "discard PREP from %6D, old seq no %u < %u",
+ prep->prep_targetaddr, ":",
+ prep->prep_targetseq, hr->hr_seq);
+ return;
+ } else if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq) &&
+ metric > rt->rt_metric) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "discard PREP from %6D, new metric %u > %u",
+ prep->prep_targetaddr, ":",
+ metric, rt->rt_metric);
+ return;
+ }
}
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "%s path to %6D, hopcount %d:%d metric %d:%d",
+ rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
+ "prefer" : "update",
+ prep->prep_targetaddr, ":",
+ rt->rt_nhops, prep->prep_hopcount + 1,
+ rt->rt_metric, metric);
+
hr->hr_seq = prep->prep_targetseq;
+ hr->hr_preqretries = 0;
+ IEEE80211_ADDR_COPY(rt->rt_nexthop, ni->ni_macaddr);
+ rt->rt_metric = metric;
+ rt->rt_nhops = prep->prep_hopcount + 1;
+ ieee80211_mesh_rt_update(rt, prep->prep_lifetime);
+ if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER) {
+ /* discovery complete */
+ rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_DISCOVER;
+ }
+ rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; /* mark valid */
+
+ /* Update forwarding information to TA if metric improves */
+ hwmp_update_transmitter(vap, ni, "PREP");
+
/*
- * If it's NOT for us, propagate the PREP.
+ * 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) {
+ prep->prep_ttl > 1 &&
+ prep->prep_hopcount < hs->hs_maxhops) {
struct ieee80211_meshprep_ie pprep; /* propagated PREP */
+ /*
+ * NB: We should already have setup the path to orig
+ * mesh STA when we propagated PREQ to target mesh STA,
+ * no PREP is generated without a corresponding PREQ.
+ * XXX: for now just ignore.
+ */
+ if (rtorig == NULL) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "received PREP for an unknown orig(%6D)",
+ prep->prep_origaddr, ":");
+ return;
+ }
IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
- "propagate PREP from %s",
- ether_sprintf(prep->prep_targetaddr));
+ "propagate PREP from %6D",
+ 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);
+ hwmp_send_prep(vap, rtorig->rt_nexthop, &pprep);
+
+ /* precursor list for the Target Mesh STA Address is updated */
}
- hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
- if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
- /* NB: never clobber a proxy entry */;
+
+ /*
+ * Check if we received a PREP w/ AE and store target external address.
+ * We may store target external address if recevied PREP w/ AE
+ * and we are not final destination
+ */
+ if (prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE) {
+ rtext = ieee80211_mesh_rt_find(vap,
+ prep->prep_target_ext_addr);
+ if (rtext == NULL) {
+ rtext = ieee80211_mesh_rt_add(vap,
+ prep->prep_target_ext_addr);
+ if (rtext == NULL) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "unable to add PREP path to proxy %6D",
+ prep->prep_targetaddr, ":");
+ vap->iv_stats.is_mesh_rtaddfailed++;
+ return;
+ }
+ }
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) {
+ "%s path to %6D, hopcount %d:%d metric %d:%d",
+ rtext->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
+ "prefer" : "update",
+ prep->prep_target_ext_addr, ":",
+ rtext->rt_nhops, prep->prep_hopcount + 1,
+ rtext->rt_metric, metric);
+
+ rtext->rt_flags = IEEE80211_MESHRT_FLAGS_PROXY |
+ IEEE80211_MESHRT_FLAGS_VALID;
+ IEEE80211_ADDR_COPY(rtext->rt_dest,
+ prep->prep_target_ext_addr);
+ IEEE80211_ADDR_COPY(rtext->rt_mesh_gate,
+ prep->prep_targetaddr);
+ IEEE80211_ADDR_COPY(rtext->rt_nexthop, wh->i_addr2);
+ rtext->rt_metric = metric;
+ rtext->rt_lifetime = prep->prep_lifetime;
+ rtext->rt_nhops = prep->prep_hopcount + 1;
+ rtext->rt_ext_seq = prep->prep_origseq; /* new proxy seq */
/*
- * Check if we already have a path to this node.
- * If we do, check if this path reply contains a
- * better route.
+ * XXX: proxy entries have no HWMP priv data,
+ * nullify them to be sure?
*/
- 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
@@ -1066,21 +1442,33 @@ hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
* stuck back on the stageq because there won't be
* a path.
*/
- m = ieee80211_ageq_remove(&ic->ic_stageq,
+ addr = prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE ?
+ prep->prep_target_ext_addr : prep->prep_targetaddr;
+ m = ieee80211_ageq_remove(&ic->ic_stageq,
(struct ieee80211_node *)(uintptr_t)
- ieee80211_mac_hash(ic, rt->rt_dest));
+ ieee80211_mac_hash(ic, addr)); /* either dest or ext_dest */
+
+ /*
+ * All frames in the stageq here should be non-M_ENCAP; or things
+ * will get very unhappy.
+ */
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);
+ /*
+ * If the mbuf has M_ENCAP set, ensure we free it.
+ * Note that after if_transmit() is called, m is invalid.
+ */
+ (void) ieee80211_vap_xmitpkt(vap, m);
}
+#undef IS_PROXY
+#undef PROXIED_BY_US
}
static int
-hwmp_send_prep(struct ieee80211_node *ni,
- const uint8_t sa[IEEE80211_ADDR_LEN],
+hwmp_send_prep(struct ieee80211vap *vap,
const uint8_t da[IEEE80211_ADDR_LEN],
struct ieee80211_meshprep_ie *prep)
{
@@ -1089,15 +1477,16 @@ hwmp_send_prep(struct ieee80211_node *ni,
/*
* mesh prep action frame format
* [6] da
- * [6] sa
+ * [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));
+ prep->prep_len = prep->prep_flags & IEEE80211_MESHPREP_FLAGS_AE ?
+ IEEE80211_MESHPREP_BASE_SZ_AE : IEEE80211_MESHPREP_BASE_SZ;
+ return hwmp_send_action(vap, da, (uint8_t *)prep, prep->prep_len + 2);
}
#define PERR_DFLAGS(n) perr.perr_dests[n].dest_flags
@@ -1126,78 +1515,131 @@ hwmp_peerdown(struct ieee80211_node *ni)
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_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);
+ hwmp_send_perr(vap, 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
+#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_DEXTADDR(n) perr->perr_dests[n].dest_ext_addr
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_mesh_route *rt_ext = NULL;
struct ieee80211_hwmp_route *hr;
- struct ieee80211_meshperr_ie pperr;
- int i, forward = 0;
+ struct ieee80211_meshperr_ie *pperr = NULL;
+ int i, j = 0, forward = 0;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
+ "received PERR from %6D", wh->i_addr2, ":");
/*
- * Acceptance criteria: check if we received a PERR from a
- * neighbor and forwarding is enabled.
+ * if forwarding is true, prepare pperr
*/
- if (ni == vap->iv_bss ||
- ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
- !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
- return;
+ if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) {
+ forward = 1;
+ pperr = IEEE80211_MALLOC(sizeof(*perr) + 31*sizeof(*perr->perr_dests),
+ M_80211_MESH_PERR, IEEE80211_M_NOWAIT); /* XXX: magic number, 32 err dests */
+ }
+
/*
- * Find all routing entries that match and delete them.
+ * Acceptance criteria: check if we have forwarding information
+ * stored about destination, and that nexthop == TA of this PERR.
+ * NB: we also build a new PERR to propagate in case we should forward.
*/
for (i = 0; i < perr->perr_ndests; i++) {
rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
if (rt == NULL)
continue;
+ if (!IEEE80211_ADDR_EQ(rt->rt_nexthop, wh->i_addr2))
+ continue;
+
+ /* found and accepted a PERR ndest element, process it... */
+ if (forward)
+ memcpy(&pperr->perr_dests[j], &perr->perr_dests[i],
+ sizeof(*perr->perr_dests));
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;
+ switch(PERR_DFLAGS(i)) {
+ case (IEEE80211_REASON_MESH_PERR_NO_FI):
+ if (PERR_DSEQ(i) == 0) {
+ hr->hr_seq++;
+ if (forward) {
+ pperr->perr_dests[j].dest_seq =
+ hr->hr_seq;
+ }
+ } else {
+ hr->hr_seq = PERR_DSEQ(i);
+ }
+ rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
+ j++;
+ break;
+ case (IEEE80211_REASON_MESH_PERR_DEST_UNREACH):
+ if(HWMP_SEQ_GT(PERR_DSEQ(i), hr->hr_seq)) {
+ hr->hr_seq = PERR_DSEQ(i);
+ rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
+ j++;
+ }
+ break;
+ case (IEEE80211_REASON_MESH_PERR_NO_PROXY):
+ rt_ext = ieee80211_mesh_rt_find(vap, PERR_DEXTADDR(i));
+ if (rt_ext != NULL) {
+ rt_ext->rt_flags &=
+ ~IEEE80211_MESHRT_FLAGS_VALID;
+ j++;
+ }
+ break;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
+ "PERR, unknown reason code %u\n", PERR_DFLAGS(i));
+ goto done; /* XXX: stats?? */
}
+ ieee80211_mesh_rt_flush_peer(vap, PERR_DADDR(i));
+ KASSERT(j < 32, ("PERR, error ndest >= 32 (%u)", j));
+ }
+ if (j == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL, "%s",
+ "PERR not accepted");
+ goto done; /* XXX: stats?? */
}
+
/*
* 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);
+ "propagate PERR from %6D", wh->i_addr2, ":");
+ pperr->perr_ndests = j;
+ pperr->perr_ttl--;
+ hwmp_send_perr(vap, broadcastaddr, pperr);
}
+done:
+ if (pperr != NULL)
+ IEEE80211_FREE(pperr, M_80211_MESH_PERR);
}
-#undef PEER_DADDR
+#undef PERR_DFLAGS
+#undef PERR_DADDR
#undef PERR_DSEQ
+#undef PERR_DEXTADDR
static int
-hwmp_send_perr(struct ieee80211_node *ni,
- const uint8_t sa[IEEE80211_ADDR_LEN],
+hwmp_send_perr(struct ieee80211vap *vap,
const uint8_t da[IEEE80211_ADDR_LEN],
struct ieee80211_meshperr_ie *perr)
{
- struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ int i;
+ uint8_t length = 0;
/*
* Enforce PERR interval.
@@ -1216,10 +1658,77 @@ hwmp_send_perr(struct ieee80211_node *ni,
* [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));
+ length = IEEE80211_MESHPERR_BASE_SZ;
+ for (i = 0; i<perr->perr_ndests; i++) {
+ if (perr->perr_dests[i].dest_flags &
+ IEEE80211_MESHPERR_FLAGS_AE) {
+ length += IEEE80211_MESHPERR_DEST_SZ_AE;
+ continue ;
+ }
+ length += IEEE80211_MESHPERR_DEST_SZ;
+ }
+ perr->perr_len =length;
+ return hwmp_send_action(vap, da, (uint8_t *)perr, perr->perr_len+2);
}
+/*
+ * Called from the rest of the net80211 code (mesh code for example).
+ * NB: IEEE80211_REASON_MESH_PERR_DEST_UNREACH can be trigger by the fact that
+ * a mesh STA is unable to forward an MSDU/MMPDU to a next-hop mesh STA.
+ */
+#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_DEXTADDR(n) perr.perr_dests[n].dest_ext_addr
+#define PERR_DRCODE(n) perr.perr_dests[n].dest_rcode
+static void
+hwmp_senderror(struct ieee80211vap *vap,
+ const uint8_t addr[IEEE80211_ADDR_LEN],
+ struct ieee80211_mesh_route *rt, int rcode)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_hwmp_route *hr = NULL;
+ struct ieee80211_meshperr_ie perr;
+
+ if (rt != NULL)
+ hr = IEEE80211_MESH_ROUTE_PRIV(rt,
+ struct ieee80211_hwmp_route);
+
+ perr.perr_ndests = 1;
+ perr.perr_ttl = ms->ms_ttl;
+ PERR_DFLAGS(0) = 0;
+ PERR_DRCODE(0) = rcode;
+
+ switch (rcode) {
+ case IEEE80211_REASON_MESH_PERR_NO_FI:
+ IEEE80211_ADDR_COPY(PERR_DADDR(0), addr);
+ PERR_DSEQ(0) = 0; /* reserved */
+ break;
+ case IEEE80211_REASON_MESH_PERR_NO_PROXY:
+ KASSERT(rt != NULL, ("no proxy info for sending PERR"));
+ KASSERT(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY,
+ ("route is not marked proxy"));
+ PERR_DFLAGS(0) |= IEEE80211_MESHPERR_FLAGS_AE;
+ IEEE80211_ADDR_COPY(PERR_DADDR(0), vap->iv_myaddr);
+ PERR_DSEQ(0) = rt->rt_ext_seq;
+ IEEE80211_ADDR_COPY(PERR_DEXTADDR(0), addr);
+ break;
+ case IEEE80211_REASON_MESH_PERR_DEST_UNREACH:
+ KASSERT(rt != NULL, ("no route info for sending PERR"));
+ IEEE80211_ADDR_COPY(PERR_DADDR(0), addr);
+ PERR_DSEQ(0) = hr->hr_seq;
+ break;
+ default:
+ KASSERT(0, ("unknown reason code for HWMP PERR (%u)", rcode));
+ }
+ hwmp_send_perr(vap, broadcastaddr, &perr);
+}
+#undef PERR_DFLAGS
+#undef PEER_DADDR
+#undef PERR_DSEQ
+#undef PERR_DEXTADDR
+#undef PERR_DRCODE
+
static void
hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
@@ -1228,61 +1737,169 @@ hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
struct ieee80211_mesh_route *rt = NULL;
struct ieee80211_hwmp_route *hr;
+ struct ieee80211_meshpreq_ie preq;
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))
+ if (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 && rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) {
+ hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
+
+ /* Acceptance criteria: if RANN.seq < stored seq, discard RANN */
+ if (HWMP_SEQ_LT(rann->rann_seq, hr->hr_seq)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
+ "RANN seq %u < %u", rann->rann_seq, hr->hr_seq);
+ return;
+ }
+
+ /* Acceptance criteria: if RANN.seq == stored seq AND
+ * RANN.metric > stored metric, discard RANN */
+ if (HWMP_SEQ_EQ(rann->rann_seq, hr->hr_seq) &&
+ rann->rann_metric > rt->rt_metric) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
+ "RANN metric %u > %u", rann->rann_metric, rt->rt_metric);
+ return;
+ }
+ }
+
+ /* RANN ACCEPTED */
+
+ ieee80211_hwmp_rannint = rann->rann_interval; /* XXX: mtx lock? */
+
if (rt == NULL) {
- hwmp_discover(vap, rann->rann_addr, NULL);
- return;
+ rt = ieee80211_mesh_rt_add(vap, rann->rann_addr);
+ if (rt == NULL) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_HWMP, wh, NULL,
+ "unable to add mac for RANN root %6D",
+ rann->rann_addr, ":");
+ vap->iv_stats.is_mesh_rtaddfailed++;
+ return;
+ }
}
hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
- if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) {
+ /* Check if root is a mesh gate, mark it */
+ if (rann->rann_flags & IEEE80211_MESHRANN_FLAGS_GATE) {
+ struct ieee80211_mesh_gate_route *gr;
+
+ rt->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
+ gr = ieee80211_mesh_mark_gate(vap, rann->rann_addr,
+ rt);
+ gr->gr_lastseq = 0; /* NOT GANN */
+ }
+ /* discovery timeout */
+ ieee80211_mesh_rt_update(rt,
+ ticks_to_msecs(ieee80211_hwmp_roottimeout));
+
+ preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
+ preq.preq_hopcount = 0;
+ preq.preq_ttl = ms->ms_ttl;
+ preq.preq_id = 0; /* reserved */
+ IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
+ preq.preq_origseq = ++hs->hs_seq;
+ preq.preq_lifetime = ieee80211_hwmp_roottimeout;
+ preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
+ preq.preq_tcount = 1;
+ preq.preq_targets[0].target_flags = IEEE80211_MESHPREQ_TFLAGS_TO;
+ /* NB: IEEE80211_MESHPREQ_TFLAGS_USN = 0 implicitly implied */
+ IEEE80211_ADDR_COPY(preq.preq_targets[0].target_addr, rann->rann_addr);
+ preq.preq_targets[0].target_seq = rann->rann_seq;
+ /* XXX: if rootconfint have not passed, we built this preq in vain */
+ hwmp_send_preq(vap, wh->i_addr2, &preq, &hr->hr_lastrootconf,
+ &ieee80211_hwmp_rootconfint);
+
+ /* propagate a RANN */
+ if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID &&
+ rann->rann_ttl > 1 &&
+ ms->ms_flags & IEEE80211_MESHFLAGS_FWD) {
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);
- }
+ 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, broadcastaddr, &prann);
}
}
static int
-hwmp_send_rann(struct ieee80211_node *ni,
- const uint8_t sa[IEEE80211_ADDR_LEN],
+hwmp_send_rann(struct ieee80211vap *vap,
const uint8_t da[IEEE80211_ADDR_LEN],
struct ieee80211_meshrann_ie *rann)
{
/*
* mesh rann action frame format
* [6] da
- * [6] sa
+ * [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));
+ rann->rann_len = IEEE80211_MESHRANN_BASE_SZ;
+ return hwmp_send_action(vap, da, (uint8_t *)rann, rann->rann_len + 2);
}
#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_rediscover_cb(void *arg)
+{
+ struct ieee80211_mesh_route *rt = arg;
+ struct ieee80211vap *vap = rt->rt_vap;
+ struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_hwmp_route *hr;
+ struct ieee80211_meshpreq_ie preq; /* Optimize: storing first preq? */
+
+ if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID))
+ return ; /* nothing to do */
+
+ hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
+ if (hr->hr_preqretries >=
+ ieee80211_hwmp_maxpreq_retries) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY,
+ rt->rt_dest, "%s",
+ "max number of discovery, send queued frames to GATE");
+ ieee80211_mesh_forward_to_gates(vap, rt);
+ vap->iv_stats.is_mesh_fwd_nopath++;
+ return ; /* XXX: flush queue? */
+ }
+
+ hr->hr_preqretries++;
+
+
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, rt->rt_dest,
+ "start path rediscovery , target seq %u", hr->hr_seq);
+ /*
+ * Try to discover the path for this node.
+ * Group addressed PREQ Case A
+ */
+ 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 = ticks_to_msecs(ieee80211_hwmp_pathtimeout);
+ preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
+ preq.preq_tcount = 1;
+ IEEE80211_ADDR_COPY(PREQ_TADDR(0), rt->rt_dest);
+ PREQ_TFLAGS(0) = 0;
+ if (ieee80211_hwmp_targetonly)
+ PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
+ PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
+ PREQ_TSEQ(0) = 0; /* RESERVED when USN flag is set */
+ /* XXX check return value */
+ hwmp_send_preq(vap, broadcastaddr, &preq, &hr->hr_lastpreq,
+ &ieee80211_hwmp_preqminint);
+ callout_reset(&rt->rt_discovery,
+ ieee80211_hwmp_net_diameter_traversaltime * 2,
+ hwmp_rediscover_cb, rt);
+}
+
static struct ieee80211_node *
hwmp_discover(struct ieee80211vap *vap,
const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
@@ -1308,28 +1925,53 @@ hwmp_discover(struct ieee80211vap *vap,
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));
+ ni, "unable to add discovery path to %6D",
+ 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_DISCOVER) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
+ "%s", "already discovering queue frame until path found");
+ sendpreq = 1;
+ goto done;
+ }
if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
+ if (hr->hr_lastdiscovery != 0 &&
+ (ticks - hr->hr_lastdiscovery <
+ (ieee80211_hwmp_net_diameter_traversaltime * 2))) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ dest, NULL, "%s",
+ "too frequent discovery requeust");
+ sendpreq = 1;
+ goto done;
+ }
+ hr->hr_lastdiscovery = ticks;
+ if (hr->hr_preqretries >=
+ ieee80211_hwmp_maxpreq_retries) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ dest, NULL, "%s",
+ "no valid path , max number of discovery");
+ vap->iv_stats.is_mesh_fwd_nopath++;
+ goto done;
+ }
+ rt->rt_flags = IEEE80211_MESHRT_FLAGS_DISCOVER;
+ hr->hr_preqretries++;
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)",
+ "start path discovery (src %s), target seq %u",
m == NULL ? "<none>" : ether_sprintf(
- mtod(m, struct ether_header *)->ether_shost));
+ mtod(m, struct ether_header *)->ether_shost),
+ hr->hr_seq);
/*
* Try to discover the path for this node.
+ * Group addressed PREQ Case A
*/
preq.preq_flags = 0;
preq.preq_hopcount = 0;
@@ -1337,20 +1979,22 @@ hwmp_discover(struct ieee80211vap *vap,
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_lifetime =
+ ticks_to_msecs(ieee80211_hwmp_pathtimeout);
+ preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
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;
+ PREQ_TSEQ(0) = 0; /* RESERVED when USN flag is set */
/* XXX check return value */
- hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
- broadcastaddr, &preq);
+ hwmp_send_preq(vap, broadcastaddr, &preq,
+ &hr->hr_lastpreq, &ieee80211_hwmp_preqminint);
+ callout_reset(&rt->rt_discovery,
+ ieee80211_hwmp_net_diameter_traversaltime * 2,
+ hwmp_rediscover_cb, rt);
}
if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
@@ -1393,7 +2037,7 @@ 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;
diff --git a/freebsd/sys/net80211/ieee80211_input.c b/freebsd/sys/net80211/ieee80211_input.c
index 043d1887..0e427f84 100644
--- a/freebsd/sys/net80211/ieee80211_input.c
+++ b/freebsd/sys/net80211/ieee80211_input.c
@@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_llc.h>
#include <net/if_media.h>
#include <net/if_vlan_var.h>
@@ -84,12 +85,20 @@ ieee80211_process_mimo(struct ieee80211_node *ni, struct ieee80211_rx_stats *rx)
}
int
-ieee80211_input_mimo(struct ieee80211_node *ni, struct mbuf *m,
- struct ieee80211_rx_stats *rx)
+ieee80211_input_mimo(struct ieee80211_node *ni, struct mbuf *m)
{
+ struct ieee80211_rx_stats rxs;
+
+ /* try to read stats from mbuf */
+ bzero(&rxs, sizeof(rxs));
+ if (ieee80211_get_rx_params(m, &rxs) != 0)
+ return (-1);
+
/* XXX should assert IEEE80211_R_NF and IEEE80211_R_RSSI are set */
- ieee80211_process_mimo(ni, rx);
- return ieee80211_input(ni, m, rx->rssi, rx->nf);
+ ieee80211_process_mimo(ni, &rxs);
+
+ //return ieee80211_input(ni, m, rx->rssi, rx->nf);
+ return ni->ni_vap->iv_input(ni, m, &rxs, rxs.c_rssi, rxs.c_nf);
}
int
@@ -98,14 +107,17 @@ ieee80211_input_all(struct ieee80211com *ic, struct mbuf *m, int rssi, int nf)
struct ieee80211_rx_stats rx;
rx.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI;
- rx.nf = nf;
- rx.rssi = rssi;
- return ieee80211_input_mimo_all(ic, m, &rx);
+ rx.c_nf = nf;
+ rx.c_rssi = rssi;
+
+ if (!ieee80211_add_rx_params(m, &rx))
+ return (-1);
+
+ return ieee80211_input_mimo_all(ic, m);
}
int
-ieee80211_input_mimo_all(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_rx_stats *rx)
+ieee80211_input_mimo_all(struct ieee80211com *ic, struct mbuf *m)
{
struct ieee80211vap *vap;
int type = -1;
@@ -132,8 +144,9 @@ ieee80211_input_mimo_all(struct ieee80211com *ic, struct mbuf *m,
/*
* Packet contents are changed by ieee80211_decap
* so do a deep copy of the packet.
+ * NB: tags are copied too.
*/
- mcopy = m_dup(m, M_DONTWAIT);
+ mcopy = m_dup(m, M_NOWAIT);
if (mcopy == NULL) {
/* XXX stat+msg */
continue;
@@ -143,7 +156,7 @@ ieee80211_input_mimo_all(struct ieee80211com *ic, struct mbuf *m,
m = NULL;
}
ni = ieee80211_ref_node(vap->iv_bss);
- type = ieee80211_input_mimo(ni, mcopy, rx);
+ type = ieee80211_input_mimo(ni, mcopy);
ieee80211_free_node(ni);
}
if (m != NULL) /* no vaps, reclaim mbuf */
@@ -207,9 +220,16 @@ ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
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)) {
+ if (rxseq == last_rxseq+1 &&
+ IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) &&
+ IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) {
+ /* XXX clear MORE_FRAG bit? */
+ /* track last seqnum and fragno */
+ *(uint16_t *) lwh->i_seq = *(uint16_t *) wh->i_seq;
+
+ m_adj(m, hdrspace); /* strip header */
+ m_catpkt(mfrag, m); /* concatenate */
+ } else {
/*
* Unrelated fragment or no space for it,
* clear current fragments.
@@ -227,14 +247,6 @@ ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
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;
@@ -252,18 +264,22 @@ ieee80211_deliver_data(struct ieee80211vap *vap,
struct ifnet *ifp = vap->iv_ifp;
/* clear driver/net80211 flags before passing up */
- m->m_flags &= ~(M_80211_RX | M_MCAST | M_BCAST);
+ m->m_flags &= ~(M_MCAST | M_BCAST);
+ m_clrprotoflags(m);
/* 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++;
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
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? */
+ if (ETHER_IS_BROADCAST(eh->ether_dhost))
+ m->m_flags |= M_BCAST;
+ else
+ m->m_flags |= M_MCAST;
IEEE80211_NODE_STAT(ni, rx_mcast);
} else
IEEE80211_NODE_STAT(ni, rx_ucast);
@@ -325,13 +341,13 @@ ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen)
IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4);
break;
}
-#ifdef ALIGNED_POINTER
+#ifndef __NO_STRICT_ALIGNMENT
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 */
+#endif /* !__NO_STRICT_ALIGNMENT */
if (llc != NULL) {
eh = mtod(m, struct ether_header *);
eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
@@ -446,8 +462,9 @@ 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);
+ ni->ni_challenge = (uint32_t *)
+ IEEE80211_MALLOC(IEEE80211_CHALLENGE_LEN,
+ M_80211_NODE, IEEE80211_M_NOWAIT);
if (ni->ni_challenge == NULL) {
IEEE80211_NOTE(ni->ni_vap,
IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni,
@@ -466,7 +483,7 @@ ieee80211_alloc_challenge(struct ieee80211_node *ni)
*/
int
ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
- struct ieee80211_scanparams *scan)
+ struct ieee80211_channel *rxchan, struct ieee80211_scanparams *scan)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
@@ -503,7 +520,7 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
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->bchan = ieee80211_chan2ieee(ic, rxchan);
scan->chan = scan->bchan;
scan->ies = frm;
scan->ies_len = efrm - frm;
@@ -524,9 +541,12 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
case IEEE80211_ELEMID_CSA:
scan->csa = frm;
break;
+ case IEEE80211_ELEMID_QUIET:
+ scan->quiet = frm;
+ break;
case IEEE80211_ELEMID_FHPARMS:
if (ic->ic_phytype == IEEE80211_T_FH) {
- scan->fhdwell = LE_READ_2(&frm[2]);
+ scan->fhdwell = le16dec(&frm[2]);
scan->chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
scan->fhindex = frm[6];
}
@@ -547,6 +567,8 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
case IEEE80211_ELEMID_IBSSPARMS:
case IEEE80211_ELEMID_CFPARMS:
case IEEE80211_ELEMID_PWRCNSTR:
+ case IEEE80211_ELEMID_BSSLOAD:
+ case IEEE80211_ELEMID_APCHANREP:
/* NB: avoid debugging complaints */
break;
case IEEE80211_ELEMID_XRATES:
@@ -579,6 +601,9 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
scan->meshconf = frm;
break;
#endif
+ /* Extended capabilities; nothing handles it for now */
+ case IEEE80211_ELEMID_EXTCAP:
+ break;
case IEEE80211_ELEMID_VENDOR:
if (iswpaoui(frm))
scan->wpa = frm;
@@ -643,7 +668,8 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
*/
IEEE80211_DISCARD(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
- wh, NULL, "for off-channel %u", scan->chan);
+ wh, NULL, "for off-channel %u (bchan=%u)",
+ scan->chan, scan->bchan);
vap->iv_stats.is_rx_chanmismatch++;
scan->status |= IEEE80211_BPARSE_OFFCHAN;
}
@@ -651,7 +677,8 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
scan->bintval <= IEEE80211_BINTVAL_MAX)) {
IEEE80211_DISCARD(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
- wh, NULL, "bogus beacon interval", scan->bintval);
+ wh, NULL, "bogus beacon interval (%d TU)",
+ (int) scan->bintval);
vap->iv_stats.is_rx_badbintval++;
scan->status |= IEEE80211_BPARSE_BINTVAL_INVALID;
}
@@ -758,6 +785,61 @@ ieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m)
break;
}
break;
+#ifdef IEEE80211_SUPPORT_MESH
+ case IEEE80211_ACTION_CAT_MESH:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_MESH_LMETRIC:
+ /*
+ * XXX: verification is true only if we are using
+ * Airtime link metric (default)
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_meshlmetric_ie),
+ return EINVAL);
+ break;
+ case IEEE80211_ACTION_MESH_HWMP:
+ /* verify something */
+ break;
+ case IEEE80211_ACTION_MESH_GANN:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_meshgann_ie),
+ return EINVAL);
+ break;
+ case IEEE80211_ACTION_MESH_CC:
+ case IEEE80211_ACTION_MESH_MCCA_SREQ:
+ case IEEE80211_ACTION_MESH_MCCA_SREP:
+ case IEEE80211_ACTION_MESH_MCCA_AREQ:
+ case IEEE80211_ACTION_MESH_MCCA_ADVER:
+ case IEEE80211_ACTION_MESH_MCCA_TRDOWN:
+ case IEEE80211_ACTION_MESH_TBTT_REQ:
+ case IEEE80211_ACTION_MESH_TBTT_RES:
+ /* reject these early on, not implemented */
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+ wh, NULL, "not implemented yet, act=0x%02X",
+ ia->ia_action);
+ return EINVAL;
+ }
+ break;
+ case IEEE80211_ACTION_CAT_SELF_PROT:
+ /* If TA or RA group address discard silently */
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+ IEEE80211_IS_MULTICAST(wh->i_addr2))
+ return EINVAL;
+ /*
+ * XXX: Should we verify complete length now or it is
+ * to varying in sizes?
+ */
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_MESHPEERING_CONFIRM:
+ case IEEE80211_ACTION_MESHPEERING_CLOSE:
+ /* is not a peering candidate (yet) */
+ if (ni == vap->iv_bss)
+ return EINVAL;
+ break;
+ }
+ break;
+#endif
}
return 0;
}
@@ -845,12 +927,8 @@ ieee80211_discard_frame(const struct ieee80211vap *vap,
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);
+ printf("%s frame, ", type != NULL ? type :
+ ieee80211_mgt_subtype_name(wh->i_fc[0]));
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
diff --git a/freebsd/sys/net80211/ieee80211_input.h b/freebsd/sys/net80211/ieee80211_input.h
index b90f46a1..6fb0d707 100644
--- a/freebsd/sys/net80211/ieee80211_input.h
+++ b/freebsd/sys/net80211/ieee80211_input.h
@@ -62,8 +62,7 @@ void ieee80211_ssid_mismatch(struct ieee80211vap *, const char *tag,
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], \
+ ieee80211_mgt_subtype_name(subtype), \
wh->i_addr2, _ssid); \
vap->iv_stats.is_rx_ssidmismatch++; \
_action; \
@@ -80,70 +79,58 @@ void ieee80211_ssid_mismatch(struct ieee80211vap *, const char *tag,
} 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)))
+#include <sys/endian.h> /* For le16toh() / le32dec() */
static __inline int
iswpaoui(const uint8_t *frm)
{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+ return frm[1] > 3 && le32dec(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);
+ return frm[1] > 3 && le32dec(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) &&
+ return frm[1] > 5 && le32dec(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) &&
+ return frm[1] > 5 && le32dec(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);
+ return frm[1] > 3 && le32dec(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);
+ return frm[1] > 3 && le32dec(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);
+ return frm[1] > 3 && le32dec(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);
+ return frm[1] > 3 && le32dec(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI);
}
-#include <sys/endian.h> /* For le16toh() */
-
/*
* Check the current frame sequence number against the current TID
* state and return whether it's in sequence or should be dropped.
@@ -164,23 +151,28 @@ ishtinfooui(const uint8_t *frm)
* but a retransmit since the initial packet didn't make it.
*/
static __inline int
-ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh)
+ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ uint8_t *bssid)
{
#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
#define SEQ_EQ(a,b) ((int)((a)-(b)) == 0)
-#define HAS_SEQ(type) ((type & 0x4) == 0)
#define SEQNO(a) ((a) >> IEEE80211_SEQ_SEQ_SHIFT)
#define FRAGNO(a) ((a) & IEEE80211_SEQ_FRAG_MASK)
+ struct ieee80211vap *vap = ni->ni_vap;
uint16_t rxseq;
- uint8_t type;
+ uint8_t type, subtype;
uint8_t tid;
struct ieee80211_rx_ampdu *rap;
rxseq = le16toh(*(uint16_t *)wh->i_seq);
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
- /* Types with no sequence number are always treated valid */
- if (! HAS_SEQ(type))
+ /*
+ * Types with no sequence number (or QoS (+)Null frames)
+ * are always treated valid.
+ */
+ if (! IEEE80211_HAS_SEQ(type, subtype))
return 1;
tid = ieee80211_gettid(wh);
@@ -195,7 +187,7 @@ ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh)
/* HT nodes currently doing RX AMPDU are always valid */
if ((ni->ni_flags & IEEE80211_NODE_HT) &&
(rap->rxa_flags & IEEE80211_AGGR_RUNNING))
- return 1;
+ goto ok;
}
/*
@@ -213,7 +205,7 @@ ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh)
*/
if (SEQ_EQ(rxseq, ni->ni_rxseqs[tid]) &&
(wh->i_fc[1] & IEEE80211_FC1_RETRY))
- return 0;
+ goto fail;
/*
* Treat any subsequent frame as fine if the last seen frame
* is 4095 and it's not a retransmit for the same sequence
@@ -221,7 +213,7 @@ ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh)
* fragments w/ sequence number 4095. It shouldn't be seen
* in practice, but see the comment above for further info.
*/
- return 1;
+ goto ok;
}
/*
@@ -230,12 +222,25 @@ ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh)
*/
if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
SEQ_LEQ(rxseq, ni->ni_rxseqs[tid]))
- return 0;
+ goto fail;
+
+ok:
+ ni->ni_rxseqs[tid] = rxseq;
return 1;
+
+fail:
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, bssid, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ SEQNO(rxseq), SEQNO(ni->ni_rxseqs[tid]),
+ FRAGNO(rxseq), FRAGNO(ni->ni_rxseqs[tid]), tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+
+ return 0;
#undef SEQ_LEQ
#undef SEQ_EQ
-#undef HAS_SEQ
#undef SEQNO
#undef FRAGNO
}
@@ -253,6 +258,7 @@ 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_channel *,
struct ieee80211_scanparams *);
int ieee80211_parse_action(struct ieee80211_node *, struct mbuf *);
#endif /* _NET80211_IEEE80211_INPUT_H_ */
diff --git a/freebsd/sys/net80211/ieee80211_ioctl.c b/freebsd/sys/net80211/ieee80211_ioctl.c
index 78a6b50f..c0813a78 100644
--- a/freebsd/sys/net80211/ieee80211_ioctl.c
+++ b/freebsd/sys/net80211/ieee80211_ioctl.c
@@ -34,18 +34,19 @@ __FBSDID("$FreeBSD$");
*/
#include <rtems/bsd/local/opt_inet.h>
-#include <rtems/bsd/local/opt_ipx.h>
#include <rtems/bsd/local/opt_wlan.h>
#include <sys/endian.h>
#include <rtems/bsd/sys/param.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/priv.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/systm.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/ethernet.h>
@@ -55,11 +56,6 @@ __FBSDID("$FreeBSD$");
#include <netinet/if_ether.h>
#endif
-#ifdef IPX
-#include <netipx/ipx.h>
-#include <netipx/ipx_if.h>
-#endif
-
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_ioctl.h>
#include <net80211/ieee80211_regdomain.h>
@@ -75,7 +71,7 @@ static struct ieee80211_channel *findchannel(struct ieee80211com *,
static int ieee80211_scanreq(struct ieee80211vap *,
struct ieee80211_scan_req *);
-static __noinline int
+static int
ieee80211_ioctl_getkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211com *ic = vap->iv_ic;
@@ -131,7 +127,7 @@ ieee80211_ioctl_getkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
return copyout(&ik, ireq->i_data, sizeof(ik));
}
-static __noinline int
+static int
ieee80211_ioctl_getchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211com *ic = vap->iv_ic;
@@ -141,7 +137,7 @@ ieee80211_ioctl_getchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
}
-static __noinline int
+static int
ieee80211_ioctl_getchaninfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211com *ic = vap->iv_ic;
@@ -155,36 +151,40 @@ ieee80211_ioctl_getchaninfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
return copyout(&ic->ic_nchans, ireq->i_data, space);
}
-static __noinline int
+static int
ieee80211_ioctl_getwpaie(struct ieee80211vap *vap,
struct ieee80211req *ireq, int req)
{
struct ieee80211_node *ni;
- struct ieee80211req_wpaie2 wpaie;
+ 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);
+ wpaie = IEEE80211_MALLOC(sizeof(*wpaie), M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (wpaie == NULL)
+ return ENOMEM;
+ 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));
+ goto bad;
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, wpaie->wpa_macaddr);
+ if (ni == NULL) {
+ error = ENOENT;
+ goto bad;
+ }
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 (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 (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);
@@ -193,18 +193,21 @@ ieee80211_ioctl_getwpaie(struct ieee80211vap *vap,
/* 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 (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);
+ error = copyout(wpaie, ireq->i_data, ireq->i_len);
+bad:
+ IEEE80211_FREE(wpaie, M_TEMP);
+ return error;
}
-static __noinline int
+static int
ieee80211_ioctl_getstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
@@ -259,7 +262,7 @@ get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
req->space += scan_space(se, &ielen);
}
-static __noinline void
+static void
get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
{
struct scanreq *req = arg;
@@ -309,7 +312,7 @@ get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
}
-static __noinline int
+static int
ieee80211_ioctl_getscanresults(struct ieee80211vap *vap,
struct ieee80211req *ireq)
{
@@ -330,14 +333,15 @@ ieee80211_ioctl_getscanresults(struct ieee80211vap *vap,
space = req.space;
/* XXX M_WAITOK after driver lock released */
- p = malloc(space, M_TEMP, M_NOWAIT | M_ZERO);
+ p = IEEE80211_MALLOC(space, M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_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);
+ IEEE80211_FREE(p, M_TEMP);
} else
ireq->i_len = 0;
@@ -345,7 +349,6 @@ ieee80211_ioctl_getscanresults(struct ieee80211vap *vap,
}
struct stainforeq {
- struct ieee80211vap *vap;
struct ieee80211req_sta_info *si;
size_t space;
};
@@ -364,15 +367,13 @@ 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
+static void
get_sta_info(void *arg, struct ieee80211_node *ni)
{
struct stainforeq *req = arg;
@@ -381,8 +382,6 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
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;
@@ -458,7 +457,7 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
req->space -= len;
}
-static __noinline int
+static int
getstainfo_common(struct ieee80211vap *vap, struct ieee80211req *ireq,
struct ieee80211_node *ni, size_t off)
{
@@ -470,29 +469,31 @@ getstainfo_common(struct ieee80211vap *vap, struct ieee80211req *ireq,
error = 0;
req.space = 0;
- req.vap = vap;
- if (ni == NULL)
- ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
- else
+ if (ni == NULL) {
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap, 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);
+ p = IEEE80211_MALLOC(space, M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_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
+ if (ni == NULL) {
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap,
+ 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);
+ IEEE80211_FREE(p, M_TEMP);
} else
ireq->i_len = 0;
bad:
@@ -501,7 +502,7 @@ bad:
return error;
}
-static __noinline int
+static int
ieee80211_ioctl_getstainfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
uint8_t macaddr[IEEE80211_ADDR_LEN];
@@ -524,7 +525,7 @@ ieee80211_ioctl_getstainfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
return getstainfo_common(vap, ireq, ni, off);
}
-static __noinline int
+static int
ieee80211_ioctl_getstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
@@ -545,7 +546,7 @@ ieee80211_ioctl_getstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
return error;
}
-static __noinline int
+static int
ieee80211_ioctl_getwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211com *ic = vap->iv_ic;
@@ -588,7 +589,7 @@ ieee80211_ioctl_getwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
return 0;
}
-static __noinline int
+static int
ieee80211_ioctl_getmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
const struct ieee80211_aclator *acl = vap->iv_acl;
@@ -596,7 +597,7 @@ ieee80211_ioctl_getmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
return (acl == NULL ? EINVAL : acl->iac_getioctl(vap, ireq));
}
-static __noinline int
+static int
ieee80211_ioctl_getcurchan(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211com *ic = vap->iv_ic;
@@ -609,7 +610,7 @@ ieee80211_ioctl_getcurchan(struct ieee80211vap *vap, struct ieee80211req *ireq)
* in use. When in RUN state report the vap-specific channel.
* Otherwise return curchan.
*/
- if (vap->iv_state == IEEE80211_S_RUN)
+ if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)
c = vap->iv_bss->ni_chan;
else
c = ic->ic_curchan;
@@ -653,7 +654,7 @@ ieee80211_ioctl_getappie(struct ieee80211vap *vap, struct ieee80211req *ireq)
return EINVAL;
}
-static __noinline int
+static int
ieee80211_ioctl_getregdomain(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
@@ -665,7 +666,7 @@ ieee80211_ioctl_getregdomain(struct ieee80211vap *vap,
sizeof(ic->ic_regdomain));
}
-static __noinline int
+static int
ieee80211_ioctl_getroam(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
@@ -676,7 +677,7 @@ ieee80211_ioctl_getroam(struct ieee80211vap *vap,
return copyout(vap->iv_roamparms, ireq->i_data, len);
}
-static __noinline int
+static int
ieee80211_ioctl_gettxparams(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
@@ -687,7 +688,7 @@ ieee80211_ioctl_gettxparams(struct ieee80211vap *vap,
return copyout(vap->iv_txparms, ireq->i_data, len);
}
-static __noinline int
+static int
ieee80211_ioctl_getdevcaps(struct ieee80211com *ic,
const struct ieee80211req *ireq)
{
@@ -704,23 +705,25 @@ ieee80211_ioctl_getdevcaps(struct ieee80211com *ic,
if (maxchans > 2048)
maxchans = 2048;
dc = (struct ieee80211_devcaps_req *)
- malloc(IEEE80211_DEVCAPS_SIZE(maxchans), M_TEMP, M_NOWAIT | M_ZERO);
+ IEEE80211_MALLOC(IEEE80211_DEVCAPS_SIZE(maxchans), M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_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;
+ dc->dc_vhtcaps = ic->ic_vhtcaps;
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);
+ IEEE80211_FREE(dc, M_TEMP);
return error;
}
-static __noinline int
+static int
ieee80211_ioctl_getstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
@@ -769,23 +772,7 @@ ieee80211_ioctl_getdefault(struct ieee80211vap *vap, struct ieee80211req *ireq)
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
+static int
ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
struct ieee80211req *ireq)
{
@@ -871,6 +858,8 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
* Tx power limit is the min of max regulatory
* power, any user-set limit, and the max the
* radio can do.
+ *
+ * TODO: methodize this
*/
ireq->i_val = 2*ic->ic_curchan->ic_maxregpower;
if (ireq->i_val > ic->ic_txpowlimit)
@@ -927,7 +916,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
case IEEE80211_IOC_BSSID:
if (ireq->i_len != IEEE80211_ADDR_LEN)
return EINVAL;
- if (vap->iv_state == IEEE80211_S_RUN) {
+ if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) {
error = copyout(vap->iv_opmode == IEEE80211_M_WDS ?
vap->iv_bss->ni_macaddr : vap->iv_bss->ni_bssid,
ireq->i_data, ireq->i_len);
@@ -936,8 +925,6 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
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;
@@ -961,7 +948,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
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) */
+ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only) */
error = ieee80211_ioctl_getwmeparam(vap, ireq);
break;
case IEEE80211_IOC_DTIM_PERIOD:
@@ -974,6 +961,21 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
case IEEE80211_IOC_PUREG:
ireq->i_val = (vap->iv_flags & IEEE80211_F_PUREG) != 0;
break;
+ case IEEE80211_IOC_QUIET:
+ ireq->i_val = vap->iv_quiet;
+ break;
+ case IEEE80211_IOC_QUIET_COUNT:
+ ireq->i_val = vap->iv_quiet_count;
+ break;
+ case IEEE80211_IOC_QUIET_PERIOD:
+ ireq->i_val = vap->iv_quiet_period;
+ break;
+ case IEEE80211_IOC_QUIET_DUR:
+ ireq->i_val = vap->iv_quiet_duration;
+ break;
+ case IEEE80211_IOC_QUIET_OFFSET:
+ ireq->i_val = vap->iv_quiet_offset;
+ break;
case IEEE80211_IOC_BGSCAN:
ireq->i_val = (vap->iv_flags & IEEE80211_F_BGSCAN) != 0;
break;
@@ -1016,17 +1018,27 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
ireq->i_val |= 2;
break;
case IEEE80211_IOC_AMPDU_LIMIT:
+ /* XXX TODO: make this a per-node thing; and leave this as global */
if (vap->iv_opmode == IEEE80211_M_HOSTAP)
ireq->i_val = vap->iv_ampdu_rxmax;
- else if (vap->iv_state == IEEE80211_S_RUN)
+ else if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)
+ /*
+ * XXX TODO: this isn't completely correct, as we've
+ * negotiated the higher of the two.
+ */
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:
+ /* XXX TODO: make this a per-node thing; and leave this as global */
if (vap->iv_opmode == IEEE80211_M_STA &&
- vap->iv_state == IEEE80211_S_RUN)
+ (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP))
+ /*
+ * XXX TODO: this isn't completely correct, as we've
+ * negotiated the higher of the two.
+ */
ireq->i_val = MS(vap->iv_bss->ni_htparam,
IEEE80211_HTCAP_MPDUDENSITY);
else
@@ -1100,7 +1112,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
break;
case IEEE80211_IOC_SMPS:
if (vap->iv_opmode == IEEE80211_M_STA &&
- vap->iv_state == IEEE80211_S_RUN) {
+ (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) {
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)
@@ -1112,13 +1124,36 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
break;
case IEEE80211_IOC_RIFS:
if (vap->iv_opmode == IEEE80211_M_STA &&
- vap->iv_state == IEEE80211_S_RUN)
+ (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP))
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;
+ case IEEE80211_IOC_STBC:
+ ireq->i_val = 0;
+ if (vap->iv_flags_ht & IEEE80211_FHT_STBC_TX)
+ ireq->i_val |= 1;
+ if (vap->iv_flags_ht & IEEE80211_FHT_STBC_RX)
+ ireq->i_val |= 2;
+ break;
+
+ /* VHT */
+ case IEEE80211_IOC_VHTCONF:
+ ireq->i_val = 0;
+ if (vap->iv_flags_vht & IEEE80211_FVHT_VHT)
+ ireq->i_val |= 1;
+ if (vap->iv_flags_vht & IEEE80211_FVHT_USEVHT40)
+ ireq->i_val |= 2;
+ if (vap->iv_flags_vht & IEEE80211_FVHT_USEVHT80)
+ ireq->i_val |= 4;
+ if (vap->iv_flags_vht & IEEE80211_FVHT_USEVHT80P80)
+ ireq->i_val |= 8;
+ if (vap->iv_flags_vht & IEEE80211_FVHT_USEVHT160)
+ ireq->i_val |= 16;
+ break;
+
default:
error = ieee80211_ioctl_getdefault(vap, ireq);
break;
@@ -1127,7 +1162,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
#undef MS
}
-static __noinline int
+static int
ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_key ik;
@@ -1192,7 +1227,15 @@ ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
if (!ieee80211_crypto_setkey(vap, wk))
error = EIO;
else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
- vap->iv_def_txkey = kid;
+ /*
+ * Inform the driver that this is the default
+ * transmit key. Now, ideally we'd just set
+ * a flag in the key update that would
+ * say "yes, we're the default key", but
+ * that currently isn't the way the ioctl ->
+ * key interface works.
+ */
+ ieee80211_crypto_set_deftxkey(vap, kid);
} else
error = ENXIO;
ieee80211_key_update_end(vap);
@@ -1201,7 +1244,7 @@ ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
return error;
}
-static __noinline int
+static int
ieee80211_ioctl_delkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_del_key dk;
@@ -1272,18 +1315,20 @@ mlmedebug(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
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)",
+ "station authenticate %s via MLME (reason: %d (%s))",
reason == IEEE80211_STATUS_SUCCESS ? "ACCEPT" : "REJECT",
- reason);
+ reason, ieee80211_reason_to_string(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);
+ "unknown MLME request %d (reason: %d (%s))", op, reason,
+ ieee80211_reason_to_string(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);
+ "station %s via MLME (reason: %d (%s))", ops[op].opstr,
+ reason, ieee80211_reason_to_string(reason));
}
#endif /* IEEE80211_DEBUG */
}
@@ -1318,28 +1363,32 @@ 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_table *nt = &vap->iv_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)) {
+ if (!IEEE80211_ADDR_EQ(mac, vap->iv_ifp->if_broadcastaddr)) {
IEEE80211_NODE_LOCK(nt);
ni = ieee80211_find_node_locked(nt, mac);
+ IEEE80211_NODE_UNLOCK(nt);
+ /*
+ * Don't do the node update inside the node
+ * table lock. This unfortunately causes LORs
+ * with drivers and their TX paths.
+ */
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
+static int
setmlme_common(struct ieee80211vap *vap, int op,
const uint8_t mac[IEEE80211_ADDR_LEN], int reason)
{
@@ -1384,6 +1433,22 @@ setmlme_common(struct ieee80211vap *vap, int op,
IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
ieee80211_free_node(ni);
break;
+ case IEEE80211_M_MBSS:
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_node_locked(nt, mac);
+ /*
+ * Don't do the node update inside the node
+ * table lock. This unfortunately causes LORs
+ * with drivers and their TX paths.
+ */
+ IEEE80211_NODE_UNLOCK(nt);
+ if (ni != NULL) {
+ ieee80211_node_leave(ni);
+ ieee80211_free_node(ni);
+ } else {
+ error = ENOENT;
+ }
+ break;
default:
error = EINVAL;
break;
@@ -1398,6 +1463,12 @@ setmlme_common(struct ieee80211vap *vap, int op,
}
IEEE80211_NODE_LOCK(nt);
ni = ieee80211_find_vap_node_locked(nt, vap, mac);
+ /*
+ * Don't do the node update inside the node
+ * table lock. This unfortunately causes LORs
+ * with drivers and their TX paths.
+ */
+ IEEE80211_NODE_UNLOCK(nt);
if (ni != NULL) {
mlmedebug(vap, mac, op, reason);
if (op == IEEE80211_MLME_AUTHORIZE)
@@ -1407,7 +1478,6 @@ setmlme_common(struct ieee80211vap *vap, int op,
ieee80211_free_node(ni);
} else
error = ENOENT;
- IEEE80211_NODE_UNLOCK(nt);
break;
case IEEE80211_MLME_AUTH:
if (vap->iv_opmode != IEEE80211_M_HOSTAP) {
@@ -1416,6 +1486,12 @@ setmlme_common(struct ieee80211vap *vap, int op,
}
IEEE80211_NODE_LOCK(nt);
ni = ieee80211_find_vap_node_locked(nt, vap, mac);
+ /*
+ * Don't do the node update inside the node
+ * table lock. This unfortunately causes LORs
+ * with drivers and their TX paths.
+ */
+ IEEE80211_NODE_UNLOCK(nt);
if (ni != NULL) {
mlmedebug(vap, mac, op, reason);
if (reason == IEEE80211_STATUS_SUCCESS) {
@@ -1439,7 +1515,6 @@ setmlme_common(struct ieee80211vap *vap, int op,
ieee80211_free_node(ni);
} else
error = ENOENT;
- IEEE80211_NODE_UNLOCK(nt);
break;
default:
error = EINVAL;
@@ -1474,7 +1549,7 @@ mlmelookup(void *arg, const struct ieee80211_scan_entry *se)
look->se = se;
}
-static __noinline int
+static int
setmlme_assoc_sta(struct ieee80211vap *vap,
const uint8_t mac[IEEE80211_ADDR_LEN], int ssid_len,
const uint8_t ssid[IEEE80211_NWID_LEN])
@@ -1499,12 +1574,13 @@ setmlme_assoc_sta(struct ieee80211vap *vap,
return 0;
}
-static __noinline int
+static int
setmlme_assoc_adhoc(struct ieee80211vap *vap,
const uint8_t mac[IEEE80211_ADDR_LEN], int ssid_len,
const uint8_t ssid[IEEE80211_NWID_LEN])
{
- struct ieee80211_scan_req sr;
+ struct ieee80211_scan_req *sr;
+ int error;
KASSERT(vap->iv_opmode == IEEE80211_M_IBSS ||
vap->iv_opmode == IEEE80211_M_AHDEMO,
@@ -1514,23 +1590,30 @@ setmlme_assoc_adhoc(struct ieee80211vap *vap,
if (ssid_len == 0)
return EINVAL;
+ sr = IEEE80211_MALLOC(sizeof(*sr), M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (sr == NULL)
+ return ENOMEM;
+
/* NB: IEEE80211_IOC_SSID call missing for ap_scan=2. */
memset(vap->iv_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
vap->iv_des_ssid[0].len = ssid_len;
memcpy(vap->iv_des_ssid[0].ssid, ssid, ssid_len);
vap->iv_des_nssid = 1;
- memset(&sr, 0, sizeof(sr));
- sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE;
- sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
- memcpy(sr.sr_ssid[0].ssid, ssid, ssid_len);
- sr.sr_ssid[0].len = ssid_len;
- sr.sr_nssid = 1;
+ sr->sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE;
+ sr->sr_duration = IEEE80211_IOC_SCAN_FOREVER;
+ memcpy(sr->sr_ssid[0].ssid, ssid, ssid_len);
+ sr->sr_ssid[0].len = ssid_len;
+ sr->sr_nssid = 1;
- return ieee80211_scanreq(vap, &sr);
+ error = ieee80211_scanreq(vap, sr);
+
+ IEEE80211_FREE(sr, M_TEMP);
+ return error;
}
-static __noinline int
+static int
ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_mlme mlme;
@@ -1545,7 +1628,9 @@ ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq)
mlme.im_op == IEEE80211_MLME_ASSOC)
return setmlme_assoc_sta(vap, mlme.im_macaddr,
vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
- else if (mlme.im_op == IEEE80211_MLME_ASSOC)
+ else if ((vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO) &&
+ mlme.im_op == IEEE80211_MLME_ASSOC)
return setmlme_assoc_adhoc(vap, mlme.im_macaddr,
mlme.im_ssid_len, mlme.im_ssid);
else
@@ -1553,7 +1638,7 @@ ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq)
mlme.im_macaddr, mlme.im_reason);
}
-static __noinline int
+static int
ieee80211_ioctl_macmac(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
uint8_t mac[IEEE80211_ADDR_LEN];
@@ -1578,7 +1663,7 @@ ieee80211_ioctl_macmac(struct ieee80211vap *vap, struct ieee80211req *ireq)
return 0;
}
-static __noinline int
+static int
ieee80211_ioctl_setmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
const struct ieee80211_aclator *acl = vap->iv_acl;
@@ -1616,7 +1701,7 @@ ieee80211_ioctl_setmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
return 0;
}
-static __noinline int
+static int
ieee80211_ioctl_setchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211com *ic = vap->iv_ic;
@@ -1625,13 +1710,13 @@ ieee80211_ioctl_setchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
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);
+ list = IEEE80211_MALLOC(ireq->i_len + IEEE80211_CHAN_BYTES, M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (list == NULL)
return ENOMEM;
error = copyin(ireq->i_data, list, ireq->i_len);
if (error) {
- free(list, M_TEMP);
+ IEEE80211_FREE(list, M_TEMP);
return error;
}
nchan = 0;
@@ -1650,7 +1735,7 @@ ieee80211_ioctl_setchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
}
}
if (nchan == 0) {
- free(list, M_TEMP);
+ IEEE80211_FREE(list, M_TEMP);
return EINVAL;
}
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */
@@ -1658,11 +1743,11 @@ ieee80211_ioctl_setchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
ic->ic_bsschan = IEEE80211_CHAN_ANYC;
memcpy(ic->ic_chan_active, chanlist, IEEE80211_CHAN_BYTES);
ieee80211_scan_flush(vap);
- free(list, M_TEMP);
+ IEEE80211_FREE(list, M_TEMP);
return ENETRESET;
}
-static __noinline int
+static int
ieee80211_ioctl_setstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
@@ -1688,7 +1773,7 @@ ieee80211_ioctl_setstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
return 0;
}
-static __noinline int
+static int
ieee80211_ioctl_setstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
@@ -1708,19 +1793,20 @@ ieee80211_ioctl_setstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
return error;
}
-static __noinline int
+static 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;
+ int isbss, ac, aggrmode;
if ((ic->ic_caps & IEEE80211_C_WME) == 0)
return EOPNOTSUPP;
isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
+ aggrmode = (wme->wme_flags & WME_F_AGGRMODE);
if (ac >= WME_NUM_AC)
ac = WME_AC_BE;
if (isbss) {
@@ -1732,47 +1818,28 @@ ieee80211_ioctl_setwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
}
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;
- }
+ wmep->wmep_logcwmin = ireq->i_val;
+ if (!isbss || !aggrmode)
+ 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;
- }
+ wmep->wmep_logcwmax = ireq->i_val;
+ if (!isbss || !aggrmode)
+ 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;
- }
+ wmep->wmep_aifsn = ireq->i_val;
+ if (!isbss || !aggrmode)
+ 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;
- }
+ wmep->wmep_txopLimit = ireq->i_val;
+ if (!isbss || !aggrmode)
+ 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)
+ if (!aggrmode)
chanp->wmep_acm = ireq->i_val;
break;
case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/
@@ -1821,6 +1888,8 @@ findchannel(struct ieee80211com *ic, int ieee, int mode)
/* NB: handled specially below */
[IEEE80211_MODE_11NA] = IEEE80211_CHAN_A,
[IEEE80211_MODE_11NG] = IEEE80211_CHAN_G,
+ [IEEE80211_MODE_VHT_5GHZ] = IEEE80211_CHAN_A,
+ [IEEE80211_MODE_VHT_2GHZ] = IEEE80211_CHAN_G,
};
u_int modeflags;
int i;
@@ -1845,11 +1914,27 @@ findchannel(struct ieee80211com *ic, int ieee, int mode)
!find11gchannel(ic, i, c->ic_freq))
return c;
} else {
- /* must check HT specially */
+ /* must check VHT specifically */
+ if ((mode == IEEE80211_MODE_VHT_5GHZ ||
+ mode == IEEE80211_MODE_VHT_2GHZ) &&
+ !IEEE80211_IS_CHAN_VHT(c))
+ continue;
+
+ /*
+ * Must check HT specially - only match on HT,
+ * not HT+VHT channels
+ */
if ((mode == IEEE80211_MODE_11NA ||
mode == IEEE80211_MODE_11NG) &&
!IEEE80211_IS_CHAN_HT(c))
continue;
+
+ if ((mode == IEEE80211_MODE_11NA ||
+ mode == IEEE80211_MODE_11NG) &&
+ IEEE80211_IS_CHAN_VHT(c))
+ continue;
+
+ /* Check that the modeflags above match */
if ((c->ic_flags & modeflags) == modeflags)
return c;
}
@@ -1908,7 +1993,7 @@ setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c)
if (IEEE80211_IS_CHAN_NOADHOC(c))
return EINVAL;
}
- if (vap->iv_state == IEEE80211_S_RUN &&
+ if ((vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) &&
vap->iv_bss->ni_chan == c)
return 0; /* NB: nothing to do */
}
@@ -1952,7 +2037,7 @@ setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c)
* Old api for setting the current channel; this is
* deprecated because channel numbers are ambiguous.
*/
-static __noinline int
+static int
ieee80211_ioctl_setchannel(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
@@ -1973,6 +2058,7 @@ ieee80211_ioctl_setchannel(struct ieee80211vap *vap,
if (c == NULL)
return EINVAL;
}
+
/*
* Fine tune channel selection based on desired mode:
* if 11b is requested, find the 11b version of any
@@ -1983,6 +2069,9 @@ ieee80211_ioctl_setchannel(struct ieee80211vap *vap,
* 11a channel returned,
* if 11ng is requested, find the ht version of any
* 11g channel returned,
+ * if 11ac is requested, find the 11ac version
+ * of any 11a/11na channel returned,
+ * (TBD) 11acg (2GHz VHT)
* otherwise we should be ok with what we've got.
*/
switch (vap->iv_des_mode) {
@@ -2019,6 +2108,17 @@ ieee80211_ioctl_setchannel(struct ieee80211vap *vap,
c = c2;
}
break;
+ case IEEE80211_MODE_VHT_2GHZ:
+ printf("%s: TBD\n", __func__);
+ break;
+ case IEEE80211_MODE_VHT_5GHZ:
+ if (IEEE80211_IS_CHAN_A(c)) {
+ c2 = findchannel(ic, ireq->i_val,
+ IEEE80211_MODE_VHT_5GHZ);
+ if (c2 != NULL)
+ c = c2;
+ }
+ break;
default: /* NB: no static turboG */
break;
}
@@ -2031,7 +2131,7 @@ ieee80211_ioctl_setchannel(struct ieee80211vap *vap,
* channel description is provide so there is no ambiguity in
* identifying the channel.
*/
-static __noinline int
+static int
ieee80211_ioctl_setcurchan(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
@@ -2044,6 +2144,7 @@ ieee80211_ioctl_setcurchan(struct ieee80211vap *vap,
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;
@@ -2055,7 +2156,7 @@ ieee80211_ioctl_setcurchan(struct ieee80211vap *vap,
return setcurchan(vap, c);
}
-static __noinline int
+static int
ieee80211_ioctl_setregdomain(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
@@ -2071,7 +2172,8 @@ ieee80211_ioctl_setregdomain(struct ieee80211vap *vap,
return EINVAL;
}
reg = (struct ieee80211_regdomain_req *)
- malloc(IEEE80211_REGDOMAIN_SIZE(nchans), M_TEMP, M_NOWAIT);
+ IEEE80211_MALLOC(IEEE80211_REGDOMAIN_SIZE(nchans), M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (reg == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
"%s: no memory, nchans %d\n", __func__, nchans);
@@ -2088,7 +2190,7 @@ ieee80211_ioctl_setregdomain(struct ieee80211vap *vap,
} else
error = ieee80211_setregdomain(vap, reg);
}
- free(reg, M_TEMP);
+ IEEE80211_FREE(reg, M_TEMP);
return (error == 0 ? ENETRESET : error);
}
@@ -2128,7 +2230,7 @@ checkmcs(int mcs)
return (mcs & 0x7f) <= 15; /* XXX could search ht rate set */
}
-static __noinline int
+static int
ieee80211_ioctl_settxparams(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
@@ -2205,7 +2307,7 @@ setappie(struct ieee80211_appie **aie, const struct ieee80211req *ireq)
if (ireq->i_len == 0) { /* delete any existing ie */
if (app != NULL) {
*aie = NULL; /* XXX racey */
- free(app, M_80211_NODE_IE);
+ IEEE80211_FREE(app, M_80211_NODE_IE);
}
return 0;
}
@@ -2219,20 +2321,21 @@ setappie(struct ieee80211_appie **aie, const struct ieee80211req *ireq)
*
* XXX bad bad bad
*/
- napp = (struct ieee80211_appie *) malloc(
- sizeof(struct ieee80211_appie) + ireq->i_len, M_80211_NODE_IE, M_NOWAIT);
+ napp = (struct ieee80211_appie *) IEEE80211_MALLOC(
+ sizeof(struct ieee80211_appie) + ireq->i_len, M_80211_NODE_IE,
+ IEEE80211_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);
+ IEEE80211_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);
+ IEEE80211_FREE(app, M_80211_NODE_IE);
return 0;
}
@@ -2248,7 +2351,7 @@ setwparsnie(struct ieee80211vap *vap, uint8_t *ie, int space)
vap->iv_rsn_ie = ie;
}
-static __noinline int
+static int
ieee80211_ioctl_setappie_locked(struct ieee80211vap *vap,
const struct ieee80211req *ireq, int fc0)
{
@@ -2326,7 +2429,7 @@ ieee80211_ioctl_setappie_locked(struct ieee80211vap *vap,
return error;
}
-static __noinline int
+static int
ieee80211_ioctl_setappie(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
@@ -2344,7 +2447,7 @@ ieee80211_ioctl_setappie(struct ieee80211vap *vap,
return error;
}
-static __noinline int
+static int
ieee80211_ioctl_chanswitch(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211com *ic = vap->iv_ic;
@@ -2444,6 +2547,11 @@ ieee80211_scanreq(struct ieee80211vap *vap, struct ieee80211_scan_req *sr)
* Otherwise just invoke the scan machinery directly.
*/
IEEE80211_LOCK(ic);
+ if (ic->ic_nrunning == 0) {
+ IEEE80211_UNLOCK(ic);
+ return ENXIO;
+ }
+
if (vap->iv_state == IEEE80211_S_INIT) {
/* NB: clobbers previous settings */
vap->iv_scanreq_flags = sr->sr_flags;
@@ -2480,26 +2588,28 @@ ieee80211_scanreq(struct ieee80211vap *vap, struct ieee80211_scan_req *sr)
#undef IEEE80211_IOC_SCAN_FLAGS
}
-static __noinline int
+static int
ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
- struct ieee80211com *ic = vap->iv_ic;
- struct ieee80211_scan_req sr; /* XXX off stack? */
+ struct ieee80211_scan_req *sr;
int error;
- /* NB: parent must be running */
- if ((ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
- return ENXIO;
-
- if (ireq->i_len != sizeof(sr))
+ if (ireq->i_len != sizeof(*sr))
return EINVAL;
- error = copyin(ireq->i_data, &sr, sizeof(sr));
+ sr = IEEE80211_MALLOC(sizeof(*sr), M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (sr == NULL)
+ return ENOMEM;
+ error = copyin(ireq->i_data, sr, sizeof(*sr));
if (error != 0)
- return error;
- return ieee80211_scanreq(vap, &sr);
+ goto bad;
+ error = ieee80211_scanreq(vap, sr);
+bad:
+ IEEE80211_FREE(sr, M_TEMP);
+ return error;
}
-static __noinline int
+static int
ieee80211_ioctl_setstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
@@ -2563,7 +2673,7 @@ ieee80211_ioctl_setdefault(struct ieee80211vap *vap, struct ieee80211req *ireq)
return EINVAL;
}
-static __noinline int
+static int
ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211req *ireq)
{
struct ieee80211com *ic = vap->iv_ic;
@@ -2642,7 +2752,17 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
if (kid >= IEEE80211_WEP_NKID &&
(uint16_t) kid != IEEE80211_KEYIX_NONE)
return EINVAL;
- vap->iv_def_txkey = kid;
+ /*
+ * Firmware devices may need to be told about an explicit
+ * key index here, versus just inferring it from the
+ * key set / change. Since we may also need to pause
+ * things like transmit before the key is updated,
+ * give the driver a chance to flush things by tying
+ * into key update begin/end.
+ */
+ ieee80211_key_update_begin(vap);
+ ieee80211_crypto_set_deftxkey(vap, kid);
+ ieee80211_key_update_end(vap);
break;
case IEEE80211_IOC_AUTHMODE:
switch (ireq->i_val) {
@@ -2785,6 +2905,9 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
/* XXX verify ciphers available */
flags = vap->iv_flags & ~IEEE80211_F_WPA;
switch (ireq->i_val) {
+ case 0:
+ /* wpa_supplicant calls this to clear the WPA config */
+ break;
case 1:
if (!(vap->iv_caps & IEEE80211_C_WPA1))
return EOPNOTSUPP;
@@ -2905,7 +3028,7 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
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) */
+ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only) */
error = ieee80211_ioctl_setwmeparam(vap, ireq);
break;
case IEEE80211_IOC_DTIM_PERIOD:
@@ -2941,6 +3064,24 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
if (isvap11g(vap))
error = ENETRESET;
break;
+ case IEEE80211_IOC_QUIET:
+ vap->iv_quiet= ireq->i_val;
+ break;
+ case IEEE80211_IOC_QUIET_COUNT:
+ vap->iv_quiet_count=ireq->i_val;
+ break;
+ case IEEE80211_IOC_QUIET_PERIOD:
+ vap->iv_quiet_period=ireq->i_val;
+ break;
+ case IEEE80211_IOC_QUIET_OFFSET:
+ vap->iv_quiet_offset=ireq->i_val;
+ break;
+ case IEEE80211_IOC_QUIET_DUR:
+ if(ireq->i_val < vap->iv_bss->ni_intval)
+ vap->iv_quiet_duration = ireq->i_val;
+ else
+ error = EINVAL;
+ break;
case IEEE80211_IOC_BGSCAN:
if (ireq->i_val) {
if ((vap->iv_caps & IEEE80211_C_BGSCAN) == 0)
@@ -3028,6 +3169,7 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
error = ERESTART;
break;
case IEEE80211_IOC_AMPDU_LIMIT:
+ /* XXX TODO: figure out ampdu_limit versus ampdu_rxmax */
if (!(IEEE80211_HTCAP_MAXRXAMPDU_8K <= ireq->i_val &&
ireq->i_val <= IEEE80211_HTCAP_MAXRXAMPDU_64K))
return EINVAL;
@@ -3207,6 +3349,62 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
if (isvapht(vap))
error = ERESTART;
break;
+ case IEEE80211_IOC_STBC:
+ /* Check if we can do STBC TX/RX before changing the setting */
+ if ((ireq->i_val & 1) &&
+ ((vap->iv_htcaps & IEEE80211_HTCAP_TXSTBC) == 0))
+ return EOPNOTSUPP;
+ if ((ireq->i_val & 2) &&
+ ((vap->iv_htcaps & IEEE80211_HTCAP_RXSTBC) == 0))
+ return EOPNOTSUPP;
+
+ /* TX */
+ if (ireq->i_val & 1)
+ vap->iv_flags_ht |= IEEE80211_FHT_STBC_TX;
+ else
+ vap->iv_flags_ht &= ~IEEE80211_FHT_STBC_TX;
+
+ /* RX */
+ if (ireq->i_val & 2)
+ vap->iv_flags_ht |= IEEE80211_FHT_STBC_RX;
+ else
+ vap->iv_flags_ht &= ~IEEE80211_FHT_STBC_RX;
+
+ /* NB: reset only if we're operating on an 11n channel */
+ if (isvapht(vap))
+ error = ERESTART;
+ break;
+
+ /* VHT */
+ case IEEE80211_IOC_VHTCONF:
+ if (ireq->i_val & 1)
+ ieee80211_syncflag_vht(vap, IEEE80211_FVHT_VHT);
+ else
+ ieee80211_syncflag_vht(vap, -IEEE80211_FVHT_VHT);
+
+ if (ireq->i_val & 2)
+ ieee80211_syncflag_vht(vap, IEEE80211_FVHT_USEVHT40);
+ else
+ ieee80211_syncflag_vht(vap, -IEEE80211_FVHT_USEVHT40);
+
+ if (ireq->i_val & 4)
+ ieee80211_syncflag_vht(vap, IEEE80211_FVHT_USEVHT80);
+ else
+ ieee80211_syncflag_vht(vap, -IEEE80211_FVHT_USEVHT80);
+
+ if (ireq->i_val & 8)
+ ieee80211_syncflag_vht(vap, IEEE80211_FVHT_USEVHT80P80);
+ else
+ ieee80211_syncflag_vht(vap, -IEEE80211_FVHT_USEVHT80P80);
+
+ if (ireq->i_val & 16)
+ ieee80211_syncflag_vht(vap, IEEE80211_FVHT_USEVHT160);
+ else
+ ieee80211_syncflag_vht(vap, -IEEE80211_FVHT_USEVHT160);
+
+ error = ENETRESET;
+ break;
+
default:
error = ieee80211_ioctl_setdefault(vap, ireq);
break;
@@ -3236,55 +3434,39 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
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;
+ int error = 0, wait = 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 ^ vap->iv_ifflags) & IFF_PROMISC) {
+ /*
+ * Enable promiscuous mode when:
+ * 1. Interface is not a member of bridge, or
+ * 2. Requested by user, or
+ * 3. In monitor (or adhoc-demo) mode.
+ */
+ if (ifp->if_bridge == NULL ||
+ (ifp->if_flags & IFF_PPROMISC) != 0 ||
+ vap->iv_opmode == IEEE80211_M_MONITOR ||
+ (vap->iv_opmode == IEEE80211_M_AHDEMO &&
+ (vap->iv_caps & IEEE80211_C_TDMA) == 0)) {
+ ieee80211_promisc(vap,
+ ifp->if_flags & IFF_PROMISC);
+ vap->iv_ifflags ^= IFF_PROMISC;
+ }
+ }
+ if ((ifp->if_flags ^ vap->iv_ifflags) & IFF_ALLMULTI) {
+ ieee80211_allmulti(vap, ifp->if_flags & IFF_ALLMULTI);
+ vap->iv_ifflags ^= IFF_ALLMULTI;
+ }
if (ifp->if_flags & IFF_UP) {
/*
* Bring ourself up unless we're already operational.
@@ -3292,22 +3474,40 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
* then it will automatically be brought up as a
* side-effect of bringing ourself up.
*/
- if (vap->iv_state == IEEE80211_S_INIT)
+ if (vap->iv_state == IEEE80211_S_INIT) {
+ if (ic->ic_nrunning == 0)
+ wait = 1;
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.
*/
+ if (ic->ic_nrunning == 1)
+ wait = 1;
ieee80211_stop_locked(vap);
}
IEEE80211_UNLOCK(ic);
/* Wait for parent ioctl handler if it was queued */
- ieee80211_waitfor_parent(ic);
+ if (wait) {
+ ieee80211_waitfor_parent(ic);
+
+ /*
+ * Check if the MAC address was changed
+ * via SIOCSIFLLADDR ioctl.
+ */
+ if_addr_rlock(ifp);
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ !IEEE80211_ADDR_EQ(vap->iv_myaddr, IF_LLADDR(ifp)))
+ IEEE80211_ADDR_COPY(vap->iv_myaddr,
+ IF_LLADDR(ifp));
+ if_addr_runlock(ifp);
+ }
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
- ieee80211_ioctl_updatemulti(ic);
+ ieee80211_runtask(ic, &ic->ic_mcast_task);
break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
@@ -3338,10 +3538,10 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
case SIOCSIFADDR:
/*
- * XXX Handle this directly so we can supress if_init calls.
+ * XXX Handle this directly so we can suppress 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 set IFF_UP and so suppress if_init being called when
* XXX it should be.
*/
ifa = (struct ifaddr *) data;
@@ -3355,24 +3555,6 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
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;
@@ -3381,17 +3563,16 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
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:
+ /*
+ * Pass unknown ioctls first to the driver, and if it
+ * returns ENOTTY, then to the generic Ethernet handler.
+ */
+ if (ic->ic_ioctl != NULL &&
+ (error = ic->ic_ioctl(ic, cmd, data)) != ENOTTY)
+ break;
error = ether_ioctl(ifp, cmd, data);
break;
}
- return error;
+ return (error);
}
diff --git a/freebsd/sys/net80211/ieee80211_ioctl.h b/freebsd/sys/net80211/ieee80211_ioctl.h
index cad55760..7d472bc6 100644
--- a/freebsd/sys/net80211/ieee80211_ioctl.h
+++ b/freebsd/sys/net80211/ieee80211_ioctl.h
@@ -241,8 +241,15 @@ struct ieee80211_stats {
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];
+ uint32_t is_beacon_bad; /* Number of bad beacons */
+ uint32_t is_ampdu_bar_tx; /* A-MPDU BAR frames TXed */
+ uint32_t is_ampdu_bar_tx_retry; /* A-MPDU BAR frames TX rtry */
+ uint32_t is_ampdu_bar_tx_fail; /* A-MPDU BAR frames TX fail */
+
+ uint32_t is_ff_encapfail; /* failed FF encap */
+ uint32_t is_amsdu_encapfail; /* failed A-MSDU encap */
+
+ uint32_t is_spare[5];
};
/*
@@ -335,8 +342,10 @@ enum {
struct ieee80211req_mesh_route {
uint8_t imr_flags;
-#define IEEE80211_MESHRT_FLAGS_VALID 0x01
-#define IEEE80211_MESHRT_FLAGS_PROXY 0x02
+#define IEEE80211_MESHRT_FLAGS_DISCOVER 0x01
+#define IEEE80211_MESHRT_FLAGS_VALID 0x02
+#define IEEE80211_MESHRT_FLAGS_PROXY 0x04
+#define IEEE80211_MESHRT_FLAGS_GATE 0x08
uint8_t imr_dest[IEEE80211_ADDR_LEN];
uint8_t imr_nexthop[IEEE80211_ADDR_LEN];
uint16_t imr_nhops;
@@ -547,6 +556,7 @@ 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 */
+ uint32_t dc_vhtcaps; /* VHT/802.11ac capabilities */
struct ieee80211req_chaninfo dc_chaninfo;
};
#define IEEE80211_DEVCAPS_SIZE(_nchan) \
@@ -693,6 +703,10 @@ struct ieee80211req {
#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_LDPC 114 /* LDPC Tx/RX (on, off) */
+
+/* VHT */
+#define IEEE80211_IOC_VHTCONF 130 /* VHT config (off, on; widths) */
#define IEEE80211_IOC_MESH_ID 170 /* mesh identifier */
#define IEEE80211_IOC_MESH_AP 171 /* accepting peerings */
@@ -705,6 +719,7 @@ struct ieee80211req {
#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_MESH_GATE 181 /* mesh gate XXX: 173? */
#define IEEE80211_IOC_HWMP_ROOTMODE 190 /* HWMP root mode */
#define IEEE80211_IOC_HWMP_MAXHOPS 191 /* number of hops before drop */
@@ -715,6 +730,11 @@ struct ieee80211req {
#define IEEE80211_IOC_TDMA_SLOTLEN 203 /* TDMA: slot length (usecs) */
#define IEEE80211_IOC_TDMA_BINTERVAL 204 /* TDMA: beacon intvl (slots) */
+#define IEEE80211_IOC_QUIET 205 /* Quiet Enable/Disable */
+#define IEEE80211_IOC_QUIET_PERIOD 206 /* Quiet Period */
+#define IEEE80211_IOC_QUIET_OFFSET 207 /* Quiet Offset */
+#define IEEE80211_IOC_QUIET_DUR 208 /* Quiet Duration */
+#define IEEE80211_IOC_QUIET_COUNT 209 /* Quiet Count */
/*
* Parameters for controlling a scan requested with
* IEEE80211_IOC_SCAN_REQ.
diff --git a/freebsd/sys/net80211/ieee80211_mesh.c b/freebsd/sys/net80211/ieee80211_mesh.c
index 303d430c..98359ea0 100644
--- a/freebsd/sys/net80211/ieee80211_mesh.c
+++ b/freebsd/sys/net80211/ieee80211_mesh.c
@@ -54,13 +54,18 @@ __FBSDID("$FreeBSD$");
#include <sys/proc.h>
#include <sys/sysctl.h>
+#include <net/bpf.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/if_llc.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_action.h>
+#ifdef IEEE80211_SUPPORT_SUPERG
+#include <net80211/ieee80211_superg.h>
+#endif
#include <net80211/ieee80211_input.h>
#include <net80211/ieee80211_mesh.h>
@@ -70,20 +75,22 @@ 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_gatemode_setup(struct ieee80211vap *);
+static void mesh_gatemode_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_transmit_to_gate(struct ieee80211vap *, struct mbuf *,
+ struct ieee80211_mesh_route *);
static void mesh_forward(struct ieee80211vap *, struct mbuf *,
const struct ieee80211_meshcntl *);
-static int mesh_input(struct ieee80211_node *, struct mbuf *, int, int);
+static int mesh_input(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_rx_stats *rxs, int, int);
static void mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int,
- int, int);
+ const struct ieee80211_rx_stats *rxs, int, int);
static void mesh_recv_ctl(struct ieee80211_node *, struct mbuf *, int);
static void mesh_peer_timeout_setup(struct ieee80211_node *);
static void mesh_peer_timeout_backoff(struct ieee80211_node *);
@@ -101,11 +108,16 @@ uint32_t mesh_airtime_calc(struct ieee80211_node *);
*/
static SYSCTL_NODE(_net_wlan, OID_AUTO, mesh, CTLFLAG_RD, 0,
"IEEE 802.11s parameters");
+static int ieee80211_mesh_gateint = -1;
+SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, gateint, CTLTYPE_INT | CTLFLAG_RW,
+ &ieee80211_mesh_gateint, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "mesh gate interval (ms)");
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)");
@@ -113,10 +125,20 @@ 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_backofftimeout = -1;
+SYSCTL_PROC(_net_wlan_mesh, OID_AUTO, backofftimeout, CTLTYPE_INT | CTLFLAG_RW,
+ &ieee80211_mesh_backofftimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "Backoff timeout (msec). This is to throutles peering forever when "
+ "not receiving answer or is rejected by a neighbor");
static int ieee80211_mesh_maxretries = 2;
-SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxretries, CTLTYPE_INT | CTLFLAG_RW,
+SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxretries, CTLFLAG_RW,
&ieee80211_mesh_maxretries, 0,
"Maximum retries during peer link establishment");
+static int ieee80211_mesh_maxholding = 2;
+SYSCTL_INT(_net_wlan_mesh, OID_AUTO, maxholding, CTLFLAG_RW,
+ &ieee80211_mesh_maxholding, 0,
+ "Maximum times we are allowed to transition to HOLDING state before "
+ "backinoff during peer link establishment");
static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -124,14 +146,14 @@ static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] =
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_recv_action_func mesh_recv_action_meshlmetric;
+static ieee80211_recv_action_func mesh_recv_action_meshgate;
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 ieee80211_send_action_func mesh_send_action_meshlmetric;
+static ieee80211_send_action_func mesh_send_action_meshgate;
static const struct ieee80211_mesh_proto_metric mesh_metric_airtime = {
.mpm_descr = "AIRTIME",
@@ -142,11 +164,15 @@ static const struct ieee80211_mesh_proto_metric mesh_metric_airtime = {
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_PREQ, "80211preq", "802.11 MESH Path Request frame");
+MALLOC_DEFINE(M_80211_MESH_PREP, "80211prep", "802.11 MESH Path Reply frame");
+MALLOC_DEFINE(M_80211_MESH_PERR, "80211perr", "802.11 MESH Path Error frame");
-MALLOC_DEFINE(M_80211_MESH_RT, "80211mesh", "802.11s routing table");
+/* The longer one of the lifetime should be stored as new lifetime */
+#define MESH_ROUTE_LIFETIME_MAX(a, b) (a > b ? a : b)
+
+MALLOC_DEFINE(M_80211_MESH_RT, "80211mesh_rt", "802.11s routing table");
+MALLOC_DEFINE(M_80211_MESH_GT_RT, "80211mesh_gt", "802.11s known gates table");
/*
* Helper functions to manipulate the Mesh routing table.
@@ -168,9 +194,10 @@ mesh_rt_find_locked(struct ieee80211_mesh_state *ms,
}
static struct ieee80211_mesh_route *
-mesh_rt_add_locked(struct ieee80211_mesh_state *ms,
+mesh_rt_add_locked(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_ADDR_EQ(broadcastaddr, dest),
@@ -178,12 +205,16 @@ mesh_rt_add_locked(struct ieee80211_mesh_state *ms,
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);
+ rt = IEEE80211_MALLOC(ALIGN(sizeof(struct ieee80211_mesh_route)) +
+ ms->ms_ppath->mpp_privlen, M_80211_MESH_RT,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (rt != NULL) {
+ rt->rt_vap = vap;
IEEE80211_ADDR_COPY(rt->rt_dest, dest);
rt->rt_priv = (void *)ALIGN(&rt[1]);
- rt->rt_crtime = ticks;
+ MESH_RT_ENTRY_LOCK_INIT(rt, "MBSS_RT");
+ callout_init(&rt->rt_discovery, 1);
+ rt->rt_updtime = ticks; /* create time */
TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next);
}
return rt;
@@ -215,12 +246,56 @@ ieee80211_mesh_rt_add(struct ieee80211vap *vap,
("%s: adding self to the routing table", __func__));
MESH_RT_LOCK(ms);
- rt = mesh_rt_add_locked(ms, dest);
+ rt = mesh_rt_add_locked(vap, dest);
MESH_RT_UNLOCK(ms);
return rt;
}
/*
+ * Update the route lifetime and returns the updated lifetime.
+ * If new_lifetime is zero and route is timedout it will be invalidated.
+ * new_lifetime is in msec
+ */
+int
+ieee80211_mesh_rt_update(struct ieee80211_mesh_route *rt, int new_lifetime)
+{
+ int timesince, now;
+ uint32_t lifetime = 0;
+
+ KASSERT(rt != NULL, ("route is NULL"));
+
+ now = ticks;
+ MESH_RT_ENTRY_LOCK(rt);
+
+ /* dont clobber a proxy entry gated by us */
+ if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY && rt->rt_nhops == 0) {
+ MESH_RT_ENTRY_UNLOCK(rt);
+ return rt->rt_lifetime;
+ }
+
+ timesince = ticks_to_msecs(now - rt->rt_updtime);
+ rt->rt_updtime = now;
+ if (timesince >= rt->rt_lifetime) {
+ if (new_lifetime != 0) {
+ rt->rt_lifetime = new_lifetime;
+ }
+ else {
+ rt->rt_flags &= ~IEEE80211_MESHRT_FLAGS_VALID;
+ rt->rt_lifetime = 0;
+ }
+ } else {
+ /* update what is left of lifetime */
+ rt->rt_lifetime = rt->rt_lifetime - timesince;
+ rt->rt_lifetime = MESH_ROUTE_LIFETIME_MAX(
+ new_lifetime, rt->rt_lifetime);
+ }
+ lifetime = rt->rt_lifetime;
+ MESH_RT_ENTRY_UNLOCK(rt);
+
+ return lifetime;
+}
+
+/*
* Add a proxy route (as needed) for the specified destination.
*/
void
@@ -233,7 +308,7 @@ ieee80211_mesh_proxy_check(struct ieee80211vap *vap,
MESH_RT_LOCK(ms);
rt = mesh_rt_find_locked(ms, dest);
if (rt == NULL) {
- rt = mesh_rt_add_locked(ms, dest);
+ rt = mesh_rt_add_locked(vap, dest);
if (rt == NULL) {
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
"%s", "unable to add proxy entry");
@@ -241,12 +316,14 @@ ieee80211_mesh_proxy_check(struct ieee80211vap *vap,
} else {
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
"%s", "add proxy entry");
+ IEEE80211_ADDR_COPY(rt->rt_mesh_gate, vap->iv_myaddr);
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) {
+ KASSERT(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY,
+ ("no proxy flag for poxy entry"));
struct ieee80211com *ic = vap->iv_ic;
/*
* Fix existing entry created by received frames from
@@ -271,7 +348,14 @@ 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);
+ /*
+ * Grab the lock before destroying it, to be sure no one else
+ * is holding the route.
+ */
+ MESH_RT_ENTRY_LOCK(rt);
+ callout_drain(&rt->rt_discovery);
+ MESH_RT_ENTRY_LOCK_DESTROY(rt);
+ IEEE80211_FREE(rt, M_80211_MESH_RT);
}
void
@@ -284,6 +368,13 @@ ieee80211_mesh_rt_del(struct ieee80211vap *vap,
MESH_RT_LOCK(ms);
TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) {
if (IEEE80211_ADDR_EQ(rt->rt_dest, dest)) {
+ if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
+ ms->ms_ppath->mpp_senderror(vap, dest, rt,
+ IEEE80211_REASON_MESH_PERR_NO_PROXY);
+ } else {
+ ms->ms_ppath->mpp_senderror(vap, dest, rt,
+ IEEE80211_REASON_MESH_PERR_DEST_UNREACH);
+ }
mesh_rt_del(ms, rt);
MESH_RT_UNLOCK(ms);
return;
@@ -335,20 +426,22 @@ mesh_rt_flush_invalid(struct ieee80211vap *vap)
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)
+ /* Discover paths will be deleted by their own callout */
+ if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_DISCOVER)
+ continue;
+ ieee80211_mesh_rt_update(rt, 0);
+ if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
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++) {
+ for (i = 0; i < nitems(mesh_proto_paths); i++) {
if (strncmp(mpp->mpp_descr, mesh_proto_paths[i].mpp_descr,
IEEE80211_MESH_PROTO_DSZ) == 0)
return EEXIST;
@@ -368,7 +461,7 @@ ieee80211_mesh_register_proto_metric(const struct
{
int i, firstempty = -1;
- for (i = 0; i < N(mesh_proto_metrics); i++) {
+ for (i = 0; i < nitems(mesh_proto_metrics); i++) {
if (strncmp(mpm->mpm_descr, mesh_proto_metrics[i].mpm_descr,
IEEE80211_MESH_PROTO_DSZ) == 0)
return EEXIST;
@@ -388,7 +481,7 @@ 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++) {
+ for (i = 0; i < nitems(mesh_proto_paths); i++) {
if (strcasecmp(mesh_proto_paths[i].mpp_descr, name) == 0) {
ms->ms_ppath = &mesh_proto_paths[i];
return 0;
@@ -403,7 +496,7 @@ 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++) {
+ for (i = 0; i < nitems(mesh_proto_metrics); i++) {
if (strcasecmp(mesh_proto_metrics[i].mpm_descr, name) == 0) {
ms->ms_pmetric = &mesh_proto_metrics[i];
return 0;
@@ -411,7 +504,48 @@ mesh_select_proto_metric(struct ieee80211vap *vap, const char *name)
}
return ENOENT;
}
-#undef N
+
+static void
+mesh_gatemode_setup(struct ieee80211vap *vap)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+
+ /*
+ * NB: When a mesh gate is running as a ROOT it shall
+ * not send out periodic GANNs but instead mark the
+ * mesh gate flag for the corresponding proactive PREQ
+ * and RANN frames.
+ */
+ if (ms->ms_flags & IEEE80211_MESHFLAGS_ROOT ||
+ (ms->ms_flags & IEEE80211_MESHFLAGS_GATE) == 0) {
+ callout_drain(&ms->ms_gatetimer);
+ return ;
+ }
+ callout_reset(&ms->ms_gatetimer, ieee80211_mesh_gateint,
+ mesh_gatemode_cb, vap);
+}
+
+static void
+mesh_gatemode_cb(void *arg)
+{
+ struct ieee80211vap *vap = (struct ieee80211vap *)arg;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_meshgann_ie gann;
+
+ gann.gann_flags = 0; /* Reserved */
+ gann.gann_hopcount = 0;
+ gann.gann_ttl = ms->ms_ttl;
+ IEEE80211_ADDR_COPY(gann.gann_addr, vap->iv_myaddr);
+ gann.gann_seq = ms->ms_gateseq++;
+ gann.gann_interval = ieee80211_mesh_gateint;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, vap->iv_bss,
+ "send broadcast GANN (seq %u)", gann.gann_seq);
+
+ ieee80211_send_action(vap->iv_bss, IEEE80211_ACTION_CAT_MESH,
+ IEEE80211_ACTION_MESH_GANN, &gann);
+ mesh_gatemode_setup(vap);
+}
static void
ieee80211_mesh_init(void)
@@ -423,42 +557,44 @@ ieee80211_mesh_init(void)
/*
* Setup mesh parameters that depends on the clock frequency.
*/
+ ieee80211_mesh_gateint = msecs_to_ticks(10000);
ieee80211_mesh_retrytimeout = msecs_to_ticks(40);
ieee80211_mesh_holdingtimeout = msecs_to_ticks(40);
ieee80211_mesh_confirmtimeout = msecs_to_ticks(40);
+ ieee80211_mesh_backofftimeout = msecs_to_ticks(5000);
/*
* Register action frame handlers.
*/
- ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_OPEN,
mesh_recv_action_meshpeering_open);
- ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CONFIRM,
mesh_recv_action_meshpeering_confirm);
- ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
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_recv_action_register(IEEE80211_ACTION_CAT_MESH,
+ IEEE80211_ACTION_MESH_LMETRIC, mesh_recv_action_meshlmetric);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESH,
+ IEEE80211_ACTION_MESH_GANN, mesh_recv_action_meshgate);
- ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_OPEN,
mesh_send_action_meshpeering_open);
- ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CONFIRM,
mesh_send_action_meshpeering_confirm);
- ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESHPEERING,
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_SELF_PROT,
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);
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH,
+ IEEE80211_ACTION_MESH_LMETRIC,
+ mesh_send_action_meshlmetric);
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_MESH,
+ IEEE80211_ACTION_MESH_GANN,
+ mesh_send_action_meshgate);
/*
* Register Airtime Link Metric.
@@ -490,7 +626,7 @@ mesh_vdetach_peers(void *arg, struct ieee80211_node *ni)
args[1] = ni->ni_mllid;
args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CLOSE,
args);
}
@@ -509,9 +645,9 @@ mesh_vdetach(struct ieee80211vap *vap)
ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_vdetach_peers,
NULL);
ieee80211_mesh_rt_flush(vap);
- mtx_destroy(&ms->ms_rt_lock);
+ MESH_RT_LOCK_DESTROY(ms);
ms->ms_ppath->mpp_vdetach(vap);
- free(vap->iv_mesh, M_80211_VAP);
+ IEEE80211_FREE(vap->iv_mesh, M_80211_VAP);
vap->iv_mesh = NULL;
}
@@ -524,8 +660,8 @@ mesh_vattach(struct ieee80211vap *vap)
vap->iv_opdetach = mesh_vdetach;
vap->iv_recv_mgmt = mesh_recv_mgmt;
vap->iv_recv_ctl = mesh_recv_ctl;
- ms = malloc(sizeof(struct ieee80211_mesh_state), M_80211_VAP,
- M_NOWAIT | M_ZERO);
+ ms = IEEE80211_MALLOC(sizeof(struct ieee80211_mesh_state), M_80211_VAP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (ms == NULL) {
printf("%s: couldn't alloc MBSS state\n", __func__);
return;
@@ -534,9 +670,12 @@ mesh_vattach(struct ieee80211vap *vap)
ms->ms_seq = 0;
ms->ms_flags = (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_FWD);
ms->ms_ttl = IEEE80211_MESH_DEFAULT_TTL;
+ TAILQ_INIT(&ms->ms_known_gates);
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_RT_LOCK_INIT(ms, "MBSS");
+ callout_init(&ms->ms_cleantimer, 1);
+ callout_init(&ms->ms_gatetimer, 1);
+ ms->ms_gateseq = 0;
mesh_select_proto_metric(vap, "AIRTIME");
KASSERT(ms->ms_pmetric, ("ms_pmetric == NULL"));
mesh_select_proto_path(vap, "HWMP");
@@ -565,8 +704,10 @@ mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
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)
+ if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) {
callout_drain(&ms->ms_cleantimer);
+ callout_drain(&ms->ms_gatetimer);
+ }
switch (nstate) {
case IEEE80211_S_INIT:
switch (ostate) {
@@ -664,16 +805,15 @@ mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* Update bss node channel to reflect where
* we landed after CSA.
*/
- ieee80211_node_set_chan(vap->iv_bss,
+ ieee80211_node_set_chan(ni,
ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
- ieee80211_htchanflags(vap->iv_bss->ni_chan)));
+ ieee80211_htchanflags(ni->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));
@@ -688,9 +828,10 @@ mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
default:
break;
}
- ieee80211_node_authorize(vap->iv_bss);
+ ieee80211_node_authorize(ni);
callout_reset(&ms->ms_cleantimer, ms->ms_ppath->mpp_inact,
mesh_rt_cleanup_cb, vap);
+ mesh_gatemode_setup(vap);
break;
default:
break;
@@ -711,6 +852,44 @@ mesh_rt_cleanup_cb(void *arg)
mesh_rt_cleanup_cb, vap);
}
+/*
+ * Mark a mesh STA as gate and return a pointer to it.
+ * If this is first time, we create a new gate route.
+ * Always update the path route to this mesh gate.
+ */
+struct ieee80211_mesh_gate_route *
+ieee80211_mesh_mark_gate(struct ieee80211vap *vap, const uint8_t *addr,
+ struct ieee80211_mesh_route *rt)
+{
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_gate_route *gr = NULL, *next;
+ int found = 0;
+
+ MESH_RT_LOCK(ms);
+ TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, next) {
+ if (IEEE80211_ADDR_EQ(gr->gr_addr, addr)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* New mesh gate add it to known table. */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, addr,
+ "%s", "stored new gate information from pro-PREQ.");
+ gr = IEEE80211_MALLOC(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
+ M_80211_MESH_GT_RT,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ IEEE80211_ADDR_COPY(gr->gr_addr, addr);
+ TAILQ_INSERT_TAIL(&ms->ms_known_gates, gr, gr_next);
+ }
+ gr->gr_route = rt;
+ /* TODO: link from path route to gate route */
+ MESH_RT_UNLOCK(ms);
+
+ return gr;
+}
+
/*
* Helper function to note the Mesh Peer Link FSM change.
@@ -822,8 +1001,8 @@ mesh_checkpseq(struct ieee80211vap *vap,
/*
* Iterate the routing table and locate the next hop.
*/
-static struct ieee80211_node *
-mesh_find_txnode(struct ieee80211vap *vap,
+struct ieee80211_node *
+ieee80211_mesh_find_txnode(struct ieee80211vap *vap,
const uint8_t dest[IEEE80211_ADDR_LEN])
{
struct ieee80211_mesh_route *rt;
@@ -831,16 +1010,129 @@ mesh_find_txnode(struct ieee80211vap *vap,
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)) {
+ if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
- "%s: !valid or proxy, flags 0x%x", __func__, rt->rt_flags);
+ "%s: !valid, flags 0x%x", __func__, rt->rt_flags);
/* XXX stat */
return NULL;
}
+ if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
+ rt = ieee80211_mesh_rt_find(vap, rt->rt_mesh_gate);
+ if (rt == NULL) return NULL;
+ if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest,
+ "%s: meshgate !valid, flags 0x%x", __func__,
+ rt->rt_flags);
+ /* XXX stat */
+ return NULL;
+ }
+ }
return ieee80211_find_txnode(vap, rt->rt_nexthop);
}
+static void
+mesh_transmit_to_gate(struct ieee80211vap *vap, struct mbuf *m,
+ struct ieee80211_mesh_route *rt_gate)
+{
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_node *ni;
+
+ IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
+
+ ni = ieee80211_mesh_find_txnode(vap, rt_gate->rt_dest);
+ if (ni == NULL) {
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * Send through the VAP packet transmit path.
+ * This consumes the node ref grabbed above and
+ * the mbuf, regardless of whether there's a problem
+ * or not.
+ */
+ (void) ieee80211_vap_pkt_send_dest(vap, m, ni);
+}
+
+/*
+ * Forward the queued frames to known valid mesh gates.
+ * Assume destination to be outside the MBSS (i.e. proxy entry),
+ * If no valid mesh gates are known silently discard queued frames.
+ * After transmitting frames to all known valid mesh gates, this route
+ * will be marked invalid, and a new path discovery will happen in the hopes
+ * that (at least) one of the mesh gates have a new proxy entry for us to use.
+ */
+void
+ieee80211_mesh_forward_to_gates(struct ieee80211vap *vap,
+ struct ieee80211_mesh_route *rt_dest)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rt_gate;
+ struct ieee80211_mesh_gate_route *gr = NULL, *gr_next;
+ struct mbuf *m, *mcopy, *next;
+
+ IEEE80211_TX_UNLOCK_ASSERT(ic);
+
+ KASSERT( rt_dest->rt_flags == IEEE80211_MESHRT_FLAGS_DISCOVER,
+ ("Route is not marked with IEEE80211_MESHRT_FLAGS_DISCOVER"));
+
+ /* XXX: send to more than one valid mash gate */
+ MESH_RT_LOCK(ms);
+
+ m = ieee80211_ageq_remove(&ic->ic_stageq,
+ (struct ieee80211_node *)(uintptr_t)
+ ieee80211_mac_hash(ic, rt_dest->rt_dest));
+
+ TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, gr_next) {
+ rt_gate = gr->gr_route;
+ if (rt_gate == NULL) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
+ rt_dest->rt_dest,
+ "mesh gate with no path %6D",
+ gr->gr_addr, ":");
+ continue;
+ }
+ if ((rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
+ continue;
+ KASSERT(rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_GATE,
+ ("route not marked as a mesh gate"));
+ KASSERT((rt_gate->rt_flags &
+ IEEE80211_MESHRT_FLAGS_PROXY) == 0,
+ ("found mesh gate that is also marked porxy"));
+ /*
+ * convert route to a proxy route gated by the current
+ * mesh gate, this is needed so encap can built data
+ * frame with correct address.
+ */
+ rt_dest->rt_flags = IEEE80211_MESHRT_FLAGS_PROXY |
+ IEEE80211_MESHRT_FLAGS_VALID;
+ rt_dest->rt_ext_seq = 1; /* random value */
+ IEEE80211_ADDR_COPY(rt_dest->rt_mesh_gate, rt_gate->rt_dest);
+ IEEE80211_ADDR_COPY(rt_dest->rt_nexthop, rt_gate->rt_nexthop);
+ rt_dest->rt_metric = rt_gate->rt_metric;
+ rt_dest->rt_nhops = rt_gate->rt_nhops;
+ ieee80211_mesh_rt_update(rt_dest, ms->ms_ppath->mpp_inact);
+ MESH_RT_UNLOCK(ms);
+ /* XXX: lock?? */
+ mcopy = m_dup(m, M_NOWAIT);
+ for (; mcopy != NULL; mcopy = next) {
+ next = mcopy->m_nextpkt;
+ mcopy->m_nextpkt = NULL;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP,
+ rt_dest->rt_dest,
+ "flush queued frame %p len %d", mcopy,
+ mcopy->m_pkthdr.len);
+ mesh_transmit_to_gate(vap, mcopy, rt_gate);
+ }
+ MESH_RT_LOCK(ms);
+ }
+ rt_dest->rt_flags = 0; /* Mark invalid */
+ m_freem(m);
+ MESH_RT_UNLOCK(ms);
+}
+
/*
* Forward the specified frame.
* Decrement the TTL and set TA to our MAC address.
@@ -852,7 +1144,6 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m,
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;
@@ -861,9 +1152,17 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m,
struct ieee80211_node *ni;
int err;
- if (mc->mc_ttl == 0) {
+ /* This is called from the RX path - don't hold this lock */
+ IEEE80211_TX_UNLOCK_ASSERT(ic);
+
+ /*
+ * mesh ttl of 1 means we are the last one receiving it,
+ * according to amendment we decrement and then check if
+ * 0, if so we dont forward.
+ */
+ if (mc->mc_ttl < 1) {
IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
- "%s", "frame not fwd'd, ttl 0");
+ "%s", "frame not fwd'd, ttl 1");
vap->iv_stats.is_mesh_fwd_ttl++;
return;
}
@@ -873,12 +1172,12 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m,
vap->iv_stats.is_mesh_fwd_disabled++;
return;
}
- mcopy = m_dup(m, M_DONTWAIT);
+ mcopy = m_dup(m, M_NOWAIT);
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++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return;
}
mcopy = m_pullup(mcopy, ieee80211_hdrspace(ic, wh) +
@@ -887,7 +1186,7 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m,
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++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
m_freem(mcopy);
return;
}
@@ -901,10 +1200,18 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m,
ni = ieee80211_ref_node(vap->iv_bss);
mcopy->m_flags |= M_MCAST;
} else {
- ni = mesh_find_txnode(vap, whcopy->i_addr3);
+ ni = ieee80211_mesh_find_txnode(vap, whcopy->i_addr3);
if (ni == NULL) {
+ /*
+ * [Optional] any of the following three actions:
+ * o silently discard
+ * o trigger a path discovery
+ * o inform TA that meshDA is unknown.
+ */
IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh,
"%s", "frame not fwd'd, no path");
+ ms->ms_ppath->mpp_senderror(vap, whcopy->i_addr3, NULL,
+ IEEE80211_REASON_MESH_PERR_NO_FI);
vap->iv_stats.is_mesh_fwd_nopath++;
m_freem(mcopy);
return;
@@ -919,21 +1226,31 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m,
/* 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++;
- }
+
+ /*
+ * XXX this bypasses all of the VAP TX handling; it passes frames
+ * directly to the parent interface.
+ *
+ * Because of this, there's no TX lock being held as there's no
+ * encaps state being used.
+ *
+ * Doing a direct parent transmit may not be the correct thing
+ * to do here; we'll have to re-think this soon.
+ */
+ IEEE80211_TX_LOCK(ic);
+ err = ieee80211_parent_xmitpkt(ic, mcopy);
+ IEEE80211_TX_UNLOCK(ic);
+ if (!err)
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
}
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)
+#define WHDIR(wh) ((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK)
+#define MC01(mc) ((const struct ieee80211_meshcntl_ae01 *)mc)
uint8_t b[sizeof(struct ieee80211_qosframe_addr4) +
- sizeof(struct ieee80211_meshcntl_ae11)];
+ sizeof(struct ieee80211_meshcntl_ae10)];
const struct ieee80211_qosframe_addr4 *wh;
const struct ieee80211_meshcntl_ae10 *mc;
struct ether_header *eh;
@@ -967,13 +1284,14 @@ mesh_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, int meshdrlen)
m_adj(m, hdrlen - sizeof(*eh));
}
eh = mtod(m, struct ether_header *);
- ae = mc->mc_flags & 3;
+ ae = mc->mc_flags & IEEE80211_MESH_AE_MASK;
if (WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS) {
IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr1);
- if (ae == 0) {
+ if (ae == IEEE80211_MESH_AE_00) {
IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr3);
- } else if (ae == 1) {
- IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr4);
+ } else if (ae == IEEE80211_MESH_AE_01) {
+ IEEE80211_ADDR_COPY(eh->ether_shost,
+ MC01(mc)->mc_addr4);
} else {
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
(const struct ieee80211_frame *)wh, NULL,
@@ -983,12 +1301,12 @@ mesh_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, int meshdrlen)
return NULL;
}
} else {
- if (ae == 0) {
+ if (ae == IEEE80211_MESH_AE_00) {
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 if (ae == IEEE80211_MESH_AE_10) {
+ IEEE80211_ADDR_COPY(eh->ether_dhost, mc->mc_addr5);
+ IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr6);
} else {
IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
(const struct ieee80211_frame *)wh, NULL,
@@ -998,19 +1316,20 @@ mesh_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, int meshdrlen)
return NULL;
}
}
-#ifdef ALIGNED_POINTER
+#ifndef __NO_STRICT_ALIGNMENT
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 */
+#endif /* !__NO_STRICT_ALIGNMENT */
if (llc != NULL) {
eh = mtod(m, struct ether_header *);
eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh));
}
return m;
-#undef WDIR
+#undef WDIR
+#undef MC01
}
/*
@@ -1026,12 +1345,13 @@ mesh_isucastforme(struct ieee80211vap *vap, const struct ieee80211_frame *wh,
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 */
+ KASSERT(ae == IEEE80211_MESH_AE_00 || ae == IEEE80211_MESH_AE_10,
+ ("bad AE %d", ae));
+ if (ae == IEEE80211_MESH_AE_10) { /* 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);
+ ieee80211_mesh_rt_find(vap, mc10->mc_addr5);
/* check for proxy route to ourself */
return (rt != NULL &&
(rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY));
@@ -1039,20 +1359,185 @@ mesh_isucastforme(struct ieee80211vap *vap, const struct ieee80211_frame *wh,
return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
}
+/*
+ * Verifies transmitter, updates lifetime, precursor list and forwards data.
+ * > 0 means we have forwarded data and no need to process locally
+ * == 0 means we want to process locally (and we may have forwarded data
+ * < 0 means there was an error and data should be discarded
+ */
+static int
+mesh_recv_indiv_data_to_fwrd(struct ieee80211vap *vap, struct mbuf *m,
+ struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
+{
+ struct ieee80211_qosframe_addr4 *qwh;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rt_meshda, *rt_meshsa;
+
+ /* This is called from the RX path - don't hold this lock */
+ IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
+
+ qwh = (struct ieee80211_qosframe_addr4 *)wh;
+
+ /*
+ * TODO:
+ * o verify addr2 is a legitimate transmitter
+ * o lifetime of precursor of addr3 (addr2) is max(init, curr)
+ * o lifetime of precursor of addr4 (nexthop) is max(init, curr)
+ */
+
+ /* set lifetime of addr3 (meshDA) to initial value */
+ rt_meshda = ieee80211_mesh_rt_find(vap, qwh->i_addr3);
+ if (rt_meshda == NULL) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, qwh->i_addr2,
+ "no route to meshDA(%6D)", qwh->i_addr3, ":");
+ /*
+ * [Optional] any of the following three actions:
+ * o silently discard [X]
+ * o trigger a path discovery [ ]
+ * o inform TA that meshDA is unknown. [ ]
+ */
+ /* XXX: stats */
+ return (-1);
+ }
+
+ ieee80211_mesh_rt_update(rt_meshda, ticks_to_msecs(
+ ms->ms_ppath->mpp_inact));
+
+ /* set lifetime of addr4 (meshSA) to initial value */
+ rt_meshsa = ieee80211_mesh_rt_find(vap, qwh->i_addr4);
+ KASSERT(rt_meshsa != NULL, ("no route"));
+ ieee80211_mesh_rt_update(rt_meshsa, ticks_to_msecs(
+ ms->ms_ppath->mpp_inact));
+
+ mesh_forward(vap, m, mc);
+ return (1); /* dont process locally */
+}
+
+/*
+ * Verifies transmitter, updates lifetime, precursor list and process data
+ * locally, if data is proxy with AE = 10 it could mean data should go
+ * on another mesh path or data should be forwarded to the DS.
+ *
+ * > 0 means we have forwarded data and no need to process locally
+ * == 0 means we want to process locally (and we may have forwarded data
+ * < 0 means there was an error and data should be discarded
+ */
+static int
+mesh_recv_indiv_data_to_me(struct ieee80211vap *vap, struct mbuf *m,
+ struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
+{
+ struct ieee80211_qosframe_addr4 *qwh;
+ const struct ieee80211_meshcntl_ae10 *mc10;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_route *rt;
+ int ae;
+
+ /* This is called from the RX path - don't hold this lock */
+ IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
+
+ qwh = (struct ieee80211_qosframe_addr4 *)wh;
+ mc10 = (const struct ieee80211_meshcntl_ae10 *)mc;
+
+ /*
+ * TODO:
+ * o verify addr2 is a legitimate transmitter
+ * o lifetime of precursor entry is max(init, curr)
+ */
+
+ /* set lifetime of addr4 (meshSA) to initial value */
+ rt = ieee80211_mesh_rt_find(vap, qwh->i_addr4);
+ KASSERT(rt != NULL, ("no route"));
+ ieee80211_mesh_rt_update(rt, ticks_to_msecs(ms->ms_ppath->mpp_inact));
+ rt = NULL;
+
+ ae = mc10->mc_flags & IEEE80211_MESH_AE_MASK;
+ KASSERT(ae == IEEE80211_MESH_AE_00 ||
+ ae == IEEE80211_MESH_AE_10, ("bad AE %d", ae));
+ if (ae == IEEE80211_MESH_AE_10) {
+ if (IEEE80211_ADDR_EQ(mc10->mc_addr5, qwh->i_addr3)) {
+ return (0); /* process locally */
+ }
+
+ rt = ieee80211_mesh_rt_find(vap, mc10->mc_addr5);
+ if (rt != NULL &&
+ (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) &&
+ (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) == 0) {
+ /*
+ * Forward on another mesh-path, according to
+ * amendment as specified in 9.32.4.1
+ */
+ IEEE80211_ADDR_COPY(qwh->i_addr3, mc10->mc_addr5);
+ mesh_forward(vap, m,
+ (const struct ieee80211_meshcntl *)mc10);
+ return (1); /* dont process locally */
+ }
+ /*
+ * All other cases: forward of MSDUs from the MBSS to DS indiv.
+ * addressed according to 13.11.3.2.
+ */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, qwh->i_addr2,
+ "forward frame to DS, SA(%6D) DA(%6D)",
+ mc10->mc_addr6, ":", mc10->mc_addr5, ":");
+ }
+ return (0); /* process locally */
+}
+
+/*
+ * Try to forward the group addressed data on to other mesh STAs, and
+ * also to the DS.
+ *
+ * > 0 means we have forwarded data and no need to process locally
+ * == 0 means we want to process locally (and we may have forwarded data
+ * < 0 means there was an error and data should be discarded
+ */
+static int
+mesh_recv_group_data(struct ieee80211vap *vap, struct mbuf *m,
+ struct ieee80211_frame *wh, const struct ieee80211_meshcntl *mc)
+{
+#define MC01(mc) ((const struct ieee80211_meshcntl_ae01 *)mc)
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
+
+ /* This is called from the RX path - don't hold this lock */
+ IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic);
+
+ mesh_forward(vap, m, mc);
+
+ if(mc->mc_ttl > 0) {
+ if (mc->mc_flags & IEEE80211_MESH_AE_01) {
+ /*
+ * Forward of MSDUs from the MBSS to DS group addressed
+ * (according to 13.11.3.2)
+ * This happens by delivering the packet, and a bridge
+ * will sent it on another port member.
+ */
+ if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE &&
+ ms->ms_flags & IEEE80211_MESHFLAGS_FWD) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH,
+ MC01(mc)->mc_addr4, "%s",
+ "forward from MBSS to the DS");
+ }
+ }
+ }
+ return (0); /* process locally */
+#undef MC01
+}
+
static int
-mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
+mesh_input(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
#define HAS_SEQ(type) ((type & 0x4) == 0)
+#define MC01(mc) ((const struct ieee80211_meshcntl_ae01 *)mc)
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;
+ int hdrspace, meshdrlen, need_tap, error;
+ uint8_t dir, type, subtype, ae;
uint32_t seq;
- uint8_t *addr;
- ieee80211_seq rxseq;
+ const uint8_t *addr;
+ uint8_t qos[2];
KASSERT(ni != NULL, ("null node"));
ni->ni_inact = ni->ni_inact_reload;
@@ -1060,6 +1545,9 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
need_tap = 1; /* mbuf need to be tapped. */
type = -1; /* undefined */
+ /* This is called from the RX path - don't hold this lock */
+ IEEE80211_TX_UNLOCK_ASSERT(ic);
+
if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, NULL,
@@ -1094,24 +1582,8 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
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 (! ieee80211_check_rxseq(ni, wh)) {
- /* 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);
+ if (! ieee80211_check_rxseq(ni, wh, wh->i_addr1))
goto out;
- }
- ni->ni_rxseqs[tid] = rxseq;
}
}
#ifdef IEEE80211_DEBUG
@@ -1122,7 +1594,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
*
* NB: this check is also done upon peering link initiation.
*/
- if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+ if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
wh, NULL, "%s", "disallowed by ACL");
vap->iv_stats.is_rx_acl++;
@@ -1140,7 +1612,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
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,
@@ -1148,8 +1620,64 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
vap->iv_stats.is_rx_wrongdir++;
goto err;
}
- /* pull up enough to get to the mesh control */
+
+ /* All Mesh data frames are QoS subtype */
+ if (!HAS_SEQ(type)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect subtype 0x%x", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ goto err;
+ }
+
+ /*
+ * Next up, any fragmentation.
+ * XXX: we defrag before we even try to forward,
+ * Mesh Control field is not present in sub-sequent
+ * fragmented frames. This is in contrast to Draft 4.0.
+ */
hdrspace = ieee80211_hdrspace(ic, wh);
+ 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 = mtod(m, struct ieee80211_frame *); /* NB: after defrag */
+
+ /*
+ * Now we have a complete Mesh Data frame.
+ */
+
+ /*
+ * Only fromDStoDS data frames use 4 address qos frames
+ * as specified in amendment. Otherwise addr4 is located
+ * in the Mesh Control field and a 3 address qos frame
+ * is used.
+ */
+ if (IEEE80211_IS_DSTODS(wh))
+ *(uint16_t *)qos = *(uint16_t *)
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos;
+ else
+ *(uint16_t *)qos = *(uint16_t *)
+ ((struct ieee80211_qosframe *)wh)->i_qos;
+
+ /*
+ * NB: The mesh STA sets the Mesh Control Present
+ * subfield to 1 in the Mesh Data frame containing
+ * an unfragmented MSDU, an A-MSDU, or the first
+ * fragment of an MSDU.
+ * After defrag it should always be present.
+ */
+ if (!(qos[1] & IEEE80211_QOS_MC)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
+ ni->ni_macaddr, NULL,
+ "%s", "Mesh control field not present");
+ vap->iv_stats.is_rx_elem_missing++; /* XXX: kinda */
+ goto err;
+ }
+
+ /* pull up enough to get to the mesh control */
if (m->m_len < hdrspace + sizeof(struct ieee80211_meshcntl) &&
(m = m_pullup(m, hdrspace +
sizeof(struct ieee80211_meshcntl))) == NULL) {
@@ -1166,12 +1694,28 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
*/
mc = (const struct ieee80211_meshcntl *)
(mtod(m, const uint8_t *) + hdrspace);
+ ae = mc->mc_flags & IEEE80211_MESH_AE_MASK;
meshdrlen = sizeof(struct ieee80211_meshcntl) +
- (mc->mc_flags & 3) * IEEE80211_ADDR_LEN;
+ ae * IEEE80211_ADDR_LEN;
hdrspace += meshdrlen;
- seq = LE_READ_4(mc->mc_seq);
+
+ /* pull complete hdrspace = ieee80211_hdrspace + meshcontrol */
+ if ((meshdrlen > sizeof(struct ieee80211_meshcntl)) &&
+ (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 */
+ }
+ /* XXX: are we sure there is no reallocating after m_pullup? */
+
+ seq = le32dec(mc->mc_seq);
if (IEEE80211_IS_MULTICAST(wh->i_addr1))
addr = wh->i_addr3;
+ else if (ae == IEEE80211_MESH_AE_01)
+ addr = MC01(mc)->mc_addr4;
else
addr = ((struct ieee80211_qosframe_addr4 *)wh)->i_addr4;
if (IEEE80211_ADDR_EQ(vap->iv_myaddr, addr)) {
@@ -1185,38 +1729,22 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
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.
- */
+ /* This code "routes" the frame to the right control path */
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_ADDR_EQ(vap->iv_myaddr, wh->i_addr3))
+ error =
+ mesh_recv_indiv_data_to_me(vap, m, wh, mc);
+ else if (IEEE80211_IS_MULTICAST(wh->i_addr3))
+ error = mesh_recv_group_data(vap, m, wh, mc);
+ else
+ error = mesh_recv_indiv_data_to_fwrd(vap, m,
+ wh, mc);
+ } else
+ error = mesh_recv_group_data(vap, m, wh, mc);
+ if (error < 0)
+ goto err;
+ else if (error > 0)
+ goto out;
if (ieee80211_radiotap_active_vap(vap))
ieee80211_radiotap_rx(vap, m);
@@ -1238,7 +1766,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
IEEE80211_NODE_STAT(ni, rx_decap);
goto err;
}
- if (qos & IEEE80211_QOS_AMSDU) {
+ if (qos[0] & IEEE80211_QOS_AMSDU) {
m = ieee80211_decap_amsdu(ni, m);
if (m == NULL)
return IEEE80211_FC0_TYPE_DATA;
@@ -1266,18 +1794,17 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
(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],
+ ieee80211_mgt_subtype_name(subtype),
ether_sprintf(wh->i_addr2), rssi);
}
#endif
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
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);
+ vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
goto out;
case IEEE80211_FC0_TYPE_CTL:
vap->iv_stats.is_rx_ctl++;
@@ -1290,7 +1817,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
break;
}
err:
- ifp->if_ierrors++;
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
out:
if (m != NULL) {
if (need_tap && ieee80211_radiotap_active_vap(vap))
@@ -1298,16 +1825,20 @@ out:
m_freem(m);
}
return type;
+#undef HAS_SEQ
+#undef MC01
}
static void
mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
- int rssi, int nf)
+ const struct ieee80211_rx_stats *rxs, 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_channel *rxchan = ic->ic_curchan;
struct ieee80211_frame *wh;
+ struct ieee80211_mesh_route *rt;
uint8_t *frm, *efrm;
wh = mtod(m0, struct ieee80211_frame *);
@@ -1318,11 +1849,17 @@ mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
case IEEE80211_FC0_SUBTYPE_BEACON:
{
struct ieee80211_scanparams scan;
+ struct ieee80211_channel *c;
/*
* We process beacon/probe response
* frames to discover neighbors.
*/
- if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ if (rxs != NULL) {
+ c = ieee80211_lookup_channel_rxstatus(vap, rxs);
+ if (c != NULL)
+ rxchan = c;
+ }
+ if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0)
return;
/*
* Count frame now that we know it's to be processed.
@@ -1348,7 +1885,7 @@ mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
ieee80211_probe_curchan(vap, 1);
ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
}
- ieee80211_add_scan(vap, &scan, wh,
+ ieee80211_add_scan(vap, rxchan, &scan, wh,
subtype, rssi, nf);
return;
}
@@ -1380,8 +1917,7 @@ mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
/*
* Peer only based on the current ACL policy.
*/
- if (vap->iv_acl != NULL &&
- !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+ if (vap->iv_acl != NULL && !vap->iv_acl->iac_check(vap, wh)) {
IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
wh, NULL, "%s", "disallowed by ACL");
vap->iv_stats.is_rx_acl++;
@@ -1398,30 +1934,53 @@ mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
}
/*
* 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);
+ (ms->ms_flags & IEEE80211_MESHFLAGS_AP)) {
+ switch (ni->ni_mlstate) {
+ case IEEE80211_NODE_MESH_IDLE:
+ {
+ uint16_t args[1];
+
+ /* Wait for backoff callout to reset counter */
+ if (ni->ni_mlhcnt >= ieee80211_mesh_maxholding)
+ return;
+
+ 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_SELF_PROT,
+ IEEE80211_ACTION_MESHPEERING_OPEN, args);
+ ni->ni_mlrcnt = 0;
+ mesh_peer_timeout_setup(ni);
+ break;
+ }
+ case IEEE80211_NODE_MESH_ESTABLISHED:
+ {
+ /*
+ * Valid beacon from a peer mesh STA
+ * bump TA lifetime
+ */
+ rt = ieee80211_mesh_rt_find(vap, wh->i_addr2);
+ if(rt != NULL) {
+ ieee80211_mesh_rt_update(rt,
+ ticks_to_msecs(
+ ms->ms_ppath->mpp_inact));
+ }
+ break;
+ }
+ default:
+ break; /* ignore */
+ }
}
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,
@@ -1445,7 +2004,6 @@ mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
* [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) {
@@ -1515,6 +2073,7 @@ mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
case IEEE80211_FC0_SUBTYPE_ATIM:
case IEEE80211_FC0_SUBTYPE_DISASSOC:
case IEEE80211_FC0_SUBTYPE_AUTH:
@@ -1544,8 +2103,7 @@ mesh_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype)
}
/*
- * Parse meshpeering action ie's for open+confirm frames; the
- * important bits are returned in the supplied structure.
+ * Parse meshpeering action ie's for MPM frames
*/
static const struct ieee80211_meshpeer_ie *
mesh_parse_meshpeering_action(struct ieee80211_node *ni,
@@ -1555,9 +2113,11 @@ mesh_parse_meshpeering_action(struct ieee80211_node *ni,
{
struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_meshpeer_ie *mpie;
- const uint8_t *meshid, *meshconf, *meshpeer;
+ uint16_t args[3];
+ const uint8_t *meshid, *meshconf;
+ uint8_t sendclose = 0; /* 1 = MPM frame rejected, close will be sent */
- meshid = meshconf = meshpeer = NULL;
+ meshid = meshconf = NULL;
while (efrm - frm > 1) {
IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return NULL);
switch (*frm) {
@@ -1568,18 +2128,30 @@ mesh_parse_meshpeering_action(struct ieee80211_node *ni,
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);
+ mp->peer_len = mpie->peer_len;
+ mp->peer_proto = le16dec(&mpie->peer_proto);
+ mp->peer_llinkid = le16dec(&mpie->peer_llinkid);
+ switch (subtype) {
+ case IEEE80211_ACTION_MESHPEERING_CONFIRM:
+ mp->peer_linkid =
+ le16dec(&mpie->peer_linkid);
+ break;
+ case IEEE80211_ACTION_MESHPEERING_CLOSE:
+ /* NB: peer link ID is optional */
+ if (mpie->peer_len ==
+ (IEEE80211_MPM_BASE_SZ + 2)) {
+ mp->peer_linkid = 0;
+ mp->peer_rcode =
+ le16dec(&mpie->peer_linkid);
+ } else {
+ mp->peer_linkid =
+ le16dec(&mpie->peer_linkid);
+ mp->peer_rcode =
+ le16dec(&mpie->peer_rcode);
+ }
+ break;
}
break;
}
@@ -1587,22 +2159,46 @@ mesh_parse_meshpeering_action(struct ieee80211_node *ni,
}
/*
- * 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.
+ * Verify the contents of the frame.
+ * If it 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];
+ if (mesh_verify_meshpeer(vap, subtype, (const uint8_t *)mp)) {
+ sendclose = 1;
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+ wh, NULL, "%s", "MPM validation failed");
+ }
+ /* If meshid is not the same reject any frames type. */
+ if (sendclose == 0 && mesh_verify_meshid(vap, meshid)) {
+ sendclose = 1;
IEEE80211_DISCARD(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
wh, NULL, "%s", "not for our mesh");
+ if (subtype == IEEE80211_ACTION_MESHPEERING_CLOSE) {
+ /*
+ * Standard not clear about this, if we dont ignore
+ * there will be an endless loop between nodes sending
+ * CLOSE frames between each other with wrong meshid.
+ * Discard and timers will bring FSM to IDLE state.
+ */
+ return NULL;
+ }
+ }
+
+ /*
+ * Close frames are accepted if meshid is the same.
+ * Verify the other two types.
+ */
+ if (sendclose == 0 && subtype != IEEE80211_ACTION_MESHPEERING_CLOSE &&
+ mesh_verify_meshconf(vap, meshconf)) {
+ sendclose = 1;
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+ wh, NULL, "%s", "configuration missmatch");
+ }
+
+ if (sendclose) {
vap->iv_stats.is_rx_mgtdiscard++;
switch (ni->ni_mlstate) {
case IEEE80211_NODE_MESH_IDLE:
@@ -1615,9 +2211,17 @@ mesh_parse_meshpeering_action(struct ieee80211_node *ni,
case IEEE80211_NODE_MESH_CONFIRMRCV:
args[0] = ni->ni_mlpid;
args[1] = ni->ni_mllid;
- args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
+ /* Reason codes for rejection */
+ switch (subtype) {
+ case IEEE80211_ACTION_MESHPEERING_OPEN:
+ args[2] = IEEE80211_REASON_MESH_CPVIOLATION;
+ break;
+ case IEEE80211_ACTION_MESHPEERING_CONFIRM:
+ args[2] = IEEE80211_REASON_MESH_INCONS_PARAMS;
+ break;
+ }
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CLOSE,
args);
mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -1626,6 +2230,7 @@ mesh_parse_meshpeering_action(struct ieee80211_node *ni,
}
return NULL;
}
+
return (const struct ieee80211_meshpeer_ie *) mp;
}
@@ -1635,6 +2240,7 @@ mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
const uint8_t *frm, const uint8_t *efrm)
{
struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_mesh_state *ms = vap->iv_mesh;
struct ieee80211_meshpeer_ie ie;
const struct ieee80211_meshpeer_ie *meshpeer;
uint16_t args[3];
@@ -1652,6 +2258,19 @@ mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
switch (ni->ni_mlstate) {
case IEEE80211_NODE_MESH_IDLE:
+ /* Reject open request if reached our maximum neighbor count */
+ if (ms->ms_neighbors >= IEEE80211_MESH_MAX_NEIGHBORS) {
+ args[0] = meshpeer->peer_llinkid;
+ args[1] = 0;
+ args[2] = IEEE80211_REASON_MESH_MAX_PEERS;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_SELF_PROT,
+ IEEE80211_ACTION_MESHPEERING_CLOSE,
+ args);
+ /* stay in IDLE state */
+ return (0);
+ }
+ /* Open frame accepted */
mesh_linkchange(ni, IEEE80211_NODE_MESH_OPENRCV);
ni->ni_mllid = meshpeer->peer_llinkid;
ni->ni_mlpid = mesh_generateid(vap);
@@ -1660,13 +2279,13 @@ mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
args[0] = ni->ni_mlpid;
/* Announce we're open too... */
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
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_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CONFIRM,
args);
mesh_peer_timeout_setup(ni);
@@ -1678,7 +2297,7 @@ mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
args[1] = ni->ni_mlpid;
args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CLOSE,
args);
mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -1689,7 +2308,7 @@ mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
args[0] = ni->ni_mlpid;
args[1] = ni->ni_mllid;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CONFIRM,
args);
break;
@@ -1699,7 +2318,7 @@ mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
args[0] = ni->ni_mlpid;
args[1] = ni->ni_mllid;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CONFIRM,
args);
/* NB: don't setup/clear any timeout */
@@ -1711,7 +2330,7 @@ mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
args[1] = ni->ni_mllid;
args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CLOSE,
args);
mesh_linkchange(ni,
@@ -1724,7 +2343,7 @@ mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
args[0] = ni->ni_mlpid;
args[1] = ni->ni_mllid;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CONFIRM,
args);
mesh_peer_timeout_stop(ni);
@@ -1735,7 +2354,7 @@ mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
args[1] = ni->ni_mlpid;
args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CLOSE,
args);
mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -1745,16 +2364,17 @@ mesh_recv_action_meshpeering_open(struct ieee80211_node *ni,
args[0] = ni->ni_mlpid;
args[1] = ni->ni_mllid;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
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;
+ /* Standard not clear about what the reaason code should be */
+ args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CLOSE,
args);
break;
@@ -1790,13 +2410,15 @@ mesh_recv_action_meshpeering_confirm(struct ieee80211_node *ni,
break;
case IEEE80211_NODE_MESH_OPENSNT:
mesh_linkchange(ni, IEEE80211_NODE_MESH_CONFIRMRCV);
+ mesh_peer_timeout_setup(ni);
break;
case IEEE80211_NODE_MESH_HOLDING:
args[0] = ni->ni_mlpid;
args[1] = meshpeer->peer_llinkid;
- args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
+ /* Standard not clear about what the reaason code should be */
+ args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CLOSE,
args);
break;
@@ -1806,7 +2428,7 @@ mesh_recv_action_meshpeering_confirm(struct ieee80211_node *ni,
args[1] = ni->ni_mllid;
args[2] = IEEE80211_REASON_PEER_LINK_CANCELED;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CLOSE,
args);
mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -1829,8 +2451,23 @@ mesh_recv_action_meshpeering_close(struct ieee80211_node *ni,
const struct ieee80211_frame *wh,
const uint8_t *frm, const uint8_t *efrm)
{
+ struct ieee80211_meshpeer_ie ie;
+ const struct ieee80211_meshpeer_ie *meshpeer;
uint16_t args[3];
+ /* +2 for action + code */
+ meshpeer = mesh_parse_meshpeering_action(ni, wh, frm+2, efrm, &ie,
+ IEEE80211_ACTION_MESHPEERING_CLOSE);
+ if (meshpeer == NULL) {
+ return 0;
+ }
+
+ /*
+ * XXX: check reason code, for example we could receive
+ * IEEE80211_REASON_MESH_MAX_PEERS then we should not attempt
+ * to peer again.
+ */
+
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
ni, "%s", "recv PEER CLOSE");
@@ -1846,7 +2483,7 @@ mesh_recv_action_meshpeering_close(struct ieee80211_node *ni,
args[1] = ni->ni_mllid;
args[2] = IEEE80211_REASON_MESH_CLOSE_RCVD;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CLOSE,
args);
mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -1854,7 +2491,7 @@ mesh_recv_action_meshpeering_close(struct ieee80211_node *ni,
break;
case IEEE80211_NODE_MESH_HOLDING:
mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
- mesh_peer_timeout_setup(ni);
+ mesh_peer_timeout_stop(ni);
break;
}
return 0;
@@ -1864,41 +2501,201 @@ mesh_recv_action_meshpeering_close(struct ieee80211_node *ni,
* Link Metric handling.
*/
static int
-mesh_recv_action_meshlmetric_req(struct ieee80211_node *ni,
+mesh_recv_action_meshlmetric(struct ieee80211_node *ni,
const struct ieee80211_frame *wh,
const uint8_t *frm, const uint8_t *efrm)
{
- uint32_t metric;
+ const struct ieee80211_meshlmetric_ie *ie =
+ (const struct ieee80211_meshlmetric_ie *)
+ (frm+2); /* action + code */
+ struct ieee80211_meshlmetric_ie lm_rep;
+
+ if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
+ lm_rep.lm_flags = 0;
+ lm_rep.lm_metric = mesh_airtime_calc(ni);
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_MESH,
+ IEEE80211_ACTION_MESH_LMETRIC,
+ &lm_rep);
+ }
+ /* XXX: else do nothing for now */
+ return 0;
+}
+
+/*
+ * Parse meshgate action ie's for GANN frames.
+ * Returns -1 if parsing fails, otherwise 0.
+ */
+static int
+mesh_parse_meshgate_action(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh, /* XXX for VERIFY_LENGTH */
+ struct ieee80211_meshgann_ie *ie, const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ const struct ieee80211_meshgann_ie *gannie;
+
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return -1);
+ switch (*frm) {
+ case IEEE80211_ELEMID_MESHGANN:
+ gannie = (const struct ieee80211_meshgann_ie *) frm;
+ memset(ie, 0, sizeof(*ie));
+ ie->gann_ie = gannie->gann_ie;
+ ie->gann_len = gannie->gann_len;
+ ie->gann_flags = gannie->gann_flags;
+ ie->gann_hopcount = gannie->gann_hopcount;
+ ie->gann_ttl = gannie->gann_ttl;
+ IEEE80211_ADDR_COPY(ie->gann_addr, gannie->gann_addr);
+ ie->gann_seq = le32dec(&gannie->gann_seq);
+ ie->gann_interval = le16dec(&gannie->gann_interval);
+ break;
+ }
+ frm += frm[1] + 2;
+ }
- metric = mesh_airtime_calc(ni);
- ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHLMETRIC,
- IEEE80211_ACTION_MESHLMETRIC_REP,
- &metric);
return 0;
}
+/*
+ * Mesh Gate Announcement handling.
+ */
static int
-mesh_recv_action_meshlmetric_rep(struct ieee80211_node *ni,
+mesh_recv_action_meshgate(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_mesh_state *ms = vap->iv_mesh;
+ struct ieee80211_mesh_gate_route *gr, *next;
+ struct ieee80211_mesh_route *rt_gate;
+ struct ieee80211_meshgann_ie pgann;
+ struct ieee80211_meshgann_ie ie;
+ int found = 0;
+
+ /* +2 for action + code */
+ if (mesh_parse_meshgate_action(ni, wh, &ie, frm+2, efrm) != 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
+ ni->ni_macaddr, NULL, "%s",
+ "GANN parsing failed");
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return (0);
+ }
+
+ if (IEEE80211_ADDR_EQ(vap->iv_myaddr, ie.gann_addr))
+ return 0;
+
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ni->ni_macaddr,
+ "received GANN, meshgate: %6D (seq %u)", ie.gann_addr, ":",
+ ie.gann_seq);
+
+ if (ms == NULL)
+ return (0);
+ MESH_RT_LOCK(ms);
+ TAILQ_FOREACH_SAFE(gr, &ms->ms_known_gates, gr_next, next) {
+ if (!IEEE80211_ADDR_EQ(gr->gr_addr, ie.gann_addr))
+ continue;
+ if (ie.gann_seq <= gr->gr_lastseq) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_MESH,
+ ni->ni_macaddr, NULL,
+ "GANN old seqno %u <= %u",
+ ie.gann_seq, gr->gr_lastseq);
+ MESH_RT_UNLOCK(ms);
+ return (0);
+ }
+ /* corresponding mesh gate found & GANN accepted */
+ found = 1;
+ break;
+
+ }
+ if (found == 0) {
+ /* this GANN is from a new mesh Gate add it to known table. */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ie.gann_addr,
+ "stored new GANN information, seq %u.", ie.gann_seq);
+ gr = IEEE80211_MALLOC(ALIGN(sizeof(struct ieee80211_mesh_gate_route)),
+ M_80211_MESH_GT_RT,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ IEEE80211_ADDR_COPY(gr->gr_addr, ie.gann_addr);
+ TAILQ_INSERT_TAIL(&ms->ms_known_gates, gr, gr_next);
+ }
+ gr->gr_lastseq = ie.gann_seq;
+
+ /* check if we have a path to this gate */
+ rt_gate = mesh_rt_find_locked(ms, gr->gr_addr);
+ if (rt_gate != NULL &&
+ rt_gate->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) {
+ gr->gr_route = rt_gate;
+ rt_gate->rt_flags |= IEEE80211_MESHRT_FLAGS_GATE;
+ }
+
+ MESH_RT_UNLOCK(ms);
+
+ /* popagate only if decremented ttl >= 1 && forwarding is enabled */
+ if ((ie.gann_ttl - 1) < 1 && !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
+ return 0;
+ pgann.gann_flags = ie.gann_flags; /* Reserved */
+ pgann.gann_hopcount = ie.gann_hopcount + 1;
+ pgann.gann_ttl = ie.gann_ttl - 1;
+ IEEE80211_ADDR_COPY(pgann.gann_addr, ie.gann_addr);
+ pgann.gann_seq = ie.gann_seq;
+ pgann.gann_interval = ie.gann_interval;
+
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, ie.gann_addr,
+ "%s", "propagate GANN");
+
+ ieee80211_send_action(vap->iv_bss, IEEE80211_ACTION_CAT_MESH,
+ IEEE80211_ACTION_MESH_GANN, &pgann);
+
return 0;
}
static int
-mesh_send_action(struct ieee80211_node *ni, struct mbuf *m)
+mesh_send_action(struct ieee80211_node *ni,
+ const uint8_t sa[IEEE80211_ADDR_LEN],
+ const uint8_t da[IEEE80211_ADDR_LEN],
+ struct mbuf *m)
{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_bpf_params params;
+ int ret;
+
+ KASSERT(ni != NULL, ("null node"));
+
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
+ "block %s frame in CAC state", "Mesh action");
+ vap->iv_stats.is_tx_badstate++;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ return EIO; /* XXX */
+ }
+
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ return ENOMEM;
+ }
+
+ IEEE80211_TX_LOCK(ic);
+ 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 */
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;
+ if (IEEE80211_IS_MULTICAST(da))
+ params.ibp_try0 = 1;
+ else
+ params.ibp_try0 = ni->ni_txparms->maxretry;
params.ibp_power = ni->ni_txpower;
- return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION,
- &params);
+
+ IEEE80211_NODE_STAT(ni, tx_mgmt);
+
+ ret = ieee80211_raw_output(vap, ni, m, &params);
+ IEEE80211_TX_UNLOCK(ic);
+ return (ret);
}
#define ADDSHORT(frm, v) do { \
@@ -1937,10 +2734,10 @@ mesh_send_action_meshpeering_open(struct ieee80211_node *ni,
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_RATE_SIZE
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ 2 + IEEE80211_MESHID_LEN
- + sizeof(struct ieee80211_meshconf_ie)
+ + sizeof(struct ieee80211_meshconf_ie)
+ sizeof(struct ieee80211_meshpeer_ie)
);
if (m != NULL) {
@@ -1963,10 +2760,10 @@ mesh_send_action_meshpeering_open(struct ieee80211_node *ni,
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,
+ frm = ieee80211_add_meshpeer(frm, IEEE80211_ACTION_MESHPEERING_OPEN,
args[0], 0, 0);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- return mesh_send_action(ni, m);
+ return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
} else {
vap->iv_stats.is_tx_nobuf++;
ieee80211_free_node(ni);
@@ -2000,10 +2797,10 @@ mesh_send_action_meshpeering_confirm(struct ieee80211_node *ni,
+ 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_RATE_SIZE
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ 2 + IEEE80211_MESHID_LEN
- + sizeof(struct ieee80211_meshconf_ie)
+ + sizeof(struct ieee80211_meshconf_ie)
+ sizeof(struct ieee80211_meshpeer_ie)
);
if (m != NULL) {
@@ -2031,10 +2828,10 @@ mesh_send_action_meshpeering_confirm(struct ieee80211_node *ni,
frm = ieee80211_add_meshid(frm, vap);
frm = ieee80211_add_meshconf(frm, vap);
frm = ieee80211_add_meshpeer(frm,
- IEEE80211_MESH_PEER_LINK_CONFIRM,
+ IEEE80211_ACTION_MESHPEERING_CONFIRM,
args[0], args[1], 0);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- return mesh_send_action(ni, m);
+ return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
} else {
vap->iv_stats.is_tx_nobuf++;
ieee80211_free_node(ni);
@@ -2053,8 +2850,8 @@ mesh_send_action_meshpeering_close(struct ieee80211_node *ni,
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]);
+ "send PEER CLOSE action: localid 0x%x, peerid 0x%x reason %d (%s)",
+ args[0], args[1], args[2], ieee80211_reason_to_string(args[2]));
IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
@@ -2066,26 +2863,24 @@ mesh_send_action_meshpeering_close(struct ieee80211_node *ni,
sizeof(uint16_t) /* action+category */
+ sizeof(uint16_t) /* reason code */
+ 2 + IEEE80211_MESHID_LEN
- + sizeof(struct ieee80211_meshpeer_ie)
+ + 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,
+ IEEE80211_ACTION_MESHPEERING_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);
+ return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
} else {
vap->iv_stats.is_tx_nobuf++;
ieee80211_free_node(ni);
@@ -2094,17 +2889,23 @@ mesh_send_action_meshpeering_close(struct ieee80211_node *ni,
}
static int
-mesh_send_action_meshlink_request(struct ieee80211_node *ni,
+mesh_send_action_meshlmetric(struct ieee80211_node *ni,
int category, int action, void *arg0)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_meshlmetric_ie *ie = arg0;
struct mbuf *m;
uint8_t *frm;
- IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni,
- "%s", "send LINK METRIC REQUEST action");
-
+ if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+ ni, "%s", "send LINK METRIC REQUEST action");
+ } else {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+ ni, "send LINK METRIC REPLY action: metric 0x%x",
+ ie->lm_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);
@@ -2112,18 +2913,22 @@ mesh_send_action_meshlink_request(struct ieee80211_node *ni,
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
- sizeof(uint16_t) /* action+category */
+ sizeof(uint16_t) + /* action+category */
+ sizeof(struct ieee80211_meshlmetric_ie)
);
if (m != NULL) {
/*
- * mesh link metric request
+ * mesh link metric
* [1] category
* [1] action
+ * [tlv] mesh link metric
*/
*frm++ = category;
*frm++ = action;
+ frm = ieee80211_add_meshlmetric(frm,
+ ie->lm_flags, ie->lm_metric);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- return mesh_send_action(ni, m);
+ return mesh_send_action(ni, vap->iv_myaddr, ni->ni_macaddr, m);
} else {
vap->iv_stats.is_tx_nobuf++;
ieee80211_free_node(ni);
@@ -2132,18 +2937,15 @@ mesh_send_action_meshlink_request(struct ieee80211_node *ni,
}
static int
-mesh_send_action_meshlink_reply(struct ieee80211_node *ni,
- int category, int action, void *args0)
+mesh_send_action_meshgate(struct ieee80211_node *ni,
+ int category, int action, void *arg0)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
- uint32_t *metric = args0;
+ struct ieee80211_meshgann_ie *ie = arg0;
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);
@@ -2151,21 +2953,21 @@ mesh_send_action_meshlink_reply(struct ieee80211_node *ni,
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
- sizeof(uint16_t) /* action+category */
- + sizeof(struct ieee80211_meshlmetric_ie)
+ sizeof(uint16_t) + /* action+category */
+ IEEE80211_MESHGANN_BASE_SZ
);
if (m != NULL) {
/*
- * mesh link metric reply
+ * mesh link metric
* [1] category
* [1] action
- * [tlv] mesh link metric
+ * [tlv] mesh gate annoucement
*/
*frm++ = category;
*frm++ = action;
- frm = ieee80211_add_meshlmetric(frm, *metric);
+ frm = ieee80211_add_meshgate(frm, ie);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- return mesh_send_action(ni, m);
+ return mesh_send_action(ni, vap->iv_myaddr, broadcastaddr, m);
} else {
vap->iv_stats.is_tx_nobuf++;
ieee80211_free_node(ni);
@@ -2215,6 +3017,15 @@ mesh_peer_timeout_stop(struct ieee80211_node *ni)
callout_drain(&ni->ni_mltimer);
}
+static void
+mesh_peer_backoff_cb(void *arg)
+{
+ struct ieee80211_node *ni = (struct ieee80211_node *)arg;
+
+ /* After backoff timeout, try to peer automatically again. */
+ ni->ni_mlhcnt = 0;
+}
+
/*
* Mesh Peer Link Management FSM timeout handling.
*/
@@ -2238,7 +3049,7 @@ mesh_peer_timeout_cb(void *arg)
args[0] = ni->ni_mlpid;
args[2] = IEEE80211_REASON_MESH_MAX_RETRIES;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
IEEE80211_ACTION_MESHPEERING_CLOSE, args);
ni->ni_mlrcnt = 0;
mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
@@ -2246,28 +3057,27 @@ mesh_peer_timeout_cb(void *arg)
} else {
args[0] = ni->ni_mlpid;
ieee80211_send_action(ni,
- IEEE80211_ACTION_CAT_MESHPEERING,
+ IEEE80211_ACTION_CAT_SELF_PROT,
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);
- }
+ args[0] = ni->ni_mlpid;
+ args[2] = IEEE80211_REASON_MESH_CONFIRM_TIMEOUT;
+ ieee80211_send_action(ni,
+ IEEE80211_ACTION_CAT_SELF_PROT,
+ IEEE80211_ACTION_MESHPEERING_CLOSE, args);
+ mesh_linkchange(ni, IEEE80211_NODE_MESH_HOLDING);
+ mesh_peer_timeout_setup(ni);
break;
case IEEE80211_NODE_MESH_HOLDING:
+ ni->ni_mlhcnt++;
+ if (ni->ni_mlhcnt >= ieee80211_mesh_maxholding)
+ callout_reset(&ni->ni_mlhtimer,
+ ieee80211_mesh_backofftimeout,
+ mesh_peer_backoff_cb, ni);
mesh_linkchange(ni, IEEE80211_NODE_MESH_IDLE);
break;
}
@@ -2292,7 +3102,6 @@ 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;
- uint16_t cap;
if (meshconf == NULL)
return 1;
@@ -2326,10 +3135,8 @@ mesh_verify_meshconf(struct ieee80211vap *vap, const uint8_t *ie)
meshconf->conf_pselid);
return 1;
}
- /* NB: conf_cap is only read correctly here */
- cap = LE_READ_2(&meshconf->conf_cap);
/* Not accepting peers */
- if (!(cap & IEEE80211_MESHCONF_CAP_AP)) {
+ 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;
@@ -2344,22 +3151,31 @@ mesh_verify_meshpeer(struct ieee80211vap *vap, uint8_t subtype,
const struct ieee80211_meshpeer_ie *meshpeer =
(const struct ieee80211_meshpeer_ie *) ie;
- if (meshpeer == NULL || meshpeer->peer_len < 6 ||
- meshpeer->peer_len > 10)
+ if (meshpeer == NULL ||
+ meshpeer->peer_len < IEEE80211_MPM_BASE_SZ ||
+ meshpeer->peer_len > IEEE80211_MPM_MAX_SZ)
return 1;
+ if (meshpeer->peer_proto != IEEE80211_MPPID_MPM) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH,
+ "Only MPM protocol is supported (proto: 0x%02X)",
+ meshpeer->peer_proto);
+ return 1;
+ }
switch (subtype) {
- case IEEE80211_MESH_PEER_LINK_OPEN:
- if (meshpeer->peer_len != 6)
+ case IEEE80211_ACTION_MESHPEERING_OPEN:
+ if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ)
return 1;
break;
- case IEEE80211_MESH_PEER_LINK_CONFIRM:
- if (meshpeer->peer_len != 8)
+ case IEEE80211_ACTION_MESHPEERING_CONFIRM:
+ if (meshpeer->peer_len != IEEE80211_MPM_BASE_SZ + 2)
return 1;
break;
- case IEEE80211_MESH_PEER_LINK_CLOSE:
- if (meshpeer->peer_len < 8)
+ case IEEE80211_ACTION_MESHPEERING_CLOSE:
+ if (meshpeer->peer_len < IEEE80211_MPM_BASE_SZ + 2)
return 1;
- if (meshpeer->peer_len == 8 && meshpeer->peer_linkid != 0)
+ if (meshpeer->peer_len == (IEEE80211_MPM_BASE_SZ + 2) &&
+ meshpeer->peer_linkid != 0)
return 1;
if (meshpeer->peer_rcode == 0)
return 1;
@@ -2398,23 +3214,24 @@ ieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap)
KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap"));
*frm++ = IEEE80211_ELEMID_MESHCONF;
- *frm++ = sizeof(struct ieee80211_meshconf_ie) - 2;
+ *frm++ = IEEE80211_MESH_CONF_SZ;
*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 = (ms->ms_neighbors > IEEE80211_MESH_MAX_NEIGHBORS ?
+ IEEE80211_MESH_MAX_NEIGHBORS : ms->ms_neighbors) << 1;
+ if (ms->ms_flags & IEEE80211_MESHFLAGS_GATE)
+ *frm |= IEEE80211_MESHCONF_FORM_GATE;
frm += 1;
caps = 0;
if (ms->ms_flags & IEEE80211_MESHFLAGS_AP)
caps |= IEEE80211_MESHCONF_CAP_AP;
if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)
caps |= IEEE80211_MESHCONF_CAP_FWRD;
- ADDSHORT(frm, caps);
+ *frm++ = caps;
return frm;
}
@@ -2425,34 +3242,29 @@ 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 */
+ case IEEE80211_ACTION_MESHPEERING_OPEN:
+ *frm++ = IEEE80211_MPM_BASE_SZ; /* length */
+ ADDSHORT(frm, IEEE80211_MPPID_MPM); /* proto */
+ ADDSHORT(frm, localid); /* local ID */
break;
- case IEEE80211_MESH_PEER_LINK_CONFIRM:
+ case IEEE80211_ACTION_MESHPEERING_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 */
+ *frm++ = IEEE80211_MPM_BASE_SZ + 2; /* length */
+ ADDSHORT(frm, IEEE80211_MPPID_MPM); /* proto */
+ ADDSHORT(frm, localid); /* local ID */
+ ADDSHORT(frm, peerid); /* peer ID */
break;
- case IEEE80211_MESH_PEER_LINK_CLOSE:
+ case IEEE80211_ACTION_MESHPEERING_CLOSE:
if (peerid)
- *frm++ = 10; /* length */
+ *frm++ = IEEE80211_MPM_MAX_SZ; /* length */
else
- *frm++ = 8; /* length */
- memcpy(frm, meshpeerproto, 4);
- frm += 4;
+ *frm++ = IEEE80211_MPM_BASE_SZ + 2; /* length */
+ ADDSHORT(frm, IEEE80211_MPPID_MPM); /* proto */
ADDSHORT(frm, localid); /* local ID */
if (peerid)
ADDSHORT(frm, peerid); /* peer ID */
@@ -2472,7 +3284,7 @@ ieee80211_add_meshpeer(uint8_t *frm, uint8_t subtype, uint16_t localid,
*/
#define IEEE80211_MESH_MAXOVERHEAD \
(sizeof(struct ieee80211_qosframe_addr4) \
- + sizeof(struct ieee80211_meshcntl_ae11) \
+ + sizeof(struct ieee80211_meshcntl_ae10) \
+ sizeof(struct llc) \
+ IEEE80211_ADDR_LEN \
+ IEEE80211_WEP_IVLEN \
@@ -2497,8 +3309,9 @@ mesh_airtime_calc(struct ieee80211_node *ni)
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;
+ errrate = (((ifp->if_get_counter(ifp, IFCOUNTER_OERRORS) +
+ ifp->if_get_counter(ifp, IFCOUNTER_IERRORS)) / 100) << M_BITS)
+ / 100;
res = (overhead + (nbits / rate)) *
((1 << S_FACTOR) / ((1 << M_BITS) - errrate));
@@ -2511,13 +3324,32 @@ mesh_airtime_calc(struct ieee80211_node *ni)
* Add a Mesh Link Metric report IE to a frame.
*/
uint8_t *
-ieee80211_add_meshlmetric(uint8_t *frm, uint32_t metric)
+ieee80211_add_meshlmetric(uint8_t *frm, uint8_t flags, uint32_t metric)
{
*frm++ = IEEE80211_ELEMID_MESHLINK;
- *frm++ = 4;
+ *frm++ = 5;
+ *frm++ = flags;
ADDWORD(frm, metric);
return frm;
}
+
+/*
+ * Add a Mesh Gate Announcement IE to a frame.
+ */
+uint8_t *
+ieee80211_add_meshgate(uint8_t *frm, struct ieee80211_meshgann_ie *ie)
+{
+ *frm++ = IEEE80211_ELEMID_MESHGANN; /* ie */
+ *frm++ = IEEE80211_MESHGANN_BASE_SZ; /* len */
+ *frm++ = ie->gann_flags;
+ *frm++ = ie->gann_hopcount;
+ *frm++ = ie->gann_ttl;
+ IEEE80211_ADDR_COPY(frm, ie->gann_addr);
+ frm += 6;
+ ADDWORD(frm, ie->gann_seq);
+ ADDSHORT(frm, ie->gann_interval);
+ return frm;
+}
#undef ADDSHORT
#undef ADDWORD
@@ -2528,7 +3360,8 @@ 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);
+ callout_init(&ni->ni_mltimer, 1);
+ callout_init(&ni->ni_mlhtimer, 1);
}
/*
@@ -2541,6 +3374,7 @@ ieee80211_mesh_node_cleanup(struct ieee80211_node *ni)
struct ieee80211_mesh_state *ms = vap->iv_mesh;
callout_drain(&ni->ni_mltimer);
+ callout_drain(&ni->ni_mlhtimer);
/* NB: short-circuit callbacks after mesh_vdetach */
if (vap->iv_mesh != NULL)
ms->ms_ppath->mpp_peerdown(ni);
@@ -2603,6 +3437,9 @@ mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
case IEEE80211_IOC_MESH_FWRD:
ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) != 0;
break;
+ case IEEE80211_IOC_MESH_GATE:
+ ireq->i_val = (ms->ms_flags & IEEE80211_MESHFLAGS_GATE) != 0;
+ break;
case IEEE80211_IOC_MESH_TTL:
ireq->i_val = ms->ms_ttl;
break;
@@ -2621,7 +3458,8 @@ mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
}
ireq->i_len = len;
/* XXX M_WAIT? */
- p = malloc(len, M_TEMP, M_NOWAIT | M_ZERO);
+ p = IEEE80211_MALLOC(len, M_TEMP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (p == NULL)
return ENOMEM;
off = 0;
@@ -2631,21 +3469,22 @@ mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
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_lifetime =
+ ieee80211_mesh_rt_update(rt, 0);
imr->imr_lastmseq = rt->rt_lastmseq;
+ imr->imr_flags = rt->rt_flags; /* last */
off += sizeof(*imr);
}
MESH_RT_UNLOCK(ms);
error = copyout(p, (uint8_t *)ireq->i_data,
ireq->i_len);
- free(p, M_TEMP);
+ IEEE80211_FREE(p, M_TEMP);
break;
case IEEE80211_MESH_RTCMD_FLUSH:
case IEEE80211_MESH_RTCMD_ADD:
@@ -2716,6 +3555,13 @@ mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
ms->ms_flags |= IEEE80211_MESHFLAGS_FWD;
else
ms->ms_flags &= ~IEEE80211_MESHFLAGS_FWD;
+ mesh_gatemode_setup(vap);
+ break;
+ case IEEE80211_IOC_MESH_GATE:
+ if (ireq->i_val)
+ ms->ms_flags |= IEEE80211_MESHFLAGS_GATE;
+ else
+ ms->ms_flags &= ~IEEE80211_MESHFLAGS_GATE;
break;
case IEEE80211_IOC_MESH_TTL:
ms->ms_ttl = (uint8_t) ireq->i_val;
diff --git a/freebsd/sys/net80211/ieee80211_mesh.h b/freebsd/sys/net80211/ieee80211_mesh.h
index ad1b02af..2253af00 100644
--- a/freebsd/sys/net80211/ieee80211_mesh.h
+++ b/freebsd/sys/net80211/ieee80211_mesh.h
@@ -32,6 +32,7 @@
#define _NET80211_IEEE80211_MESH_H_
#define IEEE80211_MESH_DEFAULT_TTL 31
+#define IEEE80211_MESH_MAX_NEIGHBORS 15
/*
* NB: all structures are __packed so sizeof works on arm, et. al.
@@ -40,6 +41,7 @@
* 802.11s Information Elements.
*/
/* Mesh Configuration */
+#define IEEE80211_MESH_CONF_SZ (7)
struct ieee80211_meshconf_ie {
uint8_t conf_ie; /* IEEE80211_ELEMID_MESHCONF */
uint8_t conf_len;
@@ -49,31 +51,66 @@ struct ieee80211_meshconf_ie {
uint8_t conf_syncid; /* Sync. Protocol ID */
uint8_t conf_authid; /* Auth. Protocol ID */
uint8_t conf_form; /* Formation Information */
- uint16_t conf_cap;
+ uint8_t conf_cap;
} __packed;
/* Hybrid Wireless Mesh Protocol */
-#define IEEE80211_MESHCONF_PATH_HWMP 0x00
+enum {
+ /* 0 reserved */
+ IEEE80211_MESHCONF_PATH_HWMP = 1,
+ /* 2-254 reserved */
+ IEEE80211_MESHCONF_PATH_VENDOR = 255,
+};
+
/* Airtime Link Metric */
-#define IEEE80211_MESHCONF_METRIC_AIRTIME 0x00
+enum {
+ /* 0 reserved */
+ IEEE80211_MESHCONF_METRIC_AIRTIME = 1,
+ /* 2-254 reserved */
+ IEEE80211_MESHCONF_METRIC_VENDOR = 255,
+};
+
/* Congestion Control */
-#define IEEE80211_MESHCONF_CC_DISABLED 0x00
-#define IEEE80211_MESHCONF_CC_SIG 0x01
+enum {
+ IEEE80211_MESHCONF_CC_DISABLED = 0,
+ IEEE80211_MESHCONF_CC_SIG = 1,
+ /* 2-254 reserved */
+ IEEE80211_MESHCONF_CC_VENDOR = 255,
+};
+
/* 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 */
+enum {
+ /* 0 reserved */
+ IEEE80211_MESHCONF_SYNC_NEIGHOFF = 1,
+ /* 2-254 rserved */
+ IEEE80211_MESHCONF_SYNC_VENDOR = 255,
+};
+
+/* Authentication Protocol Identifier */
+enum {
+
+ IEEE80211_MESHCONF_AUTH_DISABLED = 0,
+ /* Simultaneous Authenticaction of Equals */
+ IEEE80211_MESHCONF_AUTH_SEA = 1,
+ IEEE80211_MESHCONF_AUTH_8021X = 2, /* IEEE 802.1X */
+ /* 3-254 reserved */
+ IEEE80211_MESHCONF_AUTH_VENDOR = 255,
+};
+
+/* Mesh Formation Info */
+#define IEEE80211_MESHCONF_FORM_GATE 0x01 /* Connected to Gate */
+#define IEEE80211_MESHCONF_FORM_NNEIGH_MASK 0x7E /* Number of Neighbours */
+#define IEEE80211_MESHCONF_FORM_SA 0xF0 /* indicating 802.1X auth */
+
+/* Mesh Capability */
#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_TBTT 0x40 /* TBTT Adjusting */
-#define IEEE80211_MESHCONF_CAP_PSL 0x80 /* Power Save Level */
+#define IEEE80211_MESHCONF_CAP_TBTT 0x20 /* TBTT Adjusting */
+#define IEEE80211_MESHCONF_CAP_PSL 0x40 /* Power Save Level */
+/* 0x80 reserved */
/* Mesh Identifier */
struct ieee80211_meshid_ie {
@@ -83,8 +120,14 @@ struct ieee80211_meshid_ie {
/* Link Metric Report */
struct ieee80211_meshlmetric_ie {
- uint8_t lm_ie; /* IEEE80211_ELEMID_MESHLINK */
+ uint8_t lm_ie; /* IEEE80211_ACTION_MESH_LMETRIC */
uint8_t lm_len;
+ uint8_t lm_flags;
+#define IEEE80211_MESH_LMETRIC_FLAGS_REQ 0x01 /* Request */
+ /*
+ * XXX: this field should be variable in size and depend on
+ * the active active path selection metric identifier
+ */
uint32_t lm_metric;
#define IEEE80211_MESHLMETRIC_INITIALVAL 0
} __packed;
@@ -98,32 +141,24 @@ struct ieee80211_meshcngst_ie {
} __packed;
/* Peer Link Management */
+#define IEEE80211_MPM_BASE_SZ (4)
+#define IEEE80211_MPM_MAX_SZ (8)
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_proto; /* Peer Management Protocol */
uint16_t peer_llinkid; /* Local Link ID */
uint16_t peer_linkid; /* Peer Link ID */
uint16_t peer_rcode;
} __packed;
+/* Mesh Peering Protocol Identifier field value */
enum {
- IEEE80211_MESH_PEER_LINK_OPEN = 0,
- IEEE80211_MESH_PEER_LINK_CONFIRM = 1,
- IEEE80211_MESH_PEER_LINK_CLOSE = 2,
- /* values 3-255 are reserved */
+ IEEE80211_MPPID_MPM = 0, /* Mesh peering management */
+ IEEE80211_MPPID_AUTH_MPM = 1, /* Auth. mesh peering exchange */
+ /* 2-65535 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 {
@@ -158,37 +193,50 @@ struct ieee80211_meshbeacont_ie {
} __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 */
+/* Gate (GANN) Annoucement */
+/*
+ * NB: these macros used for the length in the IEs does not include 2 bytes
+ * for _ie and _len fields as is defined by the standard.
+ */
+#define IEEE80211_MESHGANN_BASE_SZ (15)
+struct ieee80211_meshgann_ie {
+ uint8_t gann_ie; /* IEEE80211_ELEMID_MESHGANN */
+ uint8_t gann_len;
+ uint8_t gann_flags;
+ uint8_t gann_hopcount;
+ uint8_t gann_ttl;
+ uint8_t gann_addr[IEEE80211_ADDR_LEN];
+ uint32_t gann_seq; /* GANN Sequence Number */
+ uint16_t gann_interval; /* GANN Interval */
} __packed;
/* Root (MP) Annoucement */
+#define IEEE80211_MESHRANN_BASE_SZ (21)
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 */
+#define IEEE80211_MESHRANN_FLAGS_GATE 0x01 /* Mesh Gate */
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_interval;
uint32_t rann_metric;
} __packed;
/* Mesh Path Request */
+#define IEEE80211_MESHPREQ_BASE_SZ (26)
+#define IEEE80211_MESHPREQ_BASE_SZ_AE (32)
+#define IEEE80211_MESHPREQ_TRGT_SZ (11)
+#define IEEE80211_MESHPREQ_TCNT_OFFSET (27)
+#define IEEE80211_MESHPREQ_TCNT_OFFSET_AE (33)
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_GATE 0x01 /* Mesh Gate */
+#define IEEE80211_MESHPREQ_FLAGS_AM 0x02 /* 0 = bcast / 1 = ucast */
#define IEEE80211_MESHPREQ_FLAGS_PP 0x04 /* Proactive PREP */
#define IEEE80211_MESHPREQ_FLAGS_AE 0x40 /* Address Extension */
uint8_t preq_hopcount;
@@ -196,14 +244,14 @@ struct ieee80211_meshpreq_ie {
uint32_t preq_id;
uint8_t preq_origaddr[IEEE80211_ADDR_LEN];
uint32_t preq_origseq; /* HWMP Sequence Number */
- /* NB: may have Originator Proxied Address */
+ /* NB: may have Originator External Address */
+ uint8_t preq_orig_ext_addr[IEEE80211_ADDR_LEN];
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 */
@@ -211,15 +259,19 @@ struct ieee80211_meshpreq_ie {
} __packed;
/* Mesh Path Reply */
+#define IEEE80211_MESHPREP_BASE_SZ (31)
+#define IEEE80211_MESHPREP_BASE_SZ_AE (37)
struct ieee80211_meshprep_ie {
uint8_t prep_ie; /* IEEE80211_ELEMID_MESHPREP */
uint8_t prep_len;
uint8_t prep_flags;
+#define IEEE80211_MESHPREP_FLAGS_AE 0x40 /* Address Extension */
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 */
+ /* NB: May have Target External Address */
+ uint8_t prep_target_ext_addr[IEEE80211_ADDR_LEN];
uint32_t prep_lifetime;
uint32_t prep_metric;
uint8_t prep_origaddr[IEEE80211_ADDR_LEN];
@@ -227,6 +279,11 @@ struct ieee80211_meshprep_ie {
} __packed;
/* Mesh Path Error */
+#define IEEE80211_MESHPERR_MAXDEST (19)
+#define IEEE80211_MESHPERR_NDEST_OFFSET (3)
+#define IEEE80211_MESHPERR_BASE_SZ (2)
+#define IEEE80211_MESHPERR_DEST_SZ (13)
+#define IEEE80211_MESHPERR_DEST_SZ_AE (19)
struct ieee80211_meshperr_ie {
uint8_t perr_ie; /* IEEE80211_ELEMID_MESHPERR */
uint8_t perr_len;
@@ -234,10 +291,13 @@ struct ieee80211_meshperr_ie {
uint8_t perr_ndests; /* Number of Destinations */
struct {
uint8_t dest_flags;
-#define IEEE80211_MESHPERR_DFLAGS_USN 0x01
-#define IEEE80211_MESHPERR_DFLAGS_RC 0x02
+#define IEEE80211_MESHPERR_DFLAGS_USN 0x01 /* XXX: not part of standard */
+#define IEEE80211_MESHPERR_DFLAGS_RC 0x02 /* XXX: not part of standard */
+#define IEEE80211_MESHPERR_FLAGS_AE 0x40 /* Address Extension */
uint8_t dest_addr[IEEE80211_ADDR_LEN];
uint32_t dest_seq; /* HWMP Sequence Number */
+ /* NB: May have Destination External Address */
+ uint8_t dest_ext_addr[IEEE80211_ADDR_LEN];
uint16_t dest_rcode;
} __packed perr_dests[1]; /* NB: variable size */
} __packed;
@@ -269,10 +329,9 @@ struct ieee80211_meshpuc_ie {
/*
* 802.11s Action Frames
+ * XXX: these are wrong, and some of them should be
+ * under MESH category while PROXY is under MULTIHOP category.
*/
-#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
@@ -281,35 +340,29 @@ struct ieee80211_meshpuc_ie {
* Mesh Peering Action codes.
*/
enum {
- IEEE80211_ACTION_MESHPEERING_OPEN = 0,
- IEEE80211_ACTION_MESHPEERING_CONFIRM = 1,
- IEEE80211_ACTION_MESHPEERING_CLOSE = 2,
- /* 3-255 reserved */
+ /* 0 reserved */
+ IEEE80211_ACTION_MESHPEERING_OPEN = 1,
+ IEEE80211_ACTION_MESHPEERING_CONFIRM = 2,
+ IEEE80211_ACTION_MESHPEERING_CLOSE = 3,
+ /* 4-255 reserved */
};
/*
- * Mesh Path Selection Action code.
+ * Mesh 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 */
+ IEEE80211_ACTION_MESH_LMETRIC = 0, /* Mesh Link Metric Report */
+ IEEE80211_ACTION_MESH_HWMP = 1, /* HWMP Mesh Path Selection */
+ IEEE80211_ACTION_MESH_GANN = 2, /* Gate Announcement */
+ IEEE80211_ACTION_MESH_CC = 3, /* Congestion Control */
+ IEEE80211_ACTION_MESH_MCCA_SREQ = 4, /* MCCA Setup Request */
+ IEEE80211_ACTION_MESH_MCCA_SREP = 5, /* MCCA Setup Reply */
+ IEEE80211_ACTION_MESH_MCCA_AREQ = 6, /* MCCA Advertisement Req. */
+ IEEE80211_ACTION_MESH_MCCA_ADVER =7, /* MCCA Advertisement */
+ IEEE80211_ACTION_MESH_MCCA_TRDOWN = 8, /* MCCA Teardown */
+ IEEE80211_ACTION_MESH_TBTT_REQ = 9, /* TBTT Adjustment Request */
+ IEEE80211_ACTION_MESH_TBTT_RES = 10, /* TBTT Adjustment Response */
+ /* 11-255 reserved */
};
/*
@@ -334,37 +387,67 @@ 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;
+#define IEEE80211_MESH_AE_MASK 0x03
+enum {
+ IEEE80211_MESH_AE_00 = 0, /* MC has no AE subfield */
+ IEEE80211_MESH_AE_01 = 1, /* MC contain addr4 */
+ IEEE80211_MESH_AE_10 = 2, /* MC contain addr5 & addr6 */
+ IEEE80211_MESH_AE_11 = 3, /* RESERVED */
+};
+
#ifdef _KERNEL
+MALLOC_DECLARE(M_80211_MESH_PREQ);
+MALLOC_DECLARE(M_80211_MESH_PREP);
+MALLOC_DECLARE(M_80211_MESH_PERR);
+
MALLOC_DECLARE(M_80211_MESH_RT);
+MALLOC_DECLARE(M_80211_MESH_GT_RT);
+/*
+ * Basic forwarding information:
+ * o Destination MAC
+ * o Next-hop MAC
+ * o Precursor list (not implemented yet)
+ * o Path timeout
+ * The rest is part of the active Mesh path selection protocol.
+ * XXX: to be moved out later.
+ */
struct ieee80211_mesh_route {
TAILQ_ENTRY(ieee80211_mesh_route) rt_next;
- int rt_crtime; /* creation time */
+ struct ieee80211vap *rt_vap;
+ ieee80211_rte_lock_t rt_lock; /* fine grained route lock */
+ struct callout rt_discovery; /* discovery timeout */
+ int rt_updtime; /* last update time */
uint8_t rt_dest[IEEE80211_ADDR_LEN];
+ uint8_t rt_mesh_gate[IEEE80211_ADDR_LEN]; /* meshDA */
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;
+#define IEEE80211_MESHRT_FLAGS_DISCOVER 0x01 /* path discovery */
+#define IEEE80211_MESHRT_FLAGS_VALID 0x02 /* path discovery complete */
+#define IEEE80211_MESHRT_FLAGS_PROXY 0x04 /* proxy entry */
+#define IEEE80211_MESHRT_FLAGS_GATE 0x08 /* mesh gate entry */
+ uint32_t rt_lifetime; /* route timeout */
uint32_t rt_lastmseq; /* last seq# seen dest */
+ uint32_t rt_ext_seq; /* proxy seq number */
void *rt_priv; /* private data */
};
#define IEEE80211_MESH_ROUTE_PRIV(rt, cast) ((cast *)rt->rt_priv)
+/*
+ * Stored information about known mesh gates.
+ */
+struct ieee80211_mesh_gate_route {
+ TAILQ_ENTRY(ieee80211_mesh_gate_route) gr_next;
+ uint8_t gr_addr[IEEE80211_ADDR_LEN];
+ uint32_t gr_lastseq;
+ struct ieee80211_mesh_route *gr_route;
+};
+
#define IEEE80211_MESH_PROTO_DSZ 12 /* description size */
/*
* Mesh Path Selection Protocol.
@@ -379,6 +462,9 @@ struct ieee80211_mesh_proto_path {
const uint8_t [IEEE80211_ADDR_LEN],
struct mbuf *);
void (*mpp_peerdown)(struct ieee80211_node *);
+ void (*mpp_senderror)(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ struct ieee80211_mesh_route *, int);
void (*mpp_vattach)(struct ieee80211vap *);
void (*mpp_vdetach)(struct ieee80211vap *);
int (*mpp_newstate)(struct ieee80211vap *,
@@ -425,11 +511,15 @@ struct ieee80211_mesh_state {
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_GATE 0x02 /* mesh gate role */
#define IEEE80211_MESHFLAGS_FWD 0x04 /* forward packets */
+#define IEEE80211_MESHFLAGS_ROOT 0x08 /* configured as root */
uint8_t ms_flags;
- struct mtx ms_rt_lock;
+ ieee80211_rt_lock_t ms_rt_lock;
struct callout ms_cleantimer;
+ struct callout ms_gatetimer;
+ ieee80211_mesh_seq ms_gateseq;
+ TAILQ_HEAD(, ieee80211_mesh_gate_route) ms_known_gates;
TAILQ_HEAD(, ieee80211_mesh_route) ms_routes;
struct ieee80211_mesh_proto_metric *ms_pmetric;
struct ieee80211_mesh_proto_path *ms_ppath;
@@ -448,6 +538,7 @@ void ieee80211_mesh_rt_del(struct ieee80211vap *,
void ieee80211_mesh_rt_flush(struct ieee80211vap *);
void ieee80211_mesh_rt_flush_peer(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN]);
+int ieee80211_mesh_rt_update(struct ieee80211_mesh_route *rt, int);
void ieee80211_mesh_proxy_check(struct ieee80211vap *,
const uint8_t [IEEE80211_ADDR_LEN]);
@@ -460,7 +551,9 @@ 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);
+uint8_t * ieee80211_add_meshlmetric(uint8_t *, uint8_t, uint32_t);
+uint8_t * ieee80211_add_meshgate(uint8_t *,
+ struct ieee80211_meshgann_ie *);
void ieee80211_mesh_node_init(struct ieee80211vap *,
struct ieee80211_node *);
@@ -473,6 +566,14 @@ void ieee80211_mesh_init_neighbor(struct ieee80211_node *,
const struct ieee80211_scanparams *);
void ieee80211_mesh_update_beacon(struct ieee80211vap *,
struct ieee80211_beacon_offsets *);
+struct ieee80211_mesh_gate_route *
+ ieee80211_mesh_mark_gate(struct ieee80211vap *,
+ const uint8_t *, struct ieee80211_mesh_route *);
+void ieee80211_mesh_forward_to_gates(struct ieee80211vap *,
+ struct ieee80211_mesh_route *);
+struct ieee80211_node *
+ ieee80211_mesh_find_txnode(struct ieee80211vap *,
+ const uint8_t [IEEE80211_ADDR_LEN]);
/*
* Return non-zero if proxy operation is enabled.
@@ -482,7 +583,7 @@ 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;
+ (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_GATE)) != 0;
}
/*
diff --git a/freebsd/sys/net80211/ieee80211_monitor.c b/freebsd/sys/net80211/ieee80211_monitor.c
index c30d0e27..ec2086eb 100644
--- a/freebsd/sys/net80211/ieee80211_monitor.c
+++ b/freebsd/sys/net80211/ieee80211_monitor.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/if_llc.h>
#include <net/ethernet.h>
@@ -62,7 +63,7 @@ __FBSDID("$FreeBSD$");
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);
+ const struct ieee80211_rx_stats *rxs, int rssi, int nf);
void
ieee80211_monitor_attach(struct ieee80211com *ic)
@@ -126,12 +127,13 @@ monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* Process a received frame in monitor mode.
*/
static int
-monitor_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
+monitor_input(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ifnet *ifp = vap->iv_ifp;
- ifp->if_ipackets++;
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
if (ieee80211_radiotap_active_vap(vap))
ieee80211_radiotap_rx(vap, m);
diff --git a/freebsd/sys/net80211/ieee80211_node.c b/freebsd/sys/net80211/ieee80211_node.c
index 5bf33549..c9c6df96 100644
--- a/freebsd/sys/net80211/ieee80211_node.c
+++ b/freebsd/sys/net80211/ieee80211_node.c
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
@@ -74,12 +75,6 @@ CTASSERT((IEEE80211_NODE_HASHSIZE & (IEEE80211_NODE_HASHSIZE-1)) == 0);
#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 *,
@@ -94,6 +89,8 @@ static void node_getmimoinfo(const struct ieee80211_node *,
static void _ieee80211_free_node(struct ieee80211_node *);
+static void node_reclaim(struct ieee80211_node_table *nt,
+ struct ieee80211_node *ni);
static void ieee80211_node_table_init(struct ieee80211com *ic,
struct ieee80211_node_table *nt, const char *name,
int inact, int keymaxix);
@@ -113,7 +110,7 @@ ieee80211_node_attach(struct ieee80211com *ic)
"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_init(&ic->ic_inact, 1);
callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
ieee80211_node_timeout, ic);
@@ -171,9 +168,10 @@ ieee80211_node_latevattach(struct ieee80211vap *vap)
"WARNING: max aid too small, changed to %d\n",
vap->iv_max_aid);
}
- vap->iv_aid_bitmap = (uint32_t *) malloc(
+ vap->iv_aid_bitmap = (uint32_t *) IEEE80211_MALLOC(
howmany(vap->iv_max_aid, 32) * sizeof(uint32_t),
- M_80211_NODE, M_NOWAIT | M_ZERO);
+ M_80211_NODE,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (vap->iv_aid_bitmap == NULL) {
/* XXX no way to recover */
printf("%s: no memory for AID bitmap, max aid %d!\n",
@@ -198,7 +196,7 @@ ieee80211_node_vdetach(struct ieee80211vap *vap)
vap->iv_bss = NULL;
}
if (vap->iv_aid_bitmap != NULL) {
- free(vap->iv_aid_bitmap, M_80211_NODE);
+ IEEE80211_FREE(vap->iv_aid_bitmap, M_80211_NODE);
vap->iv_aid_bitmap = NULL;
}
}
@@ -328,9 +326,11 @@ ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan)
struct ieee80211_node *ni;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: creating %s on channel %u\n", __func__,
+ "%s: creating %s on channel %u%c flags 0x%08x\n", __func__,
ieee80211_opmode_name[vap->iv_opmode],
- ieee80211_chan2ieee(ic, chan));
+ ieee80211_chan2ieee(ic, chan),
+ ieee80211_channel_type_char(chan),
+ chan->ic_flags);
ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr);
if (ni == NULL) {
@@ -411,6 +411,14 @@ ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan)
}
}
+ /* XXX TODO: other bits and pieces - eg fast-frames? */
+
+ /* If we're an 11n channel then initialise the 11n bits */
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
+ /* XXX what else? */
+ ieee80211_ht_node_init(ni);
+ }
+
(void) ieee80211_sta_join1(ieee80211_ref_node(ni));
}
@@ -554,31 +562,18 @@ check_bss_debug(struct ieee80211vap *vap, struct ieee80211_node *ni)
}
#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)
+ieee80211_ibss_merge_check(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,
@@ -590,6 +585,89 @@ ieee80211_ibss_merge(struct ieee80211_node *ni)
vap->iv_stats.is_ibss_capmismatch++;
return 0;
}
+
+ return 1;
+}
+
+/*
+ * Check if the given node should populate the node table.
+ *
+ * We need to be in "see all beacons for all ssids" mode in order
+ * to do IBSS merges, however this means we will populate nodes for
+ * /all/ IBSS SSIDs, versus just the one we care about.
+ *
+ * So this check ensures the node can actually belong to our IBSS
+ * configuration. For now it simply checks the SSID.
+ */
+int
+ieee80211_ibss_node_check_new(struct ieee80211_node *ni,
+ const struct ieee80211_scanparams *scan)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ int i;
+
+ /*
+ * If we have no SSID and no scan SSID, return OK.
+ */
+ if (vap->iv_des_nssid == 0 && scan->ssid == NULL)
+ goto ok;
+
+ /*
+ * If we have one of (SSID, scan SSID) then return error.
+ */
+ if (!! (vap->iv_des_nssid == 0) != !! (scan->ssid == NULL))
+ goto mismatch;
+
+ /*
+ * Double-check - we need scan SSID.
+ */
+ if (scan->ssid == NULL)
+ goto mismatch;
+
+ /*
+ * Check if the scan SSID matches the SSID list for the VAP.
+ */
+ for (i = 0; i < vap->iv_des_nssid; i++) {
+
+ /* Sanity length check */
+ if (vap->iv_des_ssid[i].len != scan->ssid[1])
+ continue;
+
+ /* Note: SSID in the scan entry is the IE format */
+ if (memcmp(vap->iv_des_ssid[i].ssid, scan->ssid + 2,
+ vap->iv_des_ssid[i].len) == 0)
+ goto ok;
+ }
+
+mismatch:
+ return (0);
+ok:
+ return (1);
+}
+
+/*
+ * 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)
+{
+#ifdef IEEE80211_DEBUG
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+#endif
+
+ if (! ieee80211_ibss_merge_check(ni))
+ 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),
@@ -687,6 +765,14 @@ ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
ieee80211_runtask(ic, &ic->ic_chan_task);
}
+void
+ieee80211_update_chw(struct ieee80211com *ic)
+{
+
+ ieee80211_setupcurchan(ic, ic->ic_curchan);
+ ieee80211_runtask(ic, &ic->ic_chw_task);
+}
+
/*
* Join the specified IBSS/BSS network. The node is assumed to
* be passed in with a held reference.
@@ -712,9 +798,15 @@ ieee80211_sta_join1(struct ieee80211_node *selbs)
IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr));
vap->iv_bss = selbs; /* NB: caller assumed to bump refcnt */
if (obss != NULL) {
+ struct ieee80211_node_table *nt = obss->ni_table;
+
copy_bss(selbs, obss);
ieee80211_node_decref(obss); /* iv_bss reference */
- ieee80211_free_node(obss); /* station table reference */
+
+ IEEE80211_NODE_LOCK(nt);
+ node_reclaim(nt, obss); /* station table reference */
+ IEEE80211_NODE_UNLOCK(nt);
+
obss = NULL; /* NB: guard against later use */
}
@@ -765,6 +857,7 @@ ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan,
/* XXX msg */
return 0;
}
+
/*
* Expand scan state into node's format.
* XXX may not need all this stuff
@@ -805,6 +898,10 @@ ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan,
if (ni->ni_ies.tdma_ie != NULL)
ieee80211_parse_tdma(ni, ni->ni_ies.tdma_ie);
#endif
+
+ /* XXX parse VHT IEs */
+ /* XXX parse BSSLOAD IE */
+ /* XXX parse APCHANREP IE */
}
vap->iv_dtim_period = se->se_dtimperiod;
@@ -815,6 +912,29 @@ ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan,
IEEE80211_F_DOSORT);
if (ieee80211_iserp_rateset(&ni->ni_rates))
ni->ni_flags |= IEEE80211_NODE_ERP;
+
+ /*
+ * Setup HT state for this node if it's available, otherwise
+ * non-STA modes won't pick this state up.
+ *
+ * For IBSS and related modes that don't go through an
+ * association request/response, the only appropriate place
+ * to setup the HT state is here.
+ */
+ if (ni->ni_ies.htinfo_ie != NULL &&
+ ni->ni_ies.htcap_ie != NULL &&
+ vap->iv_flags_ht & IEEE80211_FHT_HT) {
+ ieee80211_ht_node_init(ni);
+ ieee80211_ht_updateparams(ni,
+ ni->ni_ies.htcap_ie,
+ ni->ni_ies.htinfo_ie);
+ ieee80211_setup_htrates(ni, ni->ni_ies.htcap_ie,
+ IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+ ieee80211_setup_basic_htrates(ni, ni->ni_ies.htinfo_ie);
+ }
+ /* XXX else check for ath FF? */
+ /* XXX QoS? Difficult given that WME config is specific to a master */
+
ieee80211_node_setuptxparms(ni);
ieee80211_ratectl_node_init(ni);
@@ -840,7 +960,7 @@ ieee80211_sta_leave(struct ieee80211_node *ni)
void
ieee80211_node_deauth(struct ieee80211_node *ni, int reason)
{
- /* NB: bump the refcnt to be sure temporay nodes are not reclaimed */
+ /* NB: bump the refcnt to be sure temporary nodes are not reclaimed */
ieee80211_ref_node(ni);
if (ni->ni_associd != 0)
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
@@ -853,8 +973,8 @@ 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);
+ ni = (struct ieee80211_node *) IEEE80211_MALLOC(sizeof(struct ieee80211_node),
+ M_80211_NODE, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
return ni;
}
@@ -871,11 +991,12 @@ ieee80211_ies_init(struct ieee80211_ies *ies, const uint8_t *data, int len)
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);
+ IEEE80211_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);
+ ies->data = (uint8_t *) IEEE80211_MALLOC(len, M_80211_NODE_IE,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (ies->data == NULL) {
ies->len = 0;
/* NB: pointers have already been zero'd above */
@@ -894,7 +1015,7 @@ void
ieee80211_ies_cleanup(struct ieee80211_ies *ies)
{
if (ies->data != NULL)
- free(ies->data, M_80211_NODE_IE);
+ IEEE80211_FREE(ies->data, M_80211_NODE_IE);
}
/*
@@ -932,11 +1053,29 @@ ieee80211_ies_expand(struct ieee80211_ies *ies)
case IEEE80211_ELEMID_HTCAP:
ies->htcap_ie = ie;
break;
+ case IEEE80211_ELEMID_HTINFO:
+ ies->htinfo_ie = ie;
+ break;
#ifdef IEEE80211_SUPPORT_MESH
case IEEE80211_ELEMID_MESHID:
ies->meshid_ie = ie;
break;
#endif
+ case IEEE80211_ELEMID_VHT_CAP:
+ ies->vhtcap_ie = ie;
+ break;
+ case IEEE80211_ELEMID_VHT_OPMODE:
+ ies->vhtopmode_ie = ie;
+ break;
+ case IEEE80211_ELEMID_VHT_PWR_ENV:
+ ies->vhtpwrenv_ie = ie;
+ break;
+ case IEEE80211_ELEMID_BSSLOAD:
+ ies->bssload_ie = ie;
+ break;
+ case IEEE80211_ELEMID_APCHANREP:
+ ies->apchanrep_ie = ie;
+ break;
}
ielen -= 2 + ie[1];
ie += 2 + ie[1];
@@ -952,7 +1091,6 @@ ieee80211_ies_expand(struct ieee80211_ies *ies)
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;
@@ -971,8 +1109,8 @@ node_cleanup(struct ieee80211_node *ni)
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);
+ /* Always do FF node cleanup; for A-MSDU */
+ ieee80211_ff_node_cleanup(ni);
#endif
#ifdef IEEE80211_SUPPORT_MESH
/*
@@ -1004,7 +1142,7 @@ node_cleanup(struct ieee80211_node *ni)
ni->ni_associd = 0;
if (ni->ni_challenge != NULL) {
- free(ni->ni_challenge, M_80211_NODE);
+ IEEE80211_FREE(ni->ni_challenge, M_80211_NODE);
ni->ni_challenge = NULL;
}
/*
@@ -1019,7 +1157,7 @@ node_cleanup(struct ieee80211_node *ni)
*
* XXX does this leave us open to inheriting old state?
*/
- for (i = 0; i < N(ni->ni_rxfrag); i++)
+ for (i = 0; i < nitems(ni->ni_rxfrag); i++)
if (ni->ni_rxfrag[i] != NULL) {
m_freem(ni->ni_rxfrag[i]);
ni->ni_rxfrag[i] = NULL;
@@ -1028,7 +1166,6 @@ node_cleanup(struct ieee80211_node *ni)
* Must be careful here to remove any key map entry w/o a LOR.
*/
ieee80211_node_delucastkey(ni);
-#undef N
}
static void
@@ -1040,7 +1177,7 @@ node_free(struct ieee80211_node *ni)
ic->ic_node_cleanup(ni);
ieee80211_ies_cleanup(&ni->ni_ies);
ieee80211_psq_cleanup(&ni->ni_psq);
- free(ni, M_80211_NODE);
+ IEEE80211_FREE(ni, M_80211_NODE);
}
static void
@@ -1048,8 +1185,6 @@ 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.
*/
@@ -1109,13 +1244,44 @@ node_getmimoinfo(const struct ieee80211_node *ni,
/* XXX EVM? */
}
+static void
+ieee80211_add_node_nt(struct ieee80211_node_table *nt,
+ struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = nt->nt_ic;
+ int hash;
+
+ IEEE80211_NODE_LOCK_ASSERT(nt);
+
+ hash = IEEE80211_NODE_HASH(ic, ni->ni_macaddr);
+ (void) ic; /* XXX IEEE80211_NODE_HASH */
+ TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list);
+ LIST_INSERT_HEAD(&nt->nt_hash[hash], ni, ni_hash);
+ nt->nt_count++;
+ ni->ni_table = nt;
+}
+
+static void
+ieee80211_del_node_nt(struct ieee80211_node_table *nt,
+ struct ieee80211_node *ni)
+{
+
+ IEEE80211_NODE_LOCK_ASSERT(nt);
+
+ TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
+ LIST_REMOVE(ni, ni_hash);
+ nt->nt_count--;
+ KASSERT(nt->nt_count >= 0,
+ ("nt_count is negative (%d)!\n", nt->nt_count));
+ ni->ni_table = NULL;
+}
+
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) {
@@ -1128,7 +1294,6 @@ ieee80211_alloc_node(struct ieee80211_node_table *nt,
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;
@@ -1145,9 +1310,7 @@ ieee80211_alloc_node(struct ieee80211_node_table *nt,
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;
+ ieee80211_add_node_nt(nt, ni);
ni->ni_vap = vap;
ni->ni_ic = ic;
IEEE80211_NODE_UNLOCK(nt);
@@ -1398,7 +1561,7 @@ ieee80211_fakeup_adhoc_node(struct ieee80211vap *vap,
{
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE | IEEE80211_MSG_ASSOC,
"%s: mac<%s>\n", __func__, ether_sprintf(macaddr));
ni = ieee80211_dup_bss(vap, macaddr);
if (ni != NULL) {
@@ -1416,6 +1579,9 @@ ieee80211_fakeup_adhoc_node(struct ieee80211vap *vap,
* so we can do interesting things (e.g. use
* WME to disable ACK's).
*/
+ /*
+ * XXX TODO: 11n?
+ */
if (vap->iv_flags & IEEE80211_F_WME)
ni->ni_flags |= IEEE80211_NODE_QOS;
#ifdef IEEE80211_SUPPORT_SUPERG
@@ -1425,8 +1591,44 @@ ieee80211_fakeup_adhoc_node(struct ieee80211vap *vap,
}
ieee80211_node_setuptxparms(ni);
ieee80211_ratectl_node_init(ni);
+
+ /*
+ * XXX TODO: 11n? At least 20MHz, at least A-MPDU RX,
+ * not A-MPDU TX; not 11n rates, etc. We'll cycle
+ * that after we hear that we can indeed do 11n
+ * (either by a beacon frame or by a probe response.)
+ */
+
+ /*
+ * This is the first time we see the node.
+ */
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, 1);
+
+ /*
+ * Kick off a probe request to the given node;
+ * we will then use the probe response to update
+ * 11n/etc configuration state.
+ *
+ * XXX TODO: this isn't guaranteed, and until we get
+ * a probe response, we won't be able to actually
+ * do anything 802.11n related to the node.
+ * So if this does indeed work, maybe we should hold
+ * off on sending responses until we get the probe
+ * response, or just default to some sensible subset
+ * of 802.11n behaviour (eg always allow aggregation
+ * negotiation TO us, but not FROM us, etc) so we
+ * aren't entirely busted.
+ */
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ ieee80211_send_probereq(ni, /* node */
+ vap->iv_myaddr, /* SA */
+ ni->ni_macaddr, /* DA */
+ vap->iv_bss->ni_bssid, /* BSSID */
+ vap->iv_bss->ni_essid,
+ vap->iv_bss->ni_esslen); /* SSID */
+ }
+
/* XXX not right for 802.1x/WPA */
ieee80211_node_authorize(ni);
}
@@ -1438,6 +1640,8 @@ ieee80211_init_neighbor(struct ieee80211_node *ni,
const struct ieee80211_frame *wh,
const struct ieee80211_scanparams *sp)
{
+ int do_ht_setup = 0;
+
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);
@@ -1463,12 +1667,56 @@ ieee80211_init_neighbor(struct ieee80211_node *ni,
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);
+
+ if ((ni->ni_ies.htcap_ie != NULL) &&
+ (ni->ni_ies.htinfo_ie != NULL) &&
+ (ni->ni_vap->iv_flags_ht & IEEE80211_FHT_HT)) {
+ do_ht_setup = 1;
+ }
}
/* 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);
+
+ /*
+ * If the neighbor is HT compatible, flip that on.
+ */
+ if (do_ht_setup) {
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
+ "%s: doing HT setup\n", __func__);
+ ieee80211_ht_node_init(ni);
+ ieee80211_ht_updateparams(ni,
+ ni->ni_ies.htcap_ie,
+ ni->ni_ies.htinfo_ie);
+ ieee80211_setup_htrates(ni,
+ ni->ni_ies.htcap_ie,
+ IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+ ieee80211_setup_basic_htrates(ni,
+ ni->ni_ies.htinfo_ie);
+ ieee80211_node_setuptxparms(ni);
+ ieee80211_ratectl_node_init(ni);
+
+ /* Reassociate; we're now 11n */
+ /*
+ * XXX TODO: this is the wrong thing to do -
+ * we're calling it with isnew=1 so the ath(4)
+ * driver reinitialises the rate tables.
+ * This "mostly" works for ath(4), but it won't
+ * be right for firmware devices which allocate
+ * node states.
+ *
+ * So, do we just create a new node and delete
+ * the old one? Or?
+ */
+ if (ni->ni_ic->ic_newassoc)
+ ni->ni_ic->ic_newassoc(ni, 1);
+ }
}
/*
@@ -1484,7 +1732,7 @@ ieee80211_add_neighbor(struct ieee80211vap *vap,
{
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
"%s: mac<%s>\n", __func__, ether_sprintf(wh->i_addr2));
ni = ieee80211_dup_bss(vap, wh->i_addr2);/* XXX alloc_node? */
if (ni != NULL) {
@@ -1645,6 +1893,10 @@ ieee80211_find_txnode(struct ieee80211vap *vap,
* caller to be consistent with
* ieee80211_find_node_locked.
*/
+ /*
+ * XXX TODO: this doesn't fake up 11n state; we need
+ * to find another way to get it upgraded.
+ */
ni = ieee80211_fakeup_adhoc_node(vap, macaddr);
if (ni != NULL)
(void) ieee80211_ref_node(ni);
@@ -1679,13 +1931,33 @@ _ieee80211_free_node(struct ieee80211_node *ni)
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);
- }
+ if (nt != NULL)
+ ieee80211_del_node_nt(nt, ni);
ni->ni_ic->ic_node_free(ni);
}
+/*
+ * Clear any entry in the unicast key mapping table.
+ */
+static int
+node_clear_keyixmap(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
+{
+ ieee80211_keyix keyix;
+
+ 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);
+ return 1;
+ }
+
+ return 0;
+}
+
void
#ifdef IEEE80211_DEBUG_REFCNT
ieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line)
@@ -1707,24 +1979,9 @@ ieee80211_free_node(struct ieee80211_node *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? */
+ } else if (ieee80211_node_refcnt(ni) == 1)
+ if (node_clear_keyixmap(nt, ni))
_ieee80211_free_node(ni);
- }
- }
IEEE80211_NODE_UNLOCK(nt);
} else {
if (ieee80211_node_dectestref(ni))
@@ -1792,7 +2049,6 @@ ieee80211_node_delucastkey(struct ieee80211_node *ni)
static void
node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
{
- ieee80211_keyix keyix;
IEEE80211_NODE_LOCK_ASSERT(nt);
@@ -1807,15 +2063,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
* 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 */
- }
+ (void)node_clear_keyixmap(nt, ni);
if (!ieee80211_node_dectestref(ni)) {
/*
* Other references are present, just remove the
@@ -1823,9 +2071,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
* 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 */
+ ieee80211_del_node_nt(nt, ni);
} else
_ieee80211_free_node(ni);
}
@@ -1839,22 +2085,21 @@ 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);
+ IEEE80211_NODE_LOCK_INIT(nt, ic->ic_name);
TAILQ_INIT(&nt->nt_node);
+ nt->nt_count = 0;
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(
+ nt->nt_keyixmap = (struct ieee80211_node **) IEEE80211_MALLOC(
keyixmax * sizeof(struct ieee80211_node *),
- M_80211_NODE, M_NOWAIT | M_ZERO);
+ M_80211_NODE,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (nt->nt_keyixmap == NULL)
- if_printf(ic->ic_ifp,
+ ic_printf(ic,
"Cannot allocate key index map with %u entries\n",
keyixmax);
} else
@@ -1911,162 +2156,140 @@ ieee80211_node_table_cleanup(struct ieee80211_node_table *nt)
printf("%s: %s[%u] still active\n", __func__,
nt->nt_name, i);
#endif
- free(nt->nt_keyixmap, M_80211_NODE);
+ IEEE80211_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)
+timeout_stations(void *arg __unused, struct ieee80211_node *ni)
{
- struct ieee80211_node_table *nt = &ic->ic_sta;
- struct ieee80211vap *vap;
- struct ieee80211_node *ni;
- int gen = 0;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
- 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;
+ /*
+ * 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)
+ return;
+ /*
+ * 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).
+ */
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_STA) &&
+ (ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+ return;
+ /*
+ * 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
+ */
+ /* XXX before inact decrement? */
+ if (ni == vap->iv_bss)
+ return;
+ if (ni->ni_associd != 0 ||
+ (vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO)) {
/*
- * Free fragment if not needed anymore
- * (last fragment older than 1s).
- * XXX doesn't belong here, move to node_age
+ * Age/drain resources held by the station.
*/
- 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);
- }
+ ic->ic_node_age(ni);
/*
- * Special case ourself; we may be idle for extended periods
- * of time and regardless reclaiming our state is wrong.
- * XXX run ic_node_age
+ * 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 (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) {
+ (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,
- "station timed out due to inactivity "
- "(refcnt %u)", ieee80211_node_refcnt(ni));
+ IEEE80211_MSG_INACT | IEEE80211_MSG_NODE,
+ ni, "%s",
+ "probe station due to inactivity");
/*
- * 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.
+ * Grab a reference 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.
*/
+ /* XXX fix this (not required anymore). */
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;
+ /* XXX useless */
+ ieee80211_send_nulldata(ni);
+ /* XXX stat? */
+ return;
}
}
- IEEE80211_NODE_UNLOCK(nt);
+ 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 2. 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.
+ */
+ if (ni->ni_associd != 0) {
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_EXPIRE);
+ }
+ ieee80211_node_leave(ni);
+ vap->iv_stats.is_node_timeout++;
+ }
+}
+
+/*
+ * Timeout inactive stations and do related housekeeping.
+ */
+static void
+ieee80211_timeout_stations(struct ieee80211com *ic)
+{
+ struct ieee80211_node_table *nt = &ic->ic_sta;
- IEEE80211_NODE_ITERATE_UNLOCK(nt);
+ ieee80211_iterate_nodes(nt, timeout_stations, NULL);
}
/*
@@ -2131,7 +2354,7 @@ ieee80211_node_timeout(void *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
+ * Suppressing 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.
@@ -2150,30 +2373,69 @@ ieee80211_node_timeout(void *arg)
ieee80211_node_timeout, ic);
}
-void
-ieee80211_iterate_nodes(struct ieee80211_node_table *nt,
- ieee80211_iter_func *f, void *arg)
+/*
+ * The same as ieee80211_iterate_nodes(), but for one vap only.
+ */
+int
+ieee80211_iterate_nodes_vap(struct ieee80211_node_table *nt,
+ struct ieee80211vap *vap, ieee80211_iter_func *f, void *arg)
{
+ struct ieee80211_node **ni_arr;
struct ieee80211_node *ni;
- u_int gen;
+ size_t size;
+ int count, i;
- IEEE80211_NODE_ITERATE_LOCK(nt);
- gen = ++nt->nt_scangen;
-restart:
+ /*
+ * Iterate over the node table and save an array of ref'ed nodes.
+ *
+ * This is separated out from calling the actual node function so that
+ * no LORs will occur.
+ */
IEEE80211_NODE_LOCK(nt);
+ count = nt->nt_count;
+ size = count * sizeof(struct ieee80211_node *);
+ ni_arr = (struct ieee80211_node **) IEEE80211_MALLOC(size, M_80211_NODE,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (ni_arr == NULL) {
+ IEEE80211_NODE_UNLOCK(nt);
+ return (ENOMEM);
+ }
+
+ i = 0;
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;
- }
+ if (vap != NULL && ni->ni_vap != vap)
+ continue;
+ KASSERT(i < count,
+ ("node array overflow (vap %p, i %d, count %d)\n",
+ vap, i, count));
+ ni_arr[i] = ieee80211_ref_node(ni);
+ i++;
}
IEEE80211_NODE_UNLOCK(nt);
- IEEE80211_NODE_ITERATE_UNLOCK(nt);
+ for (i = 0; i < count; i++) {
+ if (ni_arr[i] == NULL) /* end of the list */
+ break;
+ (*f)(arg, ni_arr[i]);
+ /* ieee80211_free_node() locks by itself */
+ ieee80211_free_node(ni_arr[i]);
+ }
+
+ IEEE80211_FREE(ni_arr, M_80211_NODE);
+
+ return (0);
+}
+
+/*
+ * Just a wrapper, so we don't have to change every ieee80211_iterate_nodes()
+ * reference in the source.
+ */
+void
+ieee80211_iterate_nodes(struct ieee80211_node_table *nt,
+ ieee80211_iter_func *f, void *arg)
+{
+ /* XXX no way to pass error to the caller. */
+ (void) ieee80211_iterate_nodes_vap(nt, NULL, f, arg);
}
void
@@ -2181,8 +2443,8 @@ 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("\tauthmode %u flags 0x%x\n",
+ 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",
@@ -2466,7 +2728,7 @@ 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)) {
+ ieee80211_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");
@@ -2511,7 +2773,6 @@ ieee80211_node_leave(struct ieee80211_node *ni)
IEEE80211_LOCK(ic);
IEEE80211_AID_CLR(vap, ni->ni_associd);
- ni->ni_associd = 0;
vap->iv_sta_assoc--;
ic->ic_sta_assoc--;
@@ -2544,7 +2805,6 @@ done:
}
struct rssiinfo {
- struct ieee80211vap *vap;
int rssi_samples;
uint32_t rssi_total;
};
@@ -2556,8 +2816,6 @@ get_hostap_rssi(void *arg, struct ieee80211_node *ni)
struct ieee80211vap *vap = ni->ni_vap;
int8_t rssi;
- if (info->vap != vap)
- return;
/* only associated stations */
if (ni->ni_associd == 0)
return;
@@ -2575,8 +2833,6 @@ get_adhoc_rssi(void *arg, struct ieee80211_node *ni)
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)
@@ -2596,8 +2852,6 @@ get_mesh_rssi(void *arg, struct ieee80211_node *ni)
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;
@@ -2618,18 +2872,20 @@ ieee80211_getrssi(struct ieee80211vap *vap)
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);
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap, get_adhoc_rssi,
+ &info);
break;
case IEEE80211_M_HOSTAP: /* average of all associated stations */
- ieee80211_iterate_nodes(&ic->ic_sta, get_hostap_rssi, &info);
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap, 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);
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap, get_mesh_rssi,
+ &info);
break;
#endif
case IEEE80211_M_MONITOR: /* XXX */
diff --git a/freebsd/sys/net80211/ieee80211_node.h b/freebsd/sys/net80211/ieee80211_node.h
index c1fc0069..7ca24c18 100644
--- a/freebsd/sys/net80211/ieee80211_node.h
+++ b/freebsd/sys/net80211/ieee80211_node.h
@@ -36,7 +36,7 @@
* 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
+ * has a timeout set in its 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
@@ -65,6 +65,7 @@
struct ieee80211_node_table;
struct ieee80211com;
struct ieee80211vap;
+struct ieee80211_scanparams;
/*
* Information element ``blob''. We use this structure
@@ -82,6 +83,11 @@ struct ieee80211_ies {
uint8_t *htinfo_ie; /* captured HTINFO ie */
uint8_t *tdma_ie; /* captured TDMA ie */
uint8_t *meshid_ie; /* captured MESH ID ie */
+ uint8_t *vhtcap_ie; /* captured VHTCAP ie */
+ uint8_t *vhtopmode_ie; /* captured VHTOPMODE ie */
+ uint8_t *vhtpwrenv_ie; /* captured VHTPWRENV ie */
+ uint8_t *apchanrep_ie; /* captured APCHANREP ie */
+ uint8_t *bssload_ie; /* captured BSSLOAD ie */
uint8_t *spare[4];
/* NB: these must be the last members of this structure */
uint8_t *data; /* frame data > 802.11 header */
@@ -115,7 +121,6 @@ struct ieee80211_node {
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 */
@@ -137,6 +142,7 @@ struct ieee80211_node {
#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 */
+#define IEEE80211_NODE_VHT 0x100000 /* VHT enabled */
uint16_t ni_associd; /* association ID */
uint16_t ni_vlan; /* vlan tag */
uint16_t ni_txpower; /* current transmit power */
@@ -204,6 +210,8 @@ struct ieee80211_node {
struct callout ni_mltimer; /* link mesh timer */
uint8_t ni_mlrcnt; /* link mesh retry counter */
uint8_t ni_mltval; /* link mesh timer value */
+ struct callout ni_mlhtimer; /* link mesh backoff timer */
+ uint8_t ni_mlhcnt; /* link mesh holding counter */
/* 11n state */
uint16_t ni_htcap; /* HT capabilities */
@@ -214,9 +222,23 @@ struct ieee80211_node {
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_tx_ampdu ni_tx_ampdu[WME_NUM_TID];
struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID];
+ /* VHT state */
+ uint32_t ni_vhtcap;
+ uint16_t ni_vht_basicmcs;
+ uint16_t ni_vht_pad2;
+ struct ieee80211_vht_mcs_info ni_vht_mcsinfo;
+ uint8_t ni_vht_chan1; /* 20/40/80/160 - VHT chan1 */
+ uint8_t ni_vht_chan2; /* 80+80 - VHT chan2 */
+ uint8_t ni_vht_chanwidth; /* IEEE80211_VHT_CHANWIDTH_ */
+ uint8_t ni_vht_pad1;
+ uint32_t ni_vht_spare[8];
+
+ /* fast-frames state */
+ struct mbuf * ni_tx_superg[WME_NUM_TID];
+
/* others */
short ni_inact; /* inactivity mark count */
short ni_inact_reload;/* inactivity reload value */
@@ -299,8 +321,6 @@ ieee80211_unref_node(struct ieee80211_node **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 *);
@@ -326,6 +346,10 @@ void ieee80211_sync_curchan(struct ieee80211com *);
void ieee80211_setupcurchan(struct ieee80211com *,
struct ieee80211_channel *);
void ieee80211_setcurchan(struct ieee80211com *, struct ieee80211_channel *);
+void ieee80211_update_chw(struct ieee80211com *);
+int ieee80211_ibss_merge_check(struct ieee80211_node *);
+int ieee80211_ibss_node_check_new(struct ieee80211_node *ni,
+ const struct ieee80211_scanparams *);
int ieee80211_ibss_merge(struct ieee80211_node *);
struct ieee80211_scan_entry;
int ieee80211_sta_join(struct ieee80211vap *, struct ieee80211_channel *,
@@ -352,11 +376,10 @@ struct ieee80211_node_table {
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];
+ int nt_count; /* number of nodes */
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 */
};
@@ -439,6 +462,8 @@ int ieee80211_node_delucastkey(struct ieee80211_node *);
void ieee80211_node_timeout(void *arg);
typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
+int ieee80211_iterate_nodes_vap(struct ieee80211_node_table *,
+ struct ieee80211vap *, ieee80211_iter_func *, void *);
void ieee80211_iterate_nodes(struct ieee80211_node_table *,
ieee80211_iter_func *, void *);
diff --git a/freebsd/sys/net80211/ieee80211_output.c b/freebsd/sys/net80211/ieee80211_output.c
index 229c87ea..c9251796 100644
--- a/freebsd/sys/net80211/ieee80211_output.c
+++ b/freebsd/sys/net80211/ieee80211_output.c
@@ -35,8 +35,9 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
-#include <sys/mbuf.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
#include <sys/endian.h>
#include <sys/socket.h>
@@ -44,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_llc.h>
#include <net/if_media.h>
#include <net/if_vlan_var.h>
@@ -77,18 +79,6 @@ __FBSDID("$FreeBSD$");
#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);
@@ -112,219 +102,92 @@ doprint(struct ieee80211vap *vap, int subtype)
#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.
+ * Transmit a frame to the given destination on the given VAP.
+ *
+ * It's up to the caller to figure out the details of who this
+ * is going to and resolving the node.
+ *
+ * This routine takes care of queuing it for power save,
+ * A-MPDU state stuff, fast-frames state stuff, encapsulation
+ * if required, then passing it up to the driver layer.
+ *
+ * This routine (for now) consumes the mbuf and frees the node
+ * reference; it ideally will return a TX status which reflects
+ * whether the mbuf was consumed or not, so the caller can
+ * free the mbuf (if appropriate) and the node reference (again,
+ * if appropriate.)
*/
-void
-ieee80211_start(struct ifnet *ifp)
+int
+ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m,
+ struct ieee80211_node *ni)
{
-#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;
+ struct ifnet *ifp = vap->iv_ifp;
+#ifdef IEEE80211_SUPPORT_SUPERG
+ int mcast;
+#endif
- /* 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) {
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+ (m->m_flags & M_PWR_SAV) == 0) {
/*
- * In power save, wakeup device for transmit.
+ * 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?
*/
- ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
- return;
+ if (ieee80211_pwrsave(ni, m) != 0)
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ ieee80211_free_node(ni);
+
+ /*
+ * We queued it fine, so tell the upper layer
+ * that we consumed it.
+ */
+ return (0);
+ }
+ /* calculate priority so drivers can find the tx queue */
+ if (ieee80211_classify(ni, m)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
+ ni->ni_macaddr, NULL,
+ "%s", "classification failure");
+ vap->iv_stats.is_tx_classify++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ m_freem(m);
+ ieee80211_free_node(ni);
+
+ /* XXX better status? */
+ return (0);
}
/*
- * 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).
+ * 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.
*/
- 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;
- }
- }
+ m->m_pkthdr.rcvif = (void *)ni;
+#ifdef IEEE80211_SUPPORT_SUPERG
+ mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1: 0;
#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];
+ 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)) {
+ if ((m->m_flags & M_EAPOL) == 0) {
+ int tid = WME_AC_TO_TID(M_WME_GETAC(m));
+ struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid];
ieee80211_txampdu_count_packet(tap);
if (IEEE80211_AMPDU_RUNNING(tap)) {
@@ -343,37 +206,347 @@ ieee80211_start(struct ifnet *ifp)
/* XXX hold frame for reply? */
}
}
+ }
+
#ifdef IEEE80211_SUPPORT_SUPERG
- else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) {
+ /*
+ * Check for AMSDU/FF; queue for aggregation
+ *
+ * Note: we don't bother trying to do fast frames or
+ * A-MSDU encapsulation for 802.3 drivers. Now, we
+ * likely could do it for FF (because it's a magic
+ * atheros tunnel LLC type) but I don't think we're going
+ * to really need to. For A-MSDU we'd have to set the
+ * A-MSDU QoS bit in the wifi header, so we just plain
+ * can't do it.
+ *
+ * Strictly speaking, we could actually /do/ A-MSDU / FF
+ * with A-MPDU together which for certain circumstances
+ * is beneficial (eg A-MSDU of TCK ACKs.) However,
+ * I'll ignore that for now so existing behaviour is maintained.
+ * Later on it would be good to make "amsdu + ampdu" configurable.
+ */
+ else if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) {
+ if ((! mcast) && ieee80211_amsdu_tx_ok(ni)) {
+ m = ieee80211_amsdu_check(ni, m);
+ if (m == NULL) {
+ /* NB: any ni ref held on stageq */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
+ "%s: amsdu_check queued frame\n",
+ __func__);
+ return (0);
+ }
+ } else if ((! mcast) && 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;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
+ "%s: ff_check queued frame\n",
+ __func__);
+ return (0);
}
}
+ }
#endif /* IEEE80211_SUPPORT_SUPERG */
- if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) {
+
+ /*
+ * Grab the TX lock - serialise the TX process from this
+ * point (where TX state is being checked/modified)
+ * through to driver queue.
+ */
+ IEEE80211_TX_LOCK(ic);
+
+ /*
+ * XXX make the encap and transmit code a separate function
+ * so things like the FF (and later A-MSDU) path can just call
+ * it for flushed frames.
+ */
+ 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_TX_UNLOCK(ic);
+ ieee80211_free_node(ni);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (ENOBUFS);
+ }
+ }
+ (void) ieee80211_parent_xmitpkt(ic, m);
+
+ /*
+ * Unlock at this point - no need to hold it across
+ * ieee80211_free_node() (ie, the comlock)
+ */
+ IEEE80211_TX_UNLOCK(ic);
+ ic->ic_lastdata = ticks;
+
+ return (0);
+}
+
+
+
+/*
+ * Send the given mbuf through the given vap.
+ *
+ * This consumes the mbuf regardless of whether the transmit
+ * was successful or not.
+ *
+ * This does none of the initial checks that ieee80211_start()
+ * does (eg CAC timeout, interface wakeup) - the caller must
+ * do this first.
+ */
+static int
+ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m)
+{
+#define IS_DWDS(vap) \
+ (vap->iv_opmode == IEEE80211_M_WDS && \
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0)
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_node *ni;
+ struct ether_header *eh;
+
+ /*
+ * 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 */
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (ENOBUFS);
+ }
+ eh = mtod(m, struct ether_header *);
+ if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+ if (IS_DWDS(vap)) {
/*
- * Encapsulate the packet in prep for transmission.
+ * 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).
*/
- m = ieee80211_encap(vap, ni, m);
- if (m == NULL) {
- /* NB: stat+msg handled in ieee80211_encap */
- ieee80211_free_node(ni);
- continue;
- }
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS,
+ eh->ether_dhost, "mcast", "%s", "on DWDS");
+ vap->iv_stats.is_dwds_mcast++;
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ /* XXX better status? */
+ return (ENOBUFS);
}
-
- error = parent->if_transmit(parent, m);
- if (error != 0) {
- /* NB: IFQ_HANDOFF reclaims mbuf */
+ 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 */
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ m_freem(m);
+ /* XXX better status? */
+ return (ENOBUFS);
+ }
+ 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++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ m_freem(m);
ieee80211_free_node(ni);
- } else {
- ifp->if_opackets++;
+ /* XXX better status? */
+ return (ENOBUFS);
}
+#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++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ m_freem(m);
+ /* XXX better status? */
+ return (ENOBUFS);
+ }
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+ "forward frame from DS SA(%6D), DA(%6D)\n",
+ eh->ether_shost, ":",
+ eh->ether_dhost, ":");
+ 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).
+ */
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ /* XXX better status? */
+ return (ENOBUFS);
+ }
+ }
+#endif
+
+ /*
+ * We've resolved the sender, so attempt to transmit it.
+ */
+
+ if (vap->iv_state == IEEE80211_S_SLEEP) {
+ /*
+ * In power save; queue frame and then wakeup device
+ * for transmit.
+ */
ic->ic_lastdata = ticks;
+ if (ieee80211_pwrsave(ni, m) != 0)
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ ieee80211_free_node(ni);
+ ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
+ return (0);
+ }
+
+ if (ieee80211_vap_pkt_send_dest(vap, m, ni) != 0)
+ return (ENOBUFS);
+ return (0);
+#undef IS_DWDS
+}
+
+/*
+ * 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.
+ *
+ * if_transmit() requires that the mbuf be consumed by this call
+ * regardless of the return condition.
+ */
+int
+ieee80211_vap_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ /*
+ * 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 &&
+ vap->iv_state != IEEE80211_S_SLEEP) {
+ IEEE80211_LOCK(ic);
+ /* re-check under the com lock to avoid races */
+ if (vap->iv_state != IEEE80211_S_RUN &&
+ vap->iv_state != IEEE80211_S_SLEEP) {
+ 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++;
+ IEEE80211_UNLOCK(ic);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (ENETDOWN);
+ }
+ IEEE80211_UNLOCK(ic);
}
-#undef IS_DWDS
+
+ /*
+ * 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);
+
+ /*
+ * Bump to the packet transmission path.
+ * The mbuf will be consumed here.
+ */
+ return (ieee80211_start_pkt(vap, m));
+}
+
+void
+ieee80211_vap_qflush(struct ifnet *ifp)
+{
+
+ /* Empty for now */
+}
+
+/*
+ * 802.11 raw output routine.
+ *
+ * XXX TODO: this (and other send routines) should correctly
+ * XXX keep the pwr mgmt bit set if it decides to call into the
+ * XXX driver to send a frame whilst the state is SLEEP.
+ *
+ * Otherwise the peer may decide that we're awake and flood us
+ * with traffic we are still too asleep to receive!
+ */
+int
+ieee80211_raw_output(struct ieee80211vap *vap, struct ieee80211_node *ni,
+ struct mbuf *m, const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ int error;
+
+ /*
+ * Set node - the caller has taken a reference, so ensure
+ * that the mbuf has the same node value that
+ * it would if it were going via the normal path.
+ */
+ m->m_pkthdr.rcvif = (void *)ni;
+
+ /*
+ * Attempt to add bpf transmit parameters.
+ *
+ * For now it's ok to fail; the raw_xmit api still takes
+ * them as an option.
+ *
+ * Later on when ic_raw_xmit() has params removed,
+ * they'll have to be added - so fail the transmit if
+ * they can't be.
+ */
+ if (params)
+ (void) ieee80211_add_xmit_params(m, params);
+
+ error = ic->ic_raw_xmit(ni, m, params);
+ if (error) {
+ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, 1);
+ ieee80211_free_node(ni);
+ }
+ return (error);
}
/*
@@ -383,13 +556,15 @@ ieee80211_start(struct ifnet *ifp)
*/
int
ieee80211_output(struct ifnet *ifp, struct mbuf *m,
- struct sockaddr *dst, struct route *ro)
+ const 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;
+ struct ieee80211com *ic = NULL;
int error;
+ int ret;
if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
/*
@@ -403,6 +578,7 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m,
senderr(ENETDOWN);
}
vap = ifp->if_softc;
+ ic = vap->iv_ic;
/*
* Hand to the 802.3 code if not tagged as
* a raw 802.11 frame.
@@ -434,6 +610,8 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m,
if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
IEEE80211_FC0_VERSION_0)
senderr(EIO); /* XXX */
+ if (m->m_pkthdr.len < ieee80211_anyhdrsize(wh))
+ senderr(EIO); /* XXX */
/* locate destination node */
switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
@@ -443,8 +621,6 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m,
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:
@@ -473,7 +649,6 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m,
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);
@@ -483,21 +658,25 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m,
/* NB: ieee80211_encap does not include 802.11 header */
IEEE80211_NODE_STAT_ADD(ni, tx_bytes, m->m_pkthdr.len);
+ IEEE80211_TX_LOCK(ic);
+
/*
* 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,
+ ret = ieee80211_raw_output(vap, ni, m,
(const struct ieee80211_bpf_params *)(dst->sa_len ?
dst->sa_data : NULL));
+ IEEE80211_TX_UNLOCK(ic);
+ return (ret);
bad:
if (m != NULL)
m_freem(m);
if (ni != NULL)
ieee80211_free_node(ni);
- ifp->if_oerrors++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return error;
#undef senderr
}
@@ -522,6 +701,8 @@ ieee80211_send_setup(
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
ieee80211_seq seqno;
+ IEEE80211_TX_LOCK_ASSERT(ni->ni_ic);
+
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
switch (vap->iv_opmode) {
@@ -553,7 +734,6 @@ ieee80211_send_setup(
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 */
@@ -586,11 +766,16 @@ ieee80211_send_setup(
}
*(uint16_t *)&wh->i_dur[0] = 0;
- tap = &ni->ni_tx_ampdu[TID_TO_WME_AC(tid)];
+ tap = &ni->ni_tx_ampdu[tid];
if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap))
m->m_flags |= M_AMPDU_MPDU;
else {
- seqno = ni->ni_txseqs[tid]++;
+ if (IEEE80211_HAS_SEQ(type & IEEE80211_FC0_TYPE_MASK,
+ type & IEEE80211_FC0_SUBTYPE_MASK))
+ seqno = ni->ni_txseqs[tid]++;
+ else
+ seqno = 0;
+
*(uint16_t *)&wh->i_seq[0] =
htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
M_SEQNO_SET(m, seqno);
@@ -616,27 +801,28 @@ ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type,
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
+ int ret;
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]);
+ ieee80211_mgt_subtype_name(type));
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);
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
if (m == NULL) {
ieee80211_free_node(ni);
return ENOMEM;
}
+ IEEE80211_TX_LOCK(ic);
+
wh = mtod(m, struct ieee80211_frame *);
ieee80211_send_setup(ni, m,
IEEE80211_FC0_TYPE_MGT | type, IEEE80211_NONQOS_TID,
@@ -644,7 +830,7 @@ ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type,
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;
+ wh->i_fc[1] |= IEEE80211_FC1_PROTECTED;
}
m->m_flags |= M_ENCAP; /* mark encapsulated */
@@ -657,15 +843,24 @@ ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int 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_mgt_subtype_name(type),
ieee80211_chan2ieee(ic, ic->ic_curchan));
}
#endif
IEEE80211_NODE_STAT(ni, tx_mgmt);
- return ic->ic_raw_xmit(ni, m, params);
+ ret = ieee80211_raw_output(vap, ni, m, params);
+ IEEE80211_TX_UNLOCK(ic);
+ return (ret);
+}
+
+static void
+ieee80211_nulldata_transmitted(struct ieee80211_node *ni, void *arg,
+ int status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ wakeup(vap);
}
/*
@@ -689,6 +884,7 @@ ieee80211_send_nulldata(struct ieee80211_node *ni)
struct ieee80211_frame *wh;
int hdrlen;
uint8_t *frm;
+ int ret;
if (vap->iv_state == IEEE80211_S_CAC) {
IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
@@ -717,13 +913,15 @@ ieee80211_send_nulldata(struct ieee80211_node *ni)
}
KASSERT(M_LEADINGSPACE(m) >= hdrlen,
("leading space %zd", M_LEADINGSPACE(m)));
- M_PREPEND(m, hdrlen, M_DONTWAIT);
+ M_PREPEND(m, hdrlen, M_NOWAIT);
if (m == NULL) {
/* NB: cannot happen */
ieee80211_free_node(ni);
return ENOMEM;
}
+ IEEE80211_TX_LOCK(ic);
+
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);
@@ -753,6 +951,11 @@ ieee80211_send_nulldata(struct ieee80211_node *ni)
vap->iv_opmode != IEEE80211_M_HOSTAP)
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
}
+ if ((ic->ic_flags & IEEE80211_F_SCAN) &&
+ (ni->ni_flags & IEEE80211_NODE_PWR_MGT)) {
+ ieee80211_add_callback(m, ieee80211_nulldata_transmitted,
+ NULL);
+ }
m->m_len = m->m_pkthdr.len = hdrlen;
m->m_flags |= M_ENCAP; /* mark encapsulated */
@@ -766,7 +969,9 @@ ieee80211_send_nulldata(struct ieee80211_node *ni)
ieee80211_chan2ieee(ic, ic->ic_curchan),
wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
- return ic->ic_raw_xmit(ni, m, NULL);
+ ret = ieee80211_raw_output(vap, ni, m, NULL);
+ IEEE80211_TX_UNLOCK(ic);
+ return (ret);
}
/*
@@ -934,7 +1139,7 @@ ieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize,
return NULL;
}
KASSERT(needed_space <= MHLEN,
- ("not enough room, need %u got %zu\n", needed_space, MHLEN));
+ ("not enough room, need %u got %d\n", needed_space, MHLEN));
/*
* Setup new mbuf to have leading space to prepend the
* 802.11 header and any crypto header bits that are
@@ -1013,10 +1218,13 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
struct mbuf *m)
{
#define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh))
+#define MC01(mc) ((struct ieee80211_meshcntl_ae01 *)mc)
struct ieee80211com *ic = ni->ni_ic;
#ifdef IEEE80211_SUPPORT_MESH
struct ieee80211_mesh_state *ms = vap->iv_mesh;
struct ieee80211_meshcntl_ae10 *mc;
+ struct ieee80211_mesh_route *rt = NULL;
+ int dir = -1;
#endif
struct ether_header eh;
struct ieee80211_frame *wh;
@@ -1026,6 +1234,9 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
ieee80211_seq seqno;
int meshhdrsize, meshae;
uint8_t *qos;
+ int is_amsdu = 0;
+
+ IEEE80211_TX_LOCK_ASSERT(ic);
/*
* Copy existing Ethernet header to a safe place. The
@@ -1071,9 +1282,11 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
* ap's require all data frames to be QoS-encapsulated
* once negotiated in which case we'll need to make this
* configurable.
+ * NB: mesh data frames are QoS.
*/
- addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) &&
- (m->m_flags & M_EAPOL) == 0;
+ addqos = ((ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) ||
+ (vap->iv_opmode == IEEE80211_M_MBSS)) &&
+ (m->m_flags & M_EAPOL) == 0;
if (addqos)
hdrsize = sizeof(struct ieee80211_qosframe);
else
@@ -1095,21 +1308,40 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
* 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;
+ if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) {
+ rt = ieee80211_mesh_rt_find(vap, eh.ether_dhost);
+ KASSERT(rt != NULL, ("route is NULL"));
+ dir = IEEE80211_FC1_DIR_DSTODS;
+ hdrsize += IEEE80211_ADDR_LEN;
+ if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
+ if (IEEE80211_ADDR_EQ(rt->rt_mesh_gate,
+ vap->iv_myaddr)) {
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_MESH,
+ eh.ether_dhost,
+ "%s", "trying to send to ourself");
+ goto bad;
+ }
+ meshae = IEEE80211_MESH_AE_10;
+ meshhdrsize =
+ sizeof(struct ieee80211_meshcntl_ae10);
+ } else {
+ meshae = IEEE80211_MESH_AE_00;
+ meshhdrsize =
+ sizeof(struct ieee80211_meshcntl);
+ }
} else {
- meshae = 2;
- meshhdrsize += 2*IEEE80211_ADDR_LEN;
+ dir = IEEE80211_FC1_DIR_FROMDS;
+ if (!IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) {
+ /* proxy group */
+ meshae = IEEE80211_MESH_AE_01;
+ meshhdrsize =
+ sizeof(struct ieee80211_meshcntl_ae01);
+ } else {
+ /* group */
+ meshae = IEEE80211_MESH_AE_00;
+ meshhdrsize = sizeof(struct ieee80211_meshcntl);
+ }
}
} else {
#endif
@@ -1157,16 +1389,26 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
} else {
#ifdef IEEE80211_SUPPORT_SUPERG
/*
- * Aggregated frame.
+ * Aggregated frame. Check if it's for AMSDU or FF.
+ *
+ * XXX TODO: IEEE80211_NODE_AMSDU* isn't implemented
+ * anywhere for some reason. But, since 11n requires
+ * AMSDU RX, we can just assume "11n" == "AMSDU".
*/
- m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: called; M_FF\n", __func__);
+ if (ieee80211_amsdu_tx_ok(ni)) {
+ m = ieee80211_amsdu_encap(vap, m, hdrspace + meshhdrsize, key);
+ is_amsdu = 1;
+ } else {
+ 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);
+ M_PREPEND(m, hdrspace + meshhdrsize, M_NOWAIT);
if (m == NULL) {
vap->iv_stats.is_tx_nobuf++;
goto bad;
@@ -1210,44 +1452,52 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
/* NB: offset by hdrspace to deal with DATAPAD */
mc = (struct ieee80211_meshcntl_ae10 *)
(mtod(m, uint8_t *) + hdrspace);
+ wh->i_fc[1] = dir;
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);
+ case IEEE80211_MESH_AE_00: /* no proxy */
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;
+ if (dir == IEEE80211_FC1_DIR_DSTODS) { /* ucast */
+ 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);
+ qos =((struct ieee80211_qosframe_addr4 *)
+ wh)->i_qos;
+ } else if (dir == IEEE80211_FC1_DIR_FROMDS) {
+ /* mcast */
+ 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);
+ qos = ((struct ieee80211_qosframe *)
+ wh)->i_qos;
+ }
break;
- case 1: /* mcast, proxy */
+ case IEEE80211_MESH_AE_01: /* 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);
+ IEEE80211_ADDR_COPY(MC01(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);
+ case IEEE80211_MESH_AE_10: /* ucast, proxy */
+ KASSERT(rt != NULL, ("route is NULL"));
+ IEEE80211_ADDR_COPY(wh->i_addr1, rt->rt_nexthop);
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(wh->i_addr3, rt->rt_mesh_gate);
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);
+ mc->mc_flags = IEEE80211_MESH_AE_10;
+ IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(mc->mc_addr6, eh.ether_shost);
qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos;
break;
default:
@@ -1256,7 +1506,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
}
mc->mc_ttl = ms->ms_ttl;
ms->ms_seq++;
- LE_WRITE_4(mc->mc_seq, ms->ms_seq);
+ le32enc(mc->mc_seq, ms->ms_seq);
break;
#endif
case IEEE80211_M_WDS: /* NB: is4addr should always be true */
@@ -1279,9 +1529,21 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
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;
+#ifdef IEEE80211_SUPPORT_MESH
+ if (vap->iv_opmode == IEEE80211_M_MBSS)
+ qos[1] = IEEE80211_QOS_MC;
+ else
+#endif
+ qos[1] = 0;
wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
+ /*
+ * If this is an A-MSDU then ensure we set the
+ * relevant field.
+ */
+ if (is_amsdu)
+ qos[0] |= IEEE80211_QOS_AMSDU;
+
if ((m->m_flags & M_AMPDU_MPDU) == 0) {
/*
* NB: don't assign a sequence # to potential
@@ -1305,6 +1567,14 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
*(uint16_t *)wh->i_seq =
htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
M_SEQNO_SET(m, seqno);
+
+ /*
+ * XXX TODO: we shouldn't allow EAPOL, etc that would
+ * be forced to be non-QoS traffic to be A-MSDU encapsulated.
+ */
+ if (is_amsdu)
+ printf("%s: XXX ERROR: is_amsdu set; not QoS!\n",
+ __func__);
}
@@ -1323,7 +1593,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
(vap->iv_opmode == IEEE80211_M_STA ?
!IEEE80211_KEY_UNDEFINED(key) :
!IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) {
- wh->i_fc[1] |= IEEE80211_FC1_WEP;
+ wh->i_fc[1] |= IEEE80211_FC1_PROTECTED;
if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) {
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT,
eh.ether_dhost,
@@ -1353,6 +1623,22 @@ bad:
m_freem(m);
return NULL;
#undef WH4
+#undef MC01
+}
+
+void
+ieee80211_free_mbuf(struct mbuf *m)
+{
+ struct mbuf *next;
+
+ if (m == NULL)
+ return;
+
+ do {
+ next = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ m_freem(m);
+ } while ((m = next) != NULL);
}
/*
@@ -1367,33 +1653,35 @@ static int
ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0,
u_int hdrsize, u_int ciphdrsize, u_int mtu)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_frame *wh, *whf;
- struct mbuf *m, *prev, *next;
+ struct mbuf *m, *prev;
u_int totalhdrsize, fragno, fragsize, off, remainder, payload;
+ u_int hdrspace;
KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?"));
KASSERT(m0->m_pkthdr.len > mtu,
("pktlen %u mtu %u", m0->m_pkthdr.len, mtu));
+ /*
+ * Honor driver DATAPAD requirement.
+ */
+ if (ic->ic_flags & IEEE80211_F_DATAPAD)
+ hdrspace = roundup(hdrsize, sizeof(uint32_t));
+ else
+ hdrspace = hdrsize;
+
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;
+ totalhdrsize = hdrspace + 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);
+ fragsize = MIN(totalhdrsize + remainder, mtu);
+ m = m_get2(fragsize, M_NOWAIT, MT_DATA, M_PKTHDR);
if (m == NULL)
goto bad;
/* leave room to prepend any cipher header */
@@ -1404,9 +1692,20 @@ ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0,
* 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).
+ * NB: frag 1+ dont have Mesh Control field present.
*/
whf = mtod(m, struct ieee80211_frame *);
memcpy(whf, wh, hdrsize);
+#ifdef IEEE80211_SUPPORT_MESH
+ if (vap->iv_opmode == IEEE80211_M_MBSS) {
+ if (IEEE80211_IS_DSTODS(wh))
+ ((struct ieee80211_qosframe_addr4 *)
+ whf)->i_qos[1] &= ~IEEE80211_QOS_MC;
+ else
+ ((struct ieee80211_qosframe *)
+ whf)->i_qos[1] &= ~IEEE80211_QOS_MC;
+ }
+#endif
*(uint16_t *)&whf->i_seq[0] |= htole16(
(fragno & IEEE80211_SEQ_FRAG_MASK) <<
IEEE80211_SEQ_FRAG_SHIFT);
@@ -1414,9 +1713,10 @@ ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0,
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_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrspace);
+ m->m_len = hdrspace + payload;
+ m->m_pkthdr.len = hdrspace + payload;
m->m_flags |= M_FRAG;
/* chain up the fragment */
@@ -1442,11 +1742,7 @@ ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0,
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);
- }
+ ieee80211_free_mbuf(m0->m_nextpkt);
m0->m_nextpkt = NULL;
return 0;
}
@@ -1490,7 +1786,7 @@ ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
/*
* Add an ssid element to a frame.
*/
-static uint8_t *
+uint8_t *
ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
{
*frm++ = IEEE80211_ELEMID_SSID;
@@ -1527,7 +1823,7 @@ static uint8_t *
ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic)
{
#define ADDSHORT(frm, v) do { \
- LE_WRITE_2(frm, v); \
+ le16enc(frm, v); \
frm += 2; \
} while (0)
*frm++ = IEEE80211_ELEMID_CFPARMS;
@@ -1558,7 +1854,7 @@ add_ie(uint8_t *frm, const uint8_t *ie)
/*
* Add a WME information element to a frame.
*/
-static uint8_t *
+uint8_t *
ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme)
{
static const struct ieee80211_wme_info info = {
@@ -1582,7 +1878,7 @@ 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); \
+ le16enc(frm, v); \
frm += 2; \
} while (0)
/* NB: this works 'cuz a param has an info at the front */
@@ -1663,6 +1959,33 @@ ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic)
}
/*
+ * Add an 11h Quiet time element to a frame.
+ */
+static uint8_t *
+ieee80211_add_quiet(uint8_t *frm, struct ieee80211vap *vap)
+{
+ struct ieee80211_quiet_ie *quiet = (struct ieee80211_quiet_ie *) frm;
+
+ quiet->quiet_ie = IEEE80211_ELEMID_QUIET;
+ quiet->len = 6;
+ if (vap->iv_quiet_count_value == 1)
+ vap->iv_quiet_count_value = vap->iv_quiet_count;
+ else if (vap->iv_quiet_count_value > 1)
+ vap->iv_quiet_count_value--;
+
+ if (vap->iv_quiet_count_value == 0) {
+ /* value 0 is reserved as per 802.11h standerd */
+ vap->iv_quiet_count_value = 1;
+ }
+
+ quiet->tbttcount = vap->iv_quiet_count_value;
+ quiet->period = vap->iv_quiet_period;
+ quiet->duration = htole16(vap->iv_quiet_duration);
+ quiet->offset = htole16(vap->iv_quiet_offset);
+ return frm + sizeof(*quiet);
+}
+
+/*
* 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
@@ -1697,7 +2020,7 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic)
* re-calculation.
*/
if (ic->ic_countryie != NULL)
- free(ic->ic_countryie, M_80211_NODE_IE);
+ IEEE80211_FREE(ic->ic_countryie, M_80211_NODE_IE);
ic->ic_countryie = ieee80211_alloc_countryie(ic);
if (ic->ic_countryie == NULL)
return frm;
@@ -1706,6 +2029,40 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic)
return add_appie(frm, ic->ic_countryie);
}
+uint8_t *
+ieee80211_add_wpa(uint8_t *frm, const struct ieee80211vap *vap)
+{
+ if (vap->iv_flags & IEEE80211_F_WPA1 && vap->iv_wpa_ie != NULL)
+ return (add_ie(frm, vap->iv_wpa_ie));
+ else {
+ /* XXX else complain? */
+ return (frm);
+ }
+}
+
+uint8_t *
+ieee80211_add_rsn(uint8_t *frm, const struct ieee80211vap *vap)
+{
+ if (vap->iv_flags & IEEE80211_F_WPA2 && vap->iv_rsn_ie != NULL)
+ return (add_ie(frm, vap->iv_rsn_ie));
+ else {
+ /* XXX else complain? */
+ return (frm);
+ }
+}
+
+uint8_t *
+ieee80211_add_qos(uint8_t *frm, const struct ieee80211_node *ni)
+{
+ if (ni->ni_flags & IEEE80211_NODE_QOS) {
+ *frm++ = IEEE80211_ELEMID_QOS;
+ *frm++ = 1;
+ *frm++ = 0;
+ }
+
+ return (frm);
+}
+
/*
* Send a probe request frame with the specified ssid
* and any optional information element data.
@@ -1719,17 +2076,21 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_node *bss;
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;
+ int ret;
+
+ bss = ieee80211_ref_node(vap->iv_bss);
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++;
+ ieee80211_free_node(bss);
return EIO; /* XXX */
}
@@ -1751,6 +2112,7 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
* [tlv] supported rates
* [tlv] RSN (optional)
* [tlv] extended supported rates
+ * [tlv] HT cap (optional)
* [tlv] WPA (optional)
* [tlv] user-specified ie's
*/
@@ -1758,6 +2120,8 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
ic->ic_headroom + sizeof(struct ieee80211_frame),
2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ + sizeof(struct ieee80211_ie_htcap)
+ + sizeof(struct ieee80211_ie_htinfo)
+ sizeof(struct ieee80211_ie_wpa)
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ sizeof(struct ieee80211_ie_wpa)
@@ -1767,37 +2131,52 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
if (m == NULL) {
vap->iv_stats.is_tx_nobuf++;
ieee80211_free_node(ni);
+ ieee80211_free_node(bss);
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_rsn(frm, vap);
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? */
+
+ /*
+ * Note: we can't use bss; we don't have one yet.
+ *
+ * So, we should announce our capabilities
+ * in this channel mode (2g/5g), not the
+ * channel details itself.
+ */
+ if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
+ (vap->iv_flags_ht & IEEE80211_FHT_HT)) {
+ struct ieee80211_channel *c;
+
+ /*
+ * Get the HT channel that we should try upgrading to.
+ * If we can do 40MHz then this'll upgrade it appropriately.
+ */
+ c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
+ vap->iv_flags_ht);
+ frm = ieee80211_add_htcap_ch(frm, vap, c);
}
+
+ frm = ieee80211_add_wpa(frm, vap);
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);
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
if (m == NULL) {
/* NB: cannot happen */
ieee80211_free_node(ni);
+ ieee80211_free_node(bss);
return ENOMEM;
}
- wh = mtod(m, struct ieee80211_frame *);
+ IEEE80211_TX_LOCK(ic);
ieee80211_send_setup(ni, m,
IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
IEEE80211_NONQOS_TID, sa, da, bssid);
@@ -1810,8 +2189,11 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
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),
+ "send probe req on channel %u bssid %s sa %6D da %6D ssid \"%.*s\"\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ ether_sprintf(bssid),
+ sa, ":",
+ da, ":",
ssidlen, ssid);
memset(&params, 0, sizeof(params));
@@ -1824,7 +2206,10 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
} else
params.ibp_try0 = tp->maxretry;
params.ibp_power = ni->ni_txpower;
- return ic->ic_raw_xmit(ni, m, &params);
+ ret = ieee80211_raw_output(vap, ni, m, &params);
+ IEEE80211_TX_UNLOCK(ic);
+ ieee80211_free_node(bss);
+ return (ret);
}
/*
@@ -1956,7 +2341,8 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
case IEEE80211_FC0_SUBTYPE_DEAUTH:
IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
- "send station deauthenticate (reason %d)", arg);
+ "send station deauthenticate (reason: %d (%s))", arg,
+ ieee80211_reason_to_string(arg));
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
sizeof(uint16_t));
@@ -2046,33 +2432,40 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
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_rsn(frm, vap);
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);
}
+
+ /*
+ * Check the channel - we may be using an 11n NIC with an
+ * 11n capable station, but we're configured to be an 11b
+ * channel.
+ */
if ((vap->iv_flags_ht & IEEE80211_FHT_HT) &&
+ IEEE80211_IS_CHAN_HT(ni->ni_chan) &&
ni->ni_ies.htcap_ie != NULL &&
- ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
+ 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 */
}
+ frm = ieee80211_add_wpa(frm, vap);
if ((ic->ic_flags & IEEE80211_F_WME) &&
ni->ni_ies.wme_ie != NULL)
frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
+
+ /*
+ * Same deal - only send HT info if we're on an 11n
+ * capable channel.
+ */
if ((vap->iv_flags_ht & IEEE80211_FHT_HT) &&
+ IEEE80211_IS_CHAN_HT(ni->ni_chan) &&
ni->ni_ies.htcap_ie != NULL &&
- ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
+ 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,
@@ -2169,7 +2562,8 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
case IEEE80211_FC0_SUBTYPE_DISASSOC:
IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
- "send station disassociate (reason %d)", arg);
+ "send station disassociate (reason: %d (%s))", arg,
+ ieee80211_reason_to_string(arg));
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
sizeof(uint16_t));
@@ -2255,6 +2649,7 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
+ IEEE80211_COUNTRY_MAX_SIZE
+ 3
+ sizeof(struct ieee80211_csa_ie)
+ + sizeof(struct ieee80211_quiet_ie)
+ 3
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ sizeof(struct ieee80211_ie_wpa)
@@ -2321,14 +2716,17 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
if (ic->ic_flags & IEEE80211_F_CSAPENDING)
frm = ieee80211_add_csa(frm, vap);
}
+ if (vap->iv_flags & IEEE80211_F_DOTH) {
+ if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) {
+ if (vap->iv_quiet)
+ frm = ieee80211_add_quiet(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? */
- }
+ frm = ieee80211_add_rsn(frm, vap);
/*
* NB: legacy 11b clients do not get certain ie's.
* The caller identifies such clients by passing
@@ -2340,11 +2738,7 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
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? */
- }
+ frm = ieee80211_add_wpa(frm, vap);
if (vap->iv_flags & IEEE80211_F_WME)
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
if (IEEE80211_IS_CHAN_HT(bss->ni_chan) &&
@@ -2383,8 +2777,8 @@ ieee80211_send_proberesp(struct ieee80211vap *vap,
{
struct ieee80211_node *bss = vap->iv_bss;
struct ieee80211com *ic = vap->iv_ic;
- struct ieee80211_frame *wh;
struct mbuf *m;
+ int ret;
if (vap->iv_state == IEEE80211_S_CAC) {
IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss,
@@ -2410,10 +2804,10 @@ ieee80211_send_proberesp(struct ieee80211vap *vap,
return ENOMEM;
}
- M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
KASSERT(m != NULL, ("no room for header"));
- wh = mtod(m, struct ieee80211_frame *);
+ IEEE80211_TX_LOCK(ic);
ieee80211_send_setup(bss, m,
IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP,
IEEE80211_NONQOS_TID, vap->iv_myaddr, da, bss->ni_bssid);
@@ -2428,7 +2822,9 @@ ieee80211_send_proberesp(struct ieee80211vap *vap,
legacy ? " <legacy>" : "");
IEEE80211_NODE_STAT(bss, tx_mgmt);
- return ic->ic_raw_xmit(bss, m, NULL);
+ ret = ieee80211_raw_output(vap, bss, m, NULL);
+ IEEE80211_TX_UNLOCK(ic);
+ return (ret);
}
/*
@@ -2444,7 +2840,7 @@ ieee80211_alloc_rts(struct ieee80211com *ic,
struct mbuf *m;
/* XXX honor ic_headroom */
- m = m_gethdr(M_DONTWAIT, MT_DATA);
+ m = m_gethdr(M_NOWAIT, MT_DATA);
if (m != NULL) {
rts = mtod(m, struct ieee80211_frame_rts *);
rts->i_fc[0] = IEEE80211_FC0_VERSION_0 |
@@ -2470,7 +2866,7 @@ ieee80211_alloc_cts(struct ieee80211com *ic,
struct mbuf *m;
/* XXX honor ic_headroom */
- m = m_gethdr(M_DONTWAIT, MT_DATA);
+ m = m_gethdr(M_NOWAIT, MT_DATA);
if (m != NULL) {
cts = mtod(m, struct ieee80211_frame_cts *);
cts->i_fc[0] = IEEE80211_FC0_VERSION_0 |
@@ -2487,20 +2883,35 @@ ieee80211_alloc_cts(struct ieee80211com *ic,
static void
ieee80211_tx_mgt_timeout(void *arg)
{
- struct ieee80211_node *ni = arg;
- struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211vap *vap = arg;
+ IEEE80211_LOCK(vap->iv_ic);
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_new_state_locked(vap, IEEE80211_S_SCAN,
IEEE80211_SCAN_FAIL_TIMEOUT);
}
+ IEEE80211_UNLOCK(vap->iv_ic);
}
+/*
+ * This is the callback set on net80211-sourced transmitted
+ * authentication request frames.
+ *
+ * This does a couple of things:
+ *
+ * + If the frame transmitted was a success, it schedules a future
+ * event which will transition the interface to scan.
+ * If a state transition _then_ occurs before that event occurs,
+ * said state transition will cancel this callout.
+ *
+ * + If the frame transmit was a failure, it immediately schedules
+ * the transition back to scan.
+ */
static void
ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
{
@@ -2509,7 +2920,7 @@ ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
/*
* Frame transmit completed; arrange timer callback. If
- * transmit was successfuly we wait for response. Otherwise
+ * transmit was successfully 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
@@ -2518,17 +2929,19 @@ ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
*
* XXX what happens if !acked but response shows up before callback?
*/
- if (vap->iv_state == ostate)
+ if (vap->iv_state == ostate) {
callout_reset(&vap->iv_mgtsend,
status == 0 ? IEEE80211_TRANS_WAIT*hz : 0,
- ieee80211_tx_mgt_timeout, ni);
+ ieee80211_tx_mgt_timeout, vap);
+ }
}
static void
ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
- struct ieee80211_beacon_offsets *bo, struct ieee80211_node *ni)
+ struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_rateset *rs = &ni->ni_rates;
uint16_t capinfo;
@@ -2619,29 +3032,32 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
frm = ieee80211_add_powerconstraint(frm, vap);
bo->bo_csa = frm;
if (ic->ic_flags & IEEE80211_F_CSAPENDING)
- frm = ieee80211_add_csa(frm, vap);
+ frm = ieee80211_add_csa(frm, vap);
} else
bo->bo_csa = frm;
+
+ if (vap->iv_flags & IEEE80211_F_DOTH) {
+ bo->bo_quiet = frm;
+ if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) {
+ if (vap->iv_quiet)
+ frm = ieee80211_add_quiet(frm,vap);
+ }
+ } else
+ bo->bo_quiet = 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 */
- }
+ frm = ieee80211_add_rsn(frm, vap);
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 */
- }
+ frm = ieee80211_add_wpa(frm, vap);
if (vap->iv_flags & IEEE80211_F_WME) {
bo->bo_wme = frm;
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
@@ -2684,8 +3100,7 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
* Allocate a beacon frame and fillin the appropriate bits.
*/
struct mbuf *
-ieee80211_beacon_alloc(struct ieee80211_node *ni,
- struct ieee80211_beacon_offsets *bo)
+ieee80211_beacon_alloc(struct ieee80211_node *ni)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
@@ -2735,7 +3150,8 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
+ 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */
+ IEEE80211_COUNTRY_MAX_SIZE /* country */
+ 2 + 1 /* power control */
- + sizeof(struct ieee80211_csa_ie) /* CSA */
+ + sizeof(struct ieee80211_csa_ie) /* CSA */
+ + sizeof(struct ieee80211_quiet_ie) /* Quiet */
+ 2 + 1 /* ERP */
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */
@@ -2766,9 +3182,9 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
vap->iv_stats.is_tx_nobuf++;
return NULL;
}
- ieee80211_beacon_construct(m, frm, bo, ni);
+ ieee80211_beacon_construct(m, frm, ni);
- M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT);
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 |
@@ -2787,10 +3203,10 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
* 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)
+ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast)
{
struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
struct ieee80211com *ic = ni->ni_ic;
int len_changed = 0;
uint16_t capinfo;
@@ -2820,7 +3236,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
* clear IEEE80211_BEACON_CSA.
*/
ieee80211_beacon_construct(m,
- mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), bo, ni);
+ mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), ni);
/* XXX do WME aggressive mode processing? */
IEEE80211_UNLOCK(ic);
@@ -2841,10 +3257,10 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
struct ieee80211_wme_state *wme = &ic->ic_wme;
/*
- * Check for agressive mode change. When there is
+ * Check for aggressive 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
+ * parameters. Otherwise BE uses aggressive params
* to optimize performance of legacy/non-QoS traffic.
*/
if (wme->wme_flags & WME_F_AGGRMODE) {
@@ -2955,6 +3371,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
bo->bo_appie += adjust;
bo->bo_wme += adjust;
bo->bo_csa += adjust;
+ bo->bo_quiet += adjust;
bo->bo_tim_len = timlen;
/* update information element */
@@ -3008,6 +3425,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
#endif
bo->bo_appie += sizeof(*csa);
bo->bo_csa_trailer_len += sizeof(*csa);
+ bo->bo_quiet += sizeof(*csa);
bo->bo_tim_trailer_len += sizeof(*csa);
m->m_len += sizeof(*csa);
m->m_pkthdr.len += sizeof(*csa);
@@ -3018,6 +3436,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
vap->iv_csa_count++;
/* NB: don't clear IEEE80211_BEACON_CSA */
}
+ if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_DFS) ){
+ if (vap->iv_quiet)
+ ieee80211_add_quiet(bo->bo_quiet, vap);
+ }
if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) {
/*
* ERP element needs updating.
@@ -3060,3 +3483,80 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
return len_changed;
}
+
+/*
+ * 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).
+ */
+struct mbuf *
+ieee80211_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_NOWAIT);
+ 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;
+}
+
+/*
+ * Complete an mbuf transmission.
+ *
+ * For now, this simply processes a completed frame after the
+ * driver has completed it's transmission and/or retransmission.
+ * It assumes the frame is an 802.11 encapsulated frame.
+ *
+ * Later on it will grow to become the exit path for a given frame
+ * from the driver and, depending upon how it's been encapsulated
+ * and already transmitted, it may end up doing A-MPDU retransmission,
+ * power save requeuing, etc.
+ *
+ * In order for the above to work, the driver entry point to this
+ * must not hold any driver locks. Thus, the driver needs to delay
+ * any actual mbuf completion until it can release said locks.
+ *
+ * This frees the mbuf and if the mbuf has a node reference,
+ * the node reference will be freed.
+ */
+void
+ieee80211_tx_complete(struct ieee80211_node *ni, struct mbuf *m, int status)
+{
+
+ if (ni != NULL) {
+ struct ifnet *ifp = ni->ni_vap->iv_ifp;
+
+ if (status == 0) {
+ if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ if (m->m_flags & M_MCAST)
+ if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);
+ } else
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ if (m->m_flags & M_TXCB)
+ ieee80211_process_callback(ni, m, status);
+ ieee80211_free_node(ni);
+ }
+ m_freem(m);
+}
diff --git a/freebsd/sys/net80211/ieee80211_phy.c b/freebsd/sys/net80211/ieee80211_phy.c
index b4f70a96..20458b2c 100644
--- a/freebsd/sys/net80211/ieee80211_phy.c
+++ b/freebsd/sys/net80211/ieee80211_phy.c
@@ -37,12 +37,16 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/malloc.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/route.h>
+
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_phy.h>
@@ -62,8 +66,11 @@ struct ieee80211_ds_plcp_hdr {
#define TURBO IEEE80211_T_TURBO
#define HALF IEEE80211_T_OFDM_HALF
#define QUART IEEE80211_T_OFDM_QUARTER
+#define HT IEEE80211_T_HT
+/* XXX the 11n and the basic rate flag are unfortunately overlapping. Grr. */
+#define N(r) (IEEE80211_RATE_MCS | r)
#define PBCC (IEEE80211_T_OFDM_QUARTER+1) /* XXX */
-#define B(r) (0x80 | r)
+#define B(r) (IEEE80211_RATE_BASIC | r)
#define Mb(x) (x*1000)
static struct ieee80211_rate_table ieee80211_11b_table = {
@@ -178,6 +185,98 @@ static struct ieee80211_rate_table ieee80211_turboa_table = {
},
};
+static struct ieee80211_rate_table ieee80211_11ng_table = {
+ .rateCount = 36,
+ .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 },
+
+ [12] = { .phy = HT, 6500, 0x00, N(0), 4 },
+ [13] = { .phy = HT, 13000, 0x00, N(1), 6 },
+ [14] = { .phy = HT, 19500, 0x00, N(2), 6 },
+ [15] = { .phy = HT, 26000, 0x00, N(3), 8 },
+ [16] = { .phy = HT, 39000, 0x00, N(4), 8 },
+ [17] = { .phy = HT, 52000, 0x00, N(5), 8 },
+ [18] = { .phy = HT, 58500, 0x00, N(6), 8 },
+ [19] = { .phy = HT, 65000, 0x00, N(7), 8 },
+
+ [20] = { .phy = HT, 13000, 0x00, N(8), 4 },
+ [21] = { .phy = HT, 26000, 0x00, N(9), 6 },
+ [22] = { .phy = HT, 39000, 0x00, N(10), 6 },
+ [23] = { .phy = HT, 52000, 0x00, N(11), 8 },
+ [24] = { .phy = HT, 78000, 0x00, N(12), 8 },
+ [25] = { .phy = HT, 104000, 0x00, N(13), 8 },
+ [26] = { .phy = HT, 117000, 0x00, N(14), 8 },
+ [27] = { .phy = HT, 130000, 0x00, N(15), 8 },
+
+ [28] = { .phy = HT, 19500, 0x00, N(16), 4 },
+ [29] = { .phy = HT, 39000, 0x00, N(17), 6 },
+ [30] = { .phy = HT, 58500, 0x00, N(18), 6 },
+ [31] = { .phy = HT, 78000, 0x00, N(19), 8 },
+ [32] = { .phy = HT, 117000, 0x00, N(20), 8 },
+ [33] = { .phy = HT, 156000, 0x00, N(21), 8 },
+ [34] = { .phy = HT, 175500, 0x00, N(22), 8 },
+ [35] = { .phy = HT, 195000, 0x00, N(23), 8 },
+
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_11na_table = {
+ .rateCount = 32,
+ .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 },
+
+ [8] = { .phy = HT, 6500, 0x00, N(0), 0 },
+ [9] = { .phy = HT, 13000, 0x00, N(1), 2 },
+ [10] = { .phy = HT, 19500, 0x00, N(2), 2 },
+ [11] = { .phy = HT, 26000, 0x00, N(3), 4 },
+ [12] = { .phy = HT, 39000, 0x00, N(4), 4 },
+ [13] = { .phy = HT, 52000, 0x00, N(5), 4 },
+ [14] = { .phy = HT, 58500, 0x00, N(6), 4 },
+ [15] = { .phy = HT, 65000, 0x00, N(7), 4 },
+
+ [16] = { .phy = HT, 13000, 0x00, N(8), 0 },
+ [17] = { .phy = HT, 26000, 0x00, N(9), 2 },
+ [18] = { .phy = HT, 39000, 0x00, N(10), 2 },
+ [19] = { .phy = HT, 52000, 0x00, N(11), 4 },
+ [20] = { .phy = HT, 78000, 0x00, N(12), 4 },
+ [21] = { .phy = HT, 104000, 0x00, N(13), 4 },
+ [22] = { .phy = HT, 117000, 0x00, N(14), 4 },
+ [23] = { .phy = HT, 130000, 0x00, N(15), 4 },
+
+ [24] = { .phy = HT, 19500, 0x00, N(16), 0 },
+ [25] = { .phy = HT, 39000, 0x00, N(17), 2 },
+ [26] = { .phy = HT, 58500, 0x00, N(18), 2 },
+ [27] = { .phy = HT, 78000, 0x00, N(19), 4 },
+ [28] = { .phy = HT, 117000, 0x00, N(20), 4 },
+ [29] = { .phy = HT, 156000, 0x00, N(21), 4 },
+ [30] = { .phy = HT, 175500, 0x00, N(22), 4 },
+ [31] = { .phy = HT, 195000, 0x00, N(23), 4 },
+
+ },
+};
+
#undef Mb
#undef B
#undef OFDM
@@ -186,6 +285,8 @@ static struct ieee80211_rate_table ieee80211_turboa_table = {
#undef CCK
#undef TURBO
#undef XR
+#undef HT
+#undef N
/*
* Setup a rate table's reverse lookup table and fill in
@@ -199,28 +300,35 @@ static struct ieee80211_rate_table ieee80211_turboa_table = {
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++)
+ for (i = 0; i < nitems(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;
+ /*
+ * Map without the basic rate bit.
+ *
+ * It's up to the caller to ensure that the basic
+ * rate bit is stripped here.
+ *
+ * For HT, use the MCS rate bit.
+ */
+ code &= IEEE80211_RATE_VAL;
+ if (rt->info[i].phy == IEEE80211_T_HT) {
+ code |= IEEE80211_RATE_MCS;
}
+ /* XXX assume the control rate is non-MCS? */
+ ctl_rate &= 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;
@@ -238,32 +346,28 @@ ieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
}
#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_11na_table,
+ &ieee80211_11ng_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)
+ for (i = 0; i < nitems(ratetables); ++i)
ieee80211_setup_ratetable(ratetables[i]);
-#undef N
}
SYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL);
@@ -278,9 +382,9 @@ ieee80211_get_ratetable(struct ieee80211_channel *c)
else if (IEEE80211_IS_CHAN_QUARTER(c))
rt = &ieee80211_quarter_table;
else if (IEEE80211_IS_CHAN_HTA(c))
- rt = &ieee80211_11a_table; /* XXX */
+ rt = &ieee80211_11na_table;
else if (IEEE80211_IS_CHAN_HTG(c))
- rt = &ieee80211_11g_table; /* XXX */
+ rt = &ieee80211_11ng_table;
else if (IEEE80211_IS_CHAN_108G(c))
rt = &ieee80211_turbog_table;
else if (IEEE80211_IS_CHAN_ST(c))
@@ -461,7 +565,62 @@ ieee80211_compute_duration(const struct ieee80211_rate_table *rt,
default:
panic("%s: unknown phy %u (rate %u)\n", __func__,
rt->info[rix].phy, rate);
- break;
}
return txTime;
}
+
+static const uint16_t ht20_bps[32] = {
+ 26, 52, 78, 104, 156, 208, 234, 260,
+ 52, 104, 156, 208, 312, 416, 468, 520,
+ 78, 156, 234, 312, 468, 624, 702, 780,
+ 104, 208, 312, 416, 624, 832, 936, 1040
+};
+static const uint16_t ht40_bps[32] = {
+ 54, 108, 162, 216, 324, 432, 486, 540,
+ 108, 216, 324, 432, 648, 864, 972, 1080,
+ 162, 324, 486, 648, 972, 1296, 1458, 1620,
+ 216, 432, 648, 864, 1296, 1728, 1944, 2160
+};
+
+
+#define OFDM_PLCP_BITS 22
+#define HT_L_STF 8
+#define HT_L_LTF 8
+#define HT_L_SIG 4
+#define HT_SIG 8
+#define HT_STF 4
+#define HT_LTF(n) ((n) * 4)
+
+/*
+ * Calculate the transmit duration of an 11n frame.
+ */
+uint32_t
+ieee80211_compute_duration_ht(uint32_t frameLen, uint16_t rate,
+ int streams, int isht40, int isShortGI)
+{
+ uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
+
+ KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
+ KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
+
+ if (isht40)
+ bitsPerSymbol = ht40_bps[rate & 0x1f];
+ else
+ bitsPerSymbol = ht20_bps[rate & 0x1f];
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ if (isShortGI)
+ txTime = ((numSymbols * 18) + 4) / 5; /* 3.6us */
+ else
+ txTime = numSymbols * 4; /* 4us */
+ return txTime + HT_L_STF + HT_L_LTF +
+ HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
+}
+
+#undef HT_LTF
+#undef HT_STF
+#undef HT_SIG
+#undef HT_L_SIG
+#undef HT_L_LTF
+#undef HT_L_STF
+#undef OFDM_PLCP_BITS
diff --git a/freebsd/sys/net80211/ieee80211_phy.h b/freebsd/sys/net80211/ieee80211_phy.h
index 56b404f7..7970388b 100644
--- a/freebsd/sys/net80211/ieee80211_phy.h
+++ b/freebsd/sys/net80211/ieee80211_phy.h
@@ -53,6 +53,10 @@
#define IEEE80211_DUR_SHSLOT 9 /* ERP short slottime */
#define IEEE80211_DUR_OFDM_SLOT 9 /* OFDM slottime */
+#define IEEE80211_GET_SLOTTIME(ic) \
+ ((ic->ic_flags & IEEE80211_F_SHSLOT) ? \
+ IEEE80211_DUR_SHSLOT : IEEE80211_DUR_SLOT)
+
/*
* DIFS (microseconds).
*/
@@ -60,6 +64,8 @@
struct ieee80211_channel;
+#define IEEE80211_RATE_TABLE_SIZE 128
+
struct ieee80211_rate_table {
int rateCount; /* NB: for proper padding */
uint8_t rateCodeToIndex[256]; /* back mapping */
@@ -74,7 +80,7 @@ struct ieee80211_rate_table {
* rate; used for dur. calcs */
uint16_t lpAckDuration; /* long preamble ACK dur. */
uint16_t spAckDuration; /* short preamble ACK dur. */
- } info[32];
+ } info[IEEE80211_RATE_TABLE_SIZE];
};
const struct ieee80211_rate_table *ieee80211_get_ratetable(
@@ -83,7 +89,14 @@ const struct ieee80211_rate_table *ieee80211_get_ratetable(
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;
+ /*
+ * XXX Assert this is for a legacy rate; not for an MCS rate.
+ * If the caller wishes to use it for a basic rate, they should
+ * clear the high bit first.
+ */
+ KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+ uint8_t cix = rt->info[rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]].ctlRateIndex;
KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
return rt->info[cix].dot11Rate;
}
@@ -91,7 +104,14 @@ ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
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;
+ /*
+ * XXX Assert this is for a legacy rate; not for an MCS rate.
+ * If the caller wishes to use it for a basic rate, they should
+ * clear the high bit first.
+ */
+ KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+ uint8_t cix = rt->info[rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]].ctlRateIndex;
KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
return rt->info[cix].dot11Rate;
}
@@ -99,7 +119,14 @@ ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
static __inline__ enum ieee80211_phytype
ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate)
{
- uint8_t rix = rt->rateCodeToIndex[rate];
+ /*
+ * XXX Assert this is for a legacy rate; not for an MCS rate.
+ * If the caller wishes to use it for a basic rate, they should
+ * clear the high bit first.
+ */
+ KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+ uint8_t rix = rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL];
KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
return rt->info[rix].phy;
}
@@ -107,6 +134,13 @@ ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate)
static __inline__ int
ieee80211_isratevalid(const struct ieee80211_rate_table *rt, uint8_t rate)
{
+ /*
+ * XXX Assert this is for a legacy rate; not for an MCS rate.
+ * If the caller wishes to use it for a basic rate, they should
+ * clear the high bit first.
+ */
+ KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
return rt->rateCodeToIndex[rate] != (uint8_t)-1;
}
@@ -134,6 +168,14 @@ ieee80211_ack_duration(const struct ieee80211_rate_table *rt,
}
}
+static __inline__ uint8_t
+ieee80211_legacy_rate_lookup(const struct ieee80211_rate_table *rt,
+ uint8_t rate)
+{
+
+ return (rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]);
+}
+
/*
* Compute the time to transmit a frame of length frameLen bytes
* using the specified 802.11 rate code, phy, and short preamble
@@ -151,5 +193,18 @@ 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);
+
+/*
+ * 802.11n rate manipulation.
+ */
+
+#define IEEE80211_HT_RC_2_MCS(_rc) ((_rc) & 0x1f)
+#define IEEE80211_HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1)
+#define IEEE80211_IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS)
+
+uint32_t ieee80211_compute_duration_ht(uint32_t frameLen,
+ uint16_t rate, int streams, int isht40,
+ int isShortGI);
+
#endif /* _KERNEL */
#endif /* !_NET80211_IEEE80211_PHY_H_ */
diff --git a/freebsd/sys/net80211/ieee80211_power.c b/freebsd/sys/net80211/ieee80211_power.c
index dde58269..d557689c 100644
--- a/freebsd/sys/net80211/ieee80211_power.c
+++ b/freebsd/sys/net80211/ieee80211_power.c
@@ -36,10 +36,12 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
@@ -71,6 +73,8 @@ ieee80211_power_vattach(struct ieee80211vap *vap)
vap->iv_update_ps = ieee80211_update_ps;
vap->iv_set_tim = ieee80211_set_tim;
}
+ vap->iv_node_ps = ieee80211_node_pwrsave;
+ vap->iv_sta_ps = ieee80211_sta_pwrsave;
}
void
@@ -82,8 +86,9 @@ ieee80211_power_latevattach(struct ieee80211vap *vap)
*/
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);
+ vap->iv_tim_bitmap = (uint8_t *) IEEE80211_MALLOC(vap->iv_tim_len,
+ M_80211_POWER,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (vap->iv_tim_bitmap == NULL) {
printf("%s: no memory for TIM bitmap!\n", __func__);
/* XXX good enough to keep from crashing? */
@@ -96,7 +101,7 @@ void
ieee80211_power_vdetach(struct ieee80211vap *vap)
{
if (vap->iv_tim_bitmap != NULL) {
- free(vap->iv_tim_bitmap, M_80211_POWER);
+ IEEE80211_FREE(vap->iv_tim_bitmap, M_80211_POWER);
vap->iv_tim_bitmap = NULL;
}
}
@@ -413,9 +418,11 @@ static void
pwrsave_flushq(struct ieee80211_node *ni)
{
struct ieee80211_psq *psq = &ni->ni_psq;
+ struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_psq_head *qhead;
- struct ifnet *parent, *ifp;
+ struct mbuf *parent_q = NULL, *ifp_q = NULL;
+ struct mbuf *m;
IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"flush ps queue, %u packets queued", psq->psq_len);
@@ -424,36 +431,47 @@ pwrsave_flushq(struct ieee80211_node *ni)
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);
+ parent_q = qhead->head;
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);
+ ifp_q = qhead->head;
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);
+ /* parent frames, should be encapsulated */
+ while (parent_q != NULL) {
+ m = parent_q;
+ parent_q = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ /* must be encapsulated */
+ KASSERT((m->m_flags & M_ENCAP),
+ ("%s: parentq with non-M_ENCAP frame!\n",
+ __func__));
+ (void) ieee80211_parent_xmitpkt(ic, m);
+ }
+
+ /* VAP frames, aren't encapsulated */
+ while (ifp_q != NULL) {
+ m = ifp_q;
+ ifp_q = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ KASSERT((!(m->m_flags & M_ENCAP)),
+ ("%s: vapq with M_ENCAP frame!\n", __func__));
+ (void) ieee80211_vap_xmitpkt(vap, m);
+ }
}
/*
@@ -527,3 +545,108 @@ ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable)
ieee80211_send_nulldata(ieee80211_ref_node(ni));
}
}
+
+/*
+ * Handle being notified that we have data available for us in a TIM/ATIM.
+ *
+ * This may schedule a transition from _SLEEP -> _RUN if it's appropriate.
+ *
+ * In STA mode, we may have put to sleep during scan and need to be dragged
+ * back out of powersave mode.
+ */
+void
+ieee80211_sta_tim_notify(struct ieee80211vap *vap, int set)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ /*
+ * Schedule the driver state change. It'll happen at some point soon.
+ * Since the hardware shouldn't know that we're running just yet
+ * (and thus tell the peer that we're awake before we actually wake
+ * up said hardware), we leave the actual node state transition
+ * up to the transition to RUN.
+ *
+ * XXX TODO: verify that the transition to RUN will wake up the
+ * BSS node!
+ */
+ IEEE80211_LOCK(vap->iv_ic);
+ if (set == 1 && vap->iv_state == IEEE80211_S_SLEEP) {
+ ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
+ "%s: TIM=%d; wakeup\n", __func__, set);
+ } else if ((set == 1) && (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN)) {
+ /*
+ * XXX only do this if we're in RUN state?
+ */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
+ "%s: wake up from bgscan vap sleep\n",
+ __func__);
+ /*
+ * We may be in BGSCAN mode - this means the VAP is is in STA
+ * mode powersave. If it is, we need to wake it up so we
+ * can process outbound traffic.
+ */
+ vap->iv_sta_ps(vap, 0);
+ }
+ IEEE80211_UNLOCK(vap->iv_ic);
+}
+
+/*
+ * Timer check on whether the VAP has had any transmit activity.
+ *
+ * This may schedule a transition from _RUN -> _SLEEP if it's appropriate.
+ */
+void
+ieee80211_sta_ps_timer_check(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ /* XXX lock assert */
+
+ /* For no, only do this in STA mode */
+ if (! (vap->iv_caps & IEEE80211_C_SWSLEEP))
+ goto out;
+
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ goto out;
+
+ /* If we're not at run state, bail */
+ if (vap->iv_state != IEEE80211_S_RUN)
+ goto out;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
+ "%s: lastdata=%llu, ticks=%llu\n",
+ __func__, (unsigned long long) ic->ic_lastdata,
+ (unsigned long long) ticks);
+
+ /* If powersave is disabled on the VAP, don't bother */
+ if (! (vap->iv_flags & IEEE80211_F_PMGTON))
+ goto out;
+
+ /* If we've done any data within our idle interval, bail */
+ /* XXX hard-coded to one second for now, ew! */
+ if (ieee80211_time_after(ic->ic_lastdata + 500, ticks))
+ goto out;
+
+ /*
+ * Signify we're going into power save and transition the
+ * node to powersave.
+ */
+ if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
+ vap->iv_sta_ps(vap, 1);
+
+ /*
+ * XXX The driver has to handle the fact that we're going
+ * to sleep but frames may still be transmitted;
+ * hopefully it and/or us will do the right thing and mark any
+ * transmitted frames with PWRMGT set to 1.
+ */
+ ieee80211_new_state_locked(vap, IEEE80211_S_SLEEP, 0);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
+ "%s: time delta=%d msec\n", __func__,
+ (int) ticks_to_msecs(ticks - ic->ic_lastdata));
+
+out:
+ return;
+}
diff --git a/freebsd/sys/net80211/ieee80211_power.h b/freebsd/sys/net80211/ieee80211_power.h
index 352cdadb..d9bbaa58 100644
--- a/freebsd/sys/net80211/ieee80211_power.h
+++ b/freebsd/sys/net80211/ieee80211_power.h
@@ -71,9 +71,17 @@ 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 *);
+
+/*
+ * Don't call these directly from the stack; they are vap methods
+ * that should be overridden.
+ */
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_sta_tim_notify(struct ieee80211vap *vap, int set);
+void ieee80211_sta_ps_timer_check(struct ieee80211vap *vap);
+/* XXX what's this? */
void ieee80211_power_poll(struct ieee80211com *);
#endif /* _NET80211_IEEE80211_POWER_H_ */
diff --git a/freebsd/sys/net80211/ieee80211_proto.c b/freebsd/sys/net80211/ieee80211_proto.c
index 2f9e60b8..99a8ac99 100644
--- a/freebsd/sys/net80211/ieee80211_proto.c
+++ b/freebsd/sys/net80211/ieee80211_proto.c
@@ -3,6 +3,7 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
+ * Copyright (c) 2012 IEEE
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,13 +38,15 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/local/opt_wlan.h>
#include <rtems/bsd/sys/param.h>
-#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h> /* XXX for ether_sprintf */
@@ -62,16 +65,16 @@ __FBSDID("$FreeBSD$");
#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */
#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */
-const char *ieee80211_mgt_subtype_name[] = {
+const char *mgt_subtype_name[] = {
"assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
- "probe_req", "probe_resp", "reserved#6", "reserved#7",
+ "probe_req", "probe_resp", "timing_adv", "reserved#7",
"beacon", "atim", "disassoc", "auth",
"deauth", "action", "action_noack", "reserved#15"
};
-const char *ieee80211_ctl_subtype_name[] = {
+const char *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",
+ "reserved#4", "reserved#5", "reserved#6", "control_wrap",
+ "bar", "ba", "ps_poll", "rts",
"cts", "ack", "cf_end", "cf_end_ack"
};
const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = {
@@ -101,23 +104,155 @@ const char *ieee80211_wme_acnames[] = {
"WME_UPSD",
};
+
+/*
+ * Reason code descriptions were (mostly) obtained from
+ * IEEE Std 802.11-2012, pp. 442-445 Table 8-36.
+ */
+const char *
+ieee80211_reason_to_string(uint16_t reason)
+{
+ switch (reason) {
+ case IEEE80211_REASON_UNSPECIFIED:
+ return ("unspecified");
+ case IEEE80211_REASON_AUTH_EXPIRE:
+ return ("previous authentication is expired");
+ case IEEE80211_REASON_AUTH_LEAVE:
+ return ("sending STA is leaving/has left IBSS or ESS");
+ case IEEE80211_REASON_ASSOC_EXPIRE:
+ return ("disassociated due to inactivity");
+ case IEEE80211_REASON_ASSOC_TOOMANY:
+ return ("too many associated STAs");
+ case IEEE80211_REASON_NOT_AUTHED:
+ return ("class 2 frame received from nonauthenticated STA");
+ case IEEE80211_REASON_NOT_ASSOCED:
+ return ("class 3 frame received from nonassociated STA");
+ case IEEE80211_REASON_ASSOC_LEAVE:
+ return ("sending STA is leaving/has left BSS");
+ case IEEE80211_REASON_ASSOC_NOT_AUTHED:
+ return ("STA requesting (re)association is not authenticated");
+ case IEEE80211_REASON_DISASSOC_PWRCAP_BAD:
+ return ("information in the Power Capability element is "
+ "unacceptable");
+ case IEEE80211_REASON_DISASSOC_SUPCHAN_BAD:
+ return ("information in the Supported Channels element is "
+ "unacceptable");
+ case IEEE80211_REASON_IE_INVALID:
+ return ("invalid element");
+ case IEEE80211_REASON_MIC_FAILURE:
+ return ("MIC failure");
+ case IEEE80211_REASON_4WAY_HANDSHAKE_TIMEOUT:
+ return ("4-Way handshake timeout");
+ case IEEE80211_REASON_GROUP_KEY_UPDATE_TIMEOUT:
+ return ("group key update timeout");
+ case IEEE80211_REASON_IE_IN_4WAY_DIFFERS:
+ return ("element in 4-Way handshake different from "
+ "(re)association request/probe response/beacon frame");
+ case IEEE80211_REASON_GROUP_CIPHER_INVALID:
+ return ("invalid group cipher");
+ case IEEE80211_REASON_PAIRWISE_CIPHER_INVALID:
+ return ("invalid pairwise cipher");
+ case IEEE80211_REASON_AKMP_INVALID:
+ return ("invalid AKMP");
+ case IEEE80211_REASON_UNSUPP_RSN_IE_VERSION:
+ return ("unsupported version in RSN IE");
+ case IEEE80211_REASON_INVALID_RSN_IE_CAP:
+ return ("invalid capabilities in RSN IE");
+ case IEEE80211_REASON_802_1X_AUTH_FAILED:
+ return ("IEEE 802.1X authentication failed");
+ case IEEE80211_REASON_CIPHER_SUITE_REJECTED:
+ return ("cipher suite rejected because of the security "
+ "policy");
+ case IEEE80211_REASON_UNSPECIFIED_QOS:
+ return ("unspecified (QoS-related)");
+ case IEEE80211_REASON_INSUFFICIENT_BW:
+ return ("QoS AP lacks sufficient bandwidth for this QoS STA");
+ case IEEE80211_REASON_TOOMANY_FRAMES:
+ return ("too many frames need to be acknowledged");
+ case IEEE80211_REASON_OUTSIDE_TXOP:
+ return ("STA is transmitting outside the limits of its TXOPs");
+ case IEEE80211_REASON_LEAVING_QBSS:
+ return ("requested from peer STA (the STA is "
+ "resetting/leaving the BSS)");
+ case IEEE80211_REASON_BAD_MECHANISM:
+ return ("requested from peer STA (it does not want to use "
+ "the mechanism)");
+ case IEEE80211_REASON_SETUP_NEEDED:
+ return ("requested from peer STA (setup is required for the "
+ "used mechanism)");
+ case IEEE80211_REASON_TIMEOUT:
+ return ("requested from peer STA (timeout)");
+ case IEEE80211_REASON_PEER_LINK_CANCELED:
+ return ("SME cancels the mesh peering instance (not related "
+ "to the maximum number of peer mesh STAs)");
+ case IEEE80211_REASON_MESH_MAX_PEERS:
+ return ("maximum number of peer mesh STAs was reached");
+ case IEEE80211_REASON_MESH_CPVIOLATION:
+ return ("the received information violates the Mesh "
+ "Configuration policy configured in the mesh STA "
+ "profile");
+ case IEEE80211_REASON_MESH_CLOSE_RCVD:
+ return ("the mesh STA has received a Mesh Peering Close "
+ "message requesting to close the mesh peering");
+ case IEEE80211_REASON_MESH_MAX_RETRIES:
+ return ("the mesh STA has resent dot11MeshMaxRetries Mesh "
+ "Peering Open messages, without receiving a Mesh "
+ "Peering Confirm message");
+ case IEEE80211_REASON_MESH_CONFIRM_TIMEOUT:
+ return ("the confirmTimer for the mesh peering instance times "
+ "out");
+ case IEEE80211_REASON_MESH_INVALID_GTK:
+ return ("the mesh STA fails to unwrap the GTK or the values "
+ "in the wrapped contents do not match");
+ case IEEE80211_REASON_MESH_INCONS_PARAMS:
+ return ("the mesh STA receives inconsistent information about "
+ "the mesh parameters between Mesh Peering Management "
+ "frames");
+ case IEEE80211_REASON_MESH_INVALID_SECURITY:
+ return ("the mesh STA fails the authenticated mesh peering "
+ "exchange because due to failure in selecting "
+ "pairwise/group ciphersuite");
+ case IEEE80211_REASON_MESH_PERR_NO_PROXY:
+ return ("the mesh STA does not have proxy information for "
+ "this external destination");
+ case IEEE80211_REASON_MESH_PERR_NO_FI:
+ return ("the mesh STA does not have forwarding information "
+ "for this destination");
+ case IEEE80211_REASON_MESH_PERR_DEST_UNREACH:
+ return ("the mesh STA determines that the link to the next "
+ "hop of an active path in its forwarding information "
+ "is no longer usable");
+ case IEEE80211_REASON_MESH_MAC_ALRDY_EXISTS_MBSS:
+ return ("the MAC address of the STA already exists in the "
+ "mesh BSS");
+ case IEEE80211_REASON_MESH_CHAN_SWITCH_REG:
+ return ("the mesh STA performs channel switch to meet "
+ "regulatory requirements");
+ case IEEE80211_REASON_MESH_CHAN_SWITCH_UNSPEC:
+ return ("the mesh STA performs channel switch with "
+ "unspecified reason");
+ default:
+ return ("reserved/unknown");
+ }
+}
+
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 update_chw(void *, int);
+static void update_wme(void *, int);
+static void restart_vaps(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");
+ ic_printf(ni->ni_ic, "missing ic_raw_xmit callback, drop frame\n");
m_freem(m);
return ENETDOWN;
}
@@ -125,27 +260,30 @@ null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
void
ieee80211_proto_attach(struct ieee80211com *ic)
{
- struct ifnet *ifp = ic->ic_ifp;
+ uint8_t hdrlen;
/* override the 802.3 setting */
- ifp->if_hdrlen = ic->ic_headroom
+ 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) {
+ if (ALIGN(hdrlen) > max_linkhdr) {
/* XXX sanity check... */
- max_linkhdr = ALIGN(ifp->if_hdrlen);
+ max_linkhdr = ALIGN(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_parent_task, 0, parent_updown, ic);
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);
+ TASK_INIT(&ic->ic_chw_task, 0, update_chw, ic);
+ TASK_INIT(&ic->ic_wme_task, 0, update_wme, ic);
+ TASK_INIT(&ic->ic_restart_task, 0, restart_vaps, ic);
ic->ic_wme.wme_hipri_switch_hysteresis =
AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
@@ -190,13 +328,16 @@ ieee80211_proto_vattach(struct ieee80211vap *vap)
int i;
/* override the 802.3 setting */
- ifp->if_hdrlen = ic->ic_ifp->if_hdrlen;
+ ifp->if_hdrlen = ic->ic_headroom
+ + sizeof(struct ieee80211_qosframe_addr4)
+ + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
+ + IEEE80211_WEP_EXTIVLEN;
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);
+ callout_init_mtx(&vap->iv_swbmiss, IEEE80211_LOCK_OBJ(ic), 0);
+ callout_init(&vap->iv_mgtsend, 1);
TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap);
TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap);
/*
@@ -252,7 +393,7 @@ ieee80211_proto_vdetach(struct ieee80211vap *vap)
{
#define FREEAPPIE(ie) do { \
if (ie != NULL) \
- free(ie, M_80211_NODE_IE); \
+ IEEE80211_FREE(ie, M_80211_NODE_IE); \
} while (0)
/*
* Detach operating mode module.
@@ -435,9 +576,7 @@ ieee80211_dump_pkt(struct ieee80211com *ic,
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]);
+ printf(" %s", ieee80211_mgt_subtype_name(wh->i_fc[0]));
break;
default:
printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
@@ -449,7 +588,7 @@ ieee80211_dump_pkt(struct ieee80211com *ic,
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) {
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
int off;
off = ieee80211_anyhdrspace(ic, wh);
@@ -490,7 +629,6 @@ 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;
@@ -544,7 +682,8 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
* Sort rates.
*/
for (j = i + 1; j < nrs->rs_nrates; j++) {
- if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) {
+ if (IEEE80211_RV(nrs->rs_rates[i]) >
+ IEEE80211_RV(nrs->rs_rates[j])) {
r = nrs->rs_rates[i];
nrs->rs_rates[i] = nrs->rs_rates[j];
nrs->rs_rates[j] = r;
@@ -603,8 +742,7 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
"ucastrate %x\n", __func__, fixedrate, ucastrate, flags);
return badrate | IEEE80211_RATE_BASIC;
} else
- return RV(okrate);
-#undef RV
+ return IEEE80211_RV(okrate);
}
/*
@@ -652,7 +790,7 @@ ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff)
ic->ic_flags &= ~IEEE80211_F_SHSLOT;
/* notify driver */
if (ic->ic_updateslot != NULL)
- ic->ic_updateslot(ic->ic_ifp);
+ ic->ic_updateslot(ic);
}
/*
@@ -662,13 +800,12 @@ ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff)
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))
+ if (rs->rs_nrates < nitems(rates))
return 0;
- for (i = 0; i < N(rates); i++) {
+ for (i = 0; i < nitems(rates); i++) {
for (j = 0; j < rs->rs_nrates; j++) {
int r = rs->rs_rates[j] & IEEE80211_RATE_VAL;
if (rates[i] == r)
@@ -681,7 +818,6 @@ ieee80211_iserp_rateset(const struct ieee80211_rateset *rs)
;
}
return 1;
-#undef N
}
/*
@@ -948,7 +1084,7 @@ ieee80211_wme_initparams_locked(struct ieee80211vap *vap)
/* NB: check ic_bss to avoid NULL deref on initial attach */
if (vap->iv_bss != NULL) {
/*
- * Calculate agressive mode switching threshold based
+ * Calculate aggressive 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.
@@ -996,6 +1132,7 @@ ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
struct wmeParams *chanp, *bssp;
enum ieee80211_phymode mode;
int i;
+ int do_aggrmode = 0;
/*
* Set up the channel access parameters for the physical
@@ -1029,18 +1166,45 @@ ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
mode = IEEE80211_MODE_AUTO;
/*
- * This implements agressive mode as found in certain
+ * This implements aggressive 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
+ * BE uses aggressive 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) {
+
+ /* Hostap? Only if aggressive mode is enabled */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
+ (wme->wme_flags & WME_F_AGGRMODE) != 0)
+ do_aggrmode = 1;
+
+ /*
+ * Station? Only if we're in a non-QoS BSS.
+ */
+ else if ((vap->iv_opmode == IEEE80211_M_STA &&
+ (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0))
+ do_aggrmode = 1;
+
+ /*
+ * IBSS? Only if we we have WME enabled.
+ */
+ else if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
+ (vap->iv_flags & IEEE80211_F_WME))
+ do_aggrmode = 1;
+
+ /*
+ * If WME is disabled on this VAP, default to aggressive mode
+ * regardless of the configuration.
+ */
+ if ((vap->iv_flags & IEEE80211_F_WME) == 0)
+ do_aggrmode = 1;
+
+ /* XXX WDS? */
+
+ /* XXX MBSS? */
+
+ if (do_aggrmode) {
chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
@@ -1058,7 +1222,14 @@ ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
chanp->wmep_acm, chanp->wmep_aifsn, chanp->wmep_logcwmin,
chanp->wmep_logcwmax, chanp->wmep_txopLimit);
}
-
+
+
+ /*
+ * Change the contention window based on the number of associated
+ * stations. If the number of associated stations is 1 and
+ * aggressive mode is enabled, lower the contention window even
+ * further.
+ */
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] = {
@@ -1082,8 +1253,15 @@ ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
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 the beacon update.
+ *
+ * XXX what about MBSS, WDS?
+ */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP
+ || vap->iv_opmode == IEEE80211_M_IBSS) {
/*
* Arrange for a beacon update and bump the parameter
* set number so associated stations load the new values.
@@ -1093,7 +1271,8 @@ ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME);
}
- wme->wme_update(ic);
+ /* schedule the deferred WME update */
+ ieee80211_runtask(ic, &ic->ic_wme_task);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: WME params updated, cap_info 0x%x\n", __func__,
@@ -1117,27 +1296,25 @@ ieee80211_wme_updateparams(struct ieee80211vap *vap)
static void
parent_updown(void *arg, int npending)
{
- struct ifnet *parent = arg;
+ struct ieee80211com *ic = arg;
- parent->if_ioctl(parent, SIOCSIFFLAGS, NULL);
+ ic->ic_parent(ic);
}
static void
update_mcast(void *arg, int npending)
{
struct ieee80211com *ic = arg;
- struct ifnet *parent = ic->ic_ifp;
- ic->ic_update_mcast(parent);
+ ic->ic_update_mcast(ic);
}
static void
update_promisc(void *arg, int npending)
{
struct ieee80211com *ic = arg;
- struct ifnet *parent = ic->ic_ifp;
- ic->ic_update_promisc(parent);
+ ic->ic_update_promisc(ic);
}
static void
@@ -1149,6 +1326,37 @@ update_channel(void *arg, int npending)
ieee80211_radiotap_chan_change(ic);
}
+static void
+update_chw(void *arg, int npending)
+{
+ struct ieee80211com *ic = arg;
+
+ /*
+ * XXX should we defer the channel width _config_ update until now?
+ */
+ ic->ic_update_chw(ic);
+}
+
+static void
+update_wme(void *arg, int npending)
+{
+ struct ieee80211com *ic = arg;
+
+ /*
+ * XXX should we defer the WME configuration update until now?
+ */
+ ic->ic_wme.wme_update(ic);
+}
+
+static void
+restart_vaps(void *arg, int npending)
+{
+ struct ieee80211com *ic = arg;
+
+ ieee80211_suspend_all(ic);
+ ieee80211_resume_all(ic);
+}
+
/*
* Block until the parent is in a known state. This is
* used after any operations that dispatch a task (e.g.
@@ -1163,10 +1371,47 @@ ieee80211_waitfor_parent(struct ieee80211com *ic)
ieee80211_draintask(ic, &ic->ic_promisc_task);
ieee80211_draintask(ic, &ic->ic_chan_task);
ieee80211_draintask(ic, &ic->ic_bmiss_task);
+ ieee80211_draintask(ic, &ic->ic_chw_task);
+ ieee80211_draintask(ic, &ic->ic_wme_task);
taskqueue_unblock(ic->ic_tq);
}
/*
+ * Check to see whether the current channel needs reset.
+ *
+ * Some devices don't handle being given an invalid channel
+ * in their operating mode very well (eg wpi(4) will throw a
+ * firmware exception.)
+ *
+ * Return 0 if we're ok, 1 if the channel needs to be reset.
+ *
+ * See PR kern/202502.
+ */
+static int
+ieee80211_start_check_reset_chan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ if ((vap->iv_opmode == IEEE80211_M_IBSS &&
+ IEEE80211_IS_CHAN_NOADHOC(ic->ic_curchan)) ||
+ (vap->iv_opmode == IEEE80211_M_HOSTAP &&
+ IEEE80211_IS_CHAN_NOHOSTAP(ic->ic_curchan)))
+ return (1);
+ return (0);
+}
+
+/*
+ * Reset the curchan to a known good state.
+ */
+static void
+ieee80211_start_reset_chan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ ic->ic_curchan = &ic->ic_channels[0];
+}
+
+/*
* 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.
@@ -1176,7 +1421,6 @@ 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);
@@ -1198,12 +1442,15 @@ ieee80211_start_locked(struct ieee80211vap *vap)
* 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) {
+ if (ic->ic_nrunning++ == 0) {
+
+ /* reset the channel to a known good channel */
+ if (ieee80211_start_check_reset_chan(vap))
+ ieee80211_start_reset_chan(vap);
+
IEEE80211_DPRINTF(vap,
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
- "%s: up parent %s\n", __func__, parent->if_xname);
- parent->if_flags |= IFF_UP;
+ "%s: up parent %s\n", __func__, ic->ic_name);
ieee80211_runtask(ic, &ic->ic_parent_task);
return;
}
@@ -1212,8 +1459,7 @@ ieee80211_start_locked(struct ieee80211vap *vap)
* 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_roaming != IEEE80211_ROAMING_MANUAL) {
if (vap->iv_opmode == IEEE80211_M_STA) {
#if 0
/* XXX bypasses scan too easily; disable for now */
@@ -1296,7 +1542,6 @@ 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);
@@ -1306,12 +1551,10 @@ ieee80211_stop_locked(struct ieee80211vap *vap)
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)) {
+ if (--ic->ic_nrunning == 0) {
IEEE80211_DPRINTF(vap,
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
- "down parent %s\n", parent->if_xname);
- parent->if_flags &= ~IFF_UP;
+ "down parent %s\n", ic->ic_name);
ieee80211_runtask(ic, &ic->ic_parent_task);
}
}
@@ -1388,6 +1631,19 @@ ieee80211_resume_all(struct ieee80211com *ic)
IEEE80211_UNLOCK(ic);
}
+/*
+ * Restart all vap's running on a device.
+ */
+void
+ieee80211_restart_all(struct ieee80211com *ic)
+{
+ /*
+ * NB: do not use ieee80211_runtask here, we will
+ * block & drain net80211 taskqueue.
+ */
+ taskqueue_enqueue(taskqueue_thread, &ic->ic_restart_task);
+}
+
void
ieee80211_beacon_miss(struct ieee80211com *ic)
{
@@ -1405,10 +1661,10 @@ beacon_miss(void *arg, int npending)
struct ieee80211com *ic = arg;
struct ieee80211vap *vap;
- /* XXX locking */
+ IEEE80211_LOCK(ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
/*
- * We only pass events through for sta vap's in RUN state;
+ * 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.
*/
@@ -1417,18 +1673,21 @@ beacon_miss(void *arg, int npending)
vap->iv_bmiss != NULL)
vap->iv_bmiss(vap);
}
+ IEEE80211_UNLOCK(ic);
}
static void
beacon_swmiss(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
+ struct ieee80211com *ic = vap->iv_ic;
- if (vap->iv_state != IEEE80211_S_RUN)
- return;
-
- /* XXX Call multiple times if npending > zero? */
- vap->iv_bmiss(vap);
+ IEEE80211_LOCK(ic);
+ if (vap->iv_state >= IEEE80211_S_RUN) {
+ /* XXX Call multiple times if npending > zero? */
+ vap->iv_bmiss(vap);
+ }
+ IEEE80211_UNLOCK(ic);
}
/*
@@ -1442,8 +1701,9 @@ ieee80211_swbmiss(void *arg)
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
- /* XXX sleep state? */
- KASSERT(vap->iv_state == IEEE80211_S_RUN,
+ IEEE80211_LOCK_ASSERT(ic);
+
+ KASSERT(vap->iv_state >= IEEE80211_S_RUN,
("wrong state %d", vap->iv_state));
if (ic->ic_flags & IEEE80211_F_SCAN) {
@@ -1582,7 +1842,7 @@ ieee80211_cac_completeswitch(struct ieee80211vap *vap0)
ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
- if (vap->iv_state == IEEE80211_S_CAC)
+ if (vap->iv_state == IEEE80211_S_CAC && vap != vap0)
ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
IEEE80211_UNLOCK(ic);
}
@@ -1612,6 +1872,7 @@ markwaiting(struct ieee80211vap *vap0)
if (vap->iv_state != IEEE80211_S_INIT) {
/* NB: iv_newstate may drop the lock */
vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
+ IEEE80211_LOCK_ASSERT(ic);
vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
}
}
@@ -1646,6 +1907,7 @@ wakeupwaiting(struct ieee80211vap *vap0)
vap->iv_newstate(vap,
vap->iv_opmode == IEEE80211_M_STA ?
IEEE80211_S_SCAN : IEEE80211_S_RUN, 0);
+ IEEE80211_LOCK_ASSERT(ic);
}
}
}
@@ -1670,12 +1932,19 @@ ieee80211_newstate_cb(void *xvap, int npending)
* We have been requested to drop back to the INIT before
* proceeding to the new state.
*/
+ /* Deny any state changes while we are here. */
+ vap->iv_nstate = IEEE80211_S_INIT;
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;
+ ieee80211_state_name[vap->iv_nstate], arg);
+ vap->iv_newstate(vap, vap->iv_nstate, 0);
+ IEEE80211_LOCK_ASSERT(ic);
+ vap->iv_flags_ext &= ~(IEEE80211_FEXT_REINIT |
+ IEEE80211_FEXT_STATEWAIT);
+ /* enqueue new state transition after cancel_scan() task */
+ ieee80211_new_state_locked(vap, nstate, arg);
+ goto done;
}
ostate = vap->iv_state;
@@ -1696,6 +1965,7 @@ ieee80211_newstate_cb(void *xvap, int npending)
ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg);
rc = vap->iv_newstate(vap, nstate, arg);
+ IEEE80211_LOCK_ASSERT(ic);
vap->iv_flags_ext &= ~IEEE80211_FEXT_STATEWAIT;
if (rc != 0) {
/* State transition failed */
@@ -1722,7 +1992,10 @@ ieee80211_newstate_cb(void *xvap, int npending)
* (i.e. coming out of power save mode).
*/
vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- if_start(vap->iv_ifp);
+
+ /*
+ * XXX TODO Kick-start a VAP queue - this should be a method!
+ */
/* bring up any vaps waiting on us */
wakeupwaiting(vap);
@@ -1735,8 +2008,9 @@ ieee80211_newstate_cb(void *xvap, int npending)
*/
ieee80211_scan_flush(vap);
- /* XXX NB: cast for altq */
- ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap);
+ /*
+ * XXX TODO: ic/vap queue flush
+ */
}
done:
IEEE80211_UNLOCK(ic);
@@ -1769,7 +2043,7 @@ done:
* is usually a mistake and indicates lack of proper integration
* with the net80211 layer.
*/
-static int
+int
ieee80211_new_state_locked(struct ieee80211vap *vap,
enum ieee80211_state nstate, int arg)
{
@@ -1781,11 +2055,22 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
IEEE80211_LOCK_ASSERT(ic);
if (vap->iv_flags_ext & IEEE80211_FEXT_STATEWAIT) {
- if (vap->iv_nstate == IEEE80211_S_INIT) {
+ if (vap->iv_nstate == IEEE80211_S_INIT ||
+ ((vap->iv_state == IEEE80211_S_INIT ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_REINIT)) &&
+ vap->iv_nstate == IEEE80211_S_SCAN &&
+ nstate > IEEE80211_S_SCAN)) {
/*
- * XXX The vap is being stopped, do no allow any other
- * state changes until this is completed.
+ * XXX The vap is being stopped/started,
+ * do not allow any other state changes
+ * until this is completed.
*/
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s -> %s (%s) transition discarded\n",
+ __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate],
+ ieee80211_state_name[vap->iv_nstate]);
return -1;
} else if (vap->iv_state != vap->iv_nstate) {
#if 0
diff --git a/freebsd/sys/net80211/ieee80211_proto.h b/freebsd/sys/net80211/ieee80211_proto.h
index b2ed3e80..c4d5e7b0 100644
--- a/freebsd/sys/net80211/ieee80211_proto.h
+++ b/freebsd/sys/net80211/ieee80211_proto.h
@@ -47,49 +47,45 @@ enum ieee80211_state {
#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 *mgt_subtype_name[];
+extern const char *ctl_subtype_name[];
extern const char *ieee80211_phymode_name[IEEE80211_MODE_MAX];
extern const int ieee80211_opcap[IEEE80211_OPMODE_MAX];
+static __inline const char *
+ieee80211_mgt_subtype_name(uint8_t subtype)
+{
+ return mgt_subtype_name[(subtype & IEEE80211_FC0_SUBTYPE_MASK) >>
+ IEEE80211_FC0_SUBTYPE_SHIFT];
+}
+
+static __inline const char *
+ieee80211_ctl_subtype_name(uint8_t subtype)
+{
+ return ctl_subtype_name[(subtype & IEEE80211_FC0_SUBTYPE_MASK) >>
+ IEEE80211_FC0_SUBTYPE_SHIFT];
+}
+
+const char *ieee80211_reason_to_string(uint16_t);
+
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_promisc(struct ieee80211vap *, bool);
+void ieee80211_allmulti(struct ieee80211vap *, bool);
void ieee80211_syncflag(struct ieee80211vap *, int flag);
void ieee80211_syncflag_ht(struct ieee80211vap *, int flag);
+void ieee80211_syncflag_vht(struct ieee80211vap *, int flag);
void ieee80211_syncflag_ext(struct ieee80211vap *, int flag);
-#define IEEE80211_R_NF 0x0000001 /* global NF value valid */
-#define IEEE80211_R_RSSI 0x0000002 /* global RSSI value valid */
-#define IEEE80211_R_C_CHAIN 0x0000004 /* RX chain count valid */
-#define IEEE80211_R_C_NF 0x0000008 /* per-chain NF value valid */
-#define IEEE80211_R_C_RSSI 0x0000010 /* per-chain RSSI value valid */
-#define IEEE80211_R_C_EVM 0x0000020 /* per-chain EVM valid */
-#define IEEE80211_R_C_HT40 0x0000040 /* RX'ed packet is 40mhz, pilots 4,5 valid */
-
-struct ieee80211_rx_stats {
- uint32_t r_flags; /* IEEE80211_R_* flags */
- uint8_t c_chain; /* number of RX chains involved */
- int16_t c_nf_ctl[IEEE80211_MAX_CHAINS]; /* per-chain NF */
- int16_t c_nf_ext[IEEE80211_MAX_CHAINS]; /* per-chain NF */
- int16_t c_rssi_ctl[IEEE80211_MAX_CHAINS]; /* per-chain RSSI */
- int16_t c_rssi_ext[IEEE80211_MAX_CHAINS]; /* per-chain RSSI */
- uint8_t nf; /* global NF */
- uint8_t rssi; /* global RSSI */
- uint8_t evm[IEEE80211_MAX_CHAINS][IEEE80211_MAX_EVM_PILOTS];
- /* per-chain, per-pilot EVM values */
-};
-
#define ieee80211_input(ni, m, rssi, nf) \
- ((ni)->ni_vap->iv_input(ni, m, rssi, nf))
+ ((ni)->ni_vap->iv_input(ni, m, NULL, rssi, nf))
int ieee80211_input_all(struct ieee80211com *, struct mbuf *, int, int);
-int ieee80211_input_mimo(struct ieee80211_node *, struct mbuf *,
- struct ieee80211_rx_stats *);
-int ieee80211_input_mimo_all(struct ieee80211com *, struct mbuf *,
- struct ieee80211_rx_stats *);
+int ieee80211_input_mimo(struct ieee80211_node *, struct mbuf *);
+int ieee80211_input_mimo_all(struct ieee80211com *, struct mbuf *);
struct ieee80211_bpf_params;
int ieee80211_mgmt_output(struct ieee80211_node *, struct mbuf *, int,
@@ -97,17 +93,23 @@ int ieee80211_mgmt_output(struct ieee80211_node *, struct mbuf *, int,
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);
+ const struct sockaddr *, struct route *ro);
+int ieee80211_vap_pkt_send_dest(struct ieee80211vap *, struct mbuf *,
+ struct ieee80211_node *);
+int ieee80211_raw_output(struct ieee80211vap *, struct ieee80211_node *,
+ struct mbuf *, const struct ieee80211_bpf_params *);
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_vap_transmit(struct ifnet *ifp, struct mbuf *m);
+void ieee80211_vap_qflush(struct ifnet *ifp);
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 *);
+void ieee80211_free_mbuf(struct mbuf *);
int ieee80211_send_mgmt(struct ieee80211_node *, int, int);
struct ieee80211_appie;
int ieee80211_send_probereq(struct ieee80211_node *ni,
@@ -115,6 +117,11 @@ int ieee80211_send_probereq(struct ieee80211_node *ni,
const uint8_t da[IEEE80211_ADDR_LEN],
const uint8_t bssid[IEEE80211_ADDR_LEN],
const uint8_t *ssid, size_t ssidlen);
+struct mbuf * ieee80211_ff_encap1(struct ieee80211vap *, struct mbuf *,
+ const struct ether_header *);
+void ieee80211_tx_complete(struct ieee80211_node *,
+ struct mbuf *, int);
+
/*
* The formation of ProbeResponse frames requires guidance to
* deal with legacy clients. When the client is identified as
@@ -134,11 +141,14 @@ struct mbuf *ieee80211_alloc_cts(struct ieee80211com *,
uint8_t *ieee80211_add_rates(uint8_t *, const struct ieee80211_rateset *);
uint8_t *ieee80211_add_xrates(uint8_t *, const struct ieee80211_rateset *);
+uint8_t *ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int);
uint8_t *ieee80211_add_wpa(uint8_t *, const struct ieee80211vap *);
uint8_t *ieee80211_add_rsn(uint8_t *, const struct ieee80211vap *);
uint8_t *ieee80211_add_qos(uint8_t *, const struct ieee80211_node *);
uint16_t ieee80211_getcapinfo(struct ieee80211vap *,
struct ieee80211_channel *);
+struct ieee80211_wme_state;
+uint8_t * ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme);
void ieee80211_reset_erp(struct ieee80211com *);
void ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
@@ -218,7 +228,7 @@ struct ieee80211_aclator {
int (*iac_attach)(struct ieee80211vap *);
void (*iac_detach)(struct ieee80211vap *);
int (*iac_check)(struct ieee80211vap *,
- const uint8_t mac[IEEE80211_ADDR_LEN]);
+ const struct ieee80211_frame *wh);
int (*iac_add)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
int (*iac_remove)(struct ieee80211vap *,
@@ -265,10 +275,10 @@ struct chanAccParams {
struct ieee80211_wme_state {
u_int wme_flags;
-#define WME_F_AGGRMODE 0x00000001 /* STATUS: WME agressive mode */
+#define WME_F_AGGRMODE 0x00000001 /* STATUS: WME aggressive 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 */
+ u_int wme_hipri_switch_thresh;/* aggressive mode switch thresh */
+ u_int wme_hipri_switch_hysteresis;/* aggressive mode switch hysteresis */
struct wmeParams wme_params[4]; /* from assoc resp for each AC*/
struct chanAccParams wme_wmeChanParams; /* WME params applied to self */
@@ -313,10 +323,13 @@ 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_restart_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);
+int ieee80211_new_state_locked(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);
@@ -347,11 +360,12 @@ struct ieee80211_beacon_offsets {
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_quiet; /* start of Quiet element */
uint8_t *bo_meshconf; /* start of MESHCONF element */
- uint8_t *bo_spare[3];
+ uint8_t *bo_vhtinfo; /* start of VHT info element (XXX VHTCAP?) */
+ uint8_t *bo_spare[2];
};
-struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *,
- struct ieee80211_beacon_offsets *);
+struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *);
/*
* Beacon frame updates are signaled through calls to iv_update_beacon
@@ -379,7 +393,7 @@ enum {
IEEE80211_BEACON_MESHCONF = 11, /* Mesh Configuration */
};
int ieee80211_beacon_update(struct ieee80211_node *,
- struct ieee80211_beacon_offsets *, struct mbuf *, int mcast);
+ struct mbuf *, int mcast);
void ieee80211_csa_startswitch(struct ieee80211com *,
struct ieee80211_channel *, int mode, int count);
diff --git a/freebsd/sys/net80211/ieee80211_radiotap.c b/freebsd/sys/net80211/ieee80211_radiotap.c
index 7bdb5d74..19780fc6 100644
--- a/freebsd/sys/net80211/ieee80211_radiotap.c
+++ b/freebsd/sys/net80211/ieee80211_radiotap.c
@@ -44,18 +44,30 @@ __FBSDID("$FreeBSD$");
#include <net/bpf.h>
#include <net/if.h>
-#include <net/if_llc.h>
+#include <net/if_var.h>
#include <net/if_media.h>
+#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
-static int radiotap_offset(struct ieee80211_radiotap_header *, int);
+static int radiotap_offset(struct ieee80211_radiotap_header *, int, 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)
{
+ ieee80211_radiotap_attachv(ic, th, tlen, 0, tx_radiotap,
+ rh, rlen, 0, rx_radiotap);
+}
+
+void
+ieee80211_radiotap_attachv(struct ieee80211com *ic,
+ struct ieee80211_radiotap_header *th,
+ int tlen, int n_tx_v, uint32_t tx_radiotap,
+ struct ieee80211_radiotap_header *rh,
+ int rlen, int n_rx_v, uint32_t rx_radiotap)
+{
#define B(_v) (1<<(_v))
int off;
@@ -65,12 +77,12 @@ ieee80211_radiotap_attach(struct ieee80211com *ic,
/* calculate offset to channel data */
off = -1;
if (tx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL))
- off = radiotap_offset(th, IEEE80211_RADIOTAP_CHANNEL);
+ off = radiotap_offset(th, n_tx_v, IEEE80211_RADIOTAP_CHANNEL);
else if (tx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL))
- off = radiotap_offset(th, IEEE80211_RADIOTAP_XCHANNEL);
+ off = radiotap_offset(th, n_tx_v, IEEE80211_RADIOTAP_XCHANNEL);
if (off == -1) {
- if_printf(ic->ic_ifp, "%s: no tx channel, radiotap 0x%x",
- __func__, tx_radiotap);
+ ic_printf(ic, "%s: no tx channel, radiotap 0x%x\n", __func__,
+ tx_radiotap);
/* NB: we handle this case but data will have no chan spec */
} else
ic->ic_txchan = ((uint8_t *) th) + off;
@@ -81,12 +93,12 @@ ieee80211_radiotap_attach(struct ieee80211com *ic,
/* calculate offset to channel data */
off = -1;
if (rx_radiotap & B(IEEE80211_RADIOTAP_CHANNEL))
- off = radiotap_offset(rh, IEEE80211_RADIOTAP_CHANNEL);
+ off = radiotap_offset(rh, n_rx_v, IEEE80211_RADIOTAP_CHANNEL);
else if (rx_radiotap & B(IEEE80211_RADIOTAP_XCHANNEL))
- off = radiotap_offset(rh, IEEE80211_RADIOTAP_XCHANNEL);
+ off = radiotap_offset(rh, n_rx_v, IEEE80211_RADIOTAP_XCHANNEL);
if (off == -1) {
- if_printf(ic->ic_ifp, "%s: no rx channel, radiotap 0x%x",
- __func__, rx_radiotap);
+ ic_printf(ic, "%s: no rx channel, radiotap 0x%x\n", __func__,
+ rx_radiotap);
/* NB: we handle this case but data will have no chan spec */
} else
ic->ic_rxchan = ((uint8_t *) rh) + off;
@@ -262,7 +274,8 @@ ieee80211_radiotap_rx_all(struct ieee80211com *ic, struct mbuf *m)
* known -1 is returned.
*/
static int
-radiotap_offset(struct ieee80211_radiotap_header *rh, int item)
+radiotap_offset(struct ieee80211_radiotap_header *rh,
+ int n_vendor_attributes, int item)
{
static const struct {
size_t align, width;
@@ -327,11 +340,17 @@ radiotap_offset(struct ieee80211_radiotap_header *rh, int item)
.align = sizeof(uint32_t),
.width = 2*sizeof(uint32_t),
},
+ [IEEE80211_RADIOTAP_MCS] = {
+ .align = sizeof(uint8_t),
+ .width = 3*sizeof(uint8_t),
+ },
};
uint32_t present = le32toh(rh->it_present);
int off, i;
off = sizeof(struct ieee80211_radiotap_header);
+ off += n_vendor_attributes * (sizeof(uint32_t));
+
for (i = 0; i < IEEE80211_RADIOTAP_EXT; i++) {
if ((present & (1<<i)) == 0)
continue;
diff --git a/freebsd/sys/net80211/ieee80211_radiotap.h b/freebsd/sys/net80211/ieee80211_radiotap.h
index b8a8b510..388d70ed 100644
--- a/freebsd/sys/net80211/ieee80211_radiotap.h
+++ b/freebsd/sys/net80211/ieee80211_radiotap.h
@@ -54,6 +54,12 @@
#define IEEE80211_RADIOTAP_HDRLEN 64 /* XXX deprecated */
+struct ieee80211_radiotap_vendor_header {
+ uint8_t vh_oui[3]; /* 3 byte vendor OUI */
+ uint8_t vh_sub_ns; /* Sub namespace of this section */
+ uint16_t vh_skip_len; /* Length of this vendor section */
+} __packed;
+
/*
* The radio capture header precedes the 802.11 header.
*
@@ -188,8 +194,21 @@ enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_ANTENNA = 11,
IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
- /* NB: gap for netbsd definitions */
+ /*
+ * 14-17 are from Linux, they overlap the netbsd-specific
+ * fields.
+ */
+ IEEE80211_RADIOTAP_RX_FLAGS = 14,
+ IEEE80211_RADIOTAP_TX_FLAGS = 15,
+ IEEE80211_RADIOTAP_RTS_RETRIES = 16,
+ IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+
IEEE80211_RADIOTAP_XCHANNEL = 18,
+ IEEE80211_RADIOTAP_MCS = 19,
+ IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
+
+ IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
+ IEEE80211_RADIOTAP_VENDOREXT = 30,
IEEE80211_RADIOTAP_EXT = 31,
};
diff --git a/freebsd/sys/net80211/ieee80211_ratectl.c b/freebsd/sys/net80211/ieee80211_ratectl.c
index 650371a5..57f890fb 100644
--- a/freebsd/sys/net80211/ieee80211_ratectl.c
+++ b/freebsd/sys/net80211/ieee80211_ratectl.c
@@ -30,11 +30,15 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/kernel.h>
+#include <sys/sbuf.h>
#include <sys/systm.h>
#include <sys/socket.h>
+#include <sys/malloc.h>
#include <net/if.h>
#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/route.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_ratectl.h>
@@ -67,12 +71,52 @@ ieee80211_ratectl_unregister(int type)
ratectls[type] = NULL;
}
+static void
+ieee80211_ratectl_sysctl_stats_node_iter(void *arg, struct ieee80211_node *ni)
+{
+
+ struct sbuf *sb = (struct sbuf *) arg;
+ sbuf_printf(sb, "MAC: %6D\n", ni->ni_macaddr, ":");
+ ieee80211_ratectl_node_stats(ni, sb);
+ sbuf_printf(sb, "\n");
+}
+
+static int
+ieee80211_ratectl_sysctl_stats(SYSCTL_HANDLER_ARGS)
+{
+ struct ieee80211vap *vap = arg1;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct sbuf sb;
+ int error;
+
+ error = sysctl_wire_old_buffer(req, 0);
+ if (error)
+ return (error);
+ sbuf_new_for_sysctl(&sb, NULL, 8, req);
+ sbuf_clear_flags(&sb, SBUF_INCLUDENUL);
+
+ IEEE80211_LOCK(ic);
+ ieee80211_iterate_nodes(&ic->ic_sta,
+ ieee80211_ratectl_sysctl_stats_node_iter,
+ &sb);
+ IEEE80211_UNLOCK(ic);
+
+ error = sbuf_finish(&sb);
+ sbuf_delete(&sb);
+ return (error);
+}
+
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);
+
+ /* Attach generic stats sysctl */
+ SYSCTL_ADD_PROC(vap->iv_sysctl, SYSCTL_CHILDREN(vap->iv_oid), OID_AUTO,
+ "rate_stats", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, vap,
+ 0, ieee80211_ratectl_sysctl_stats, "A", "ratectl node stats");
}
void
diff --git a/freebsd/sys/net80211/ieee80211_ratectl.h b/freebsd/sys/net80211/ieee80211_ratectl.h
index be81781c..ccb17ef6 100644
--- a/freebsd/sys/net80211/ieee80211_ratectl.h
+++ b/freebsd/sys/net80211/ieee80211_ratectl.h
@@ -34,8 +34,49 @@ enum ieee80211_ratealgs {
IEEE80211_RATECTL_MAX
};
-#define IEEE80211_RATECTL_TX_SUCCESS 1
-#define IEEE80211_RATECTL_TX_FAILURE 0
+/* used fields for tx_complete() events */
+#define IEEE80211_RATECTL_STATUS_PKTLEN 0x00000001
+#define IEEE80211_RATECTL_STATUS_FINAL_RATE 0x00000002
+#define IEEE80211_RATECTL_STATUS_SHORT_RETRY 0x00000004
+#define IEEE80211_RATECTL_STATUS_LONG_RETRY 0x00000008
+#define IEEE80211_RATECTL_STATUS_RSSI 0x00000010
+
+/* failure reason */
+enum ieee80211_ratectl_tx_fail_reason {
+ IEEE80211_RATECTL_TX_SUCCESS = 0,
+ IEEE80211_RATECTL_TX_FAIL_SHORT = 1, /* too many RTS retries */
+ IEEE80211_RATECTL_TX_FAIL_LONG = 2, /* too many retries */
+ IEEE80211_RATECTL_TX_FAIL_EXPIRED = 3, /* lifetime expired */
+ IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED = 4, /* another reason */
+};
+#define IEEE80211_RATECTL_TX_FAIL_MAX \
+ (IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED + 1)
+
+struct ieee80211_ratectl_tx_status {
+ uint32_t flags; /* mark used fields */
+ enum ieee80211_ratectl_tx_fail_reason status; /* Tx status */
+
+ int pktlen; /* frame length */
+ int final_rate; /* transmission rate */
+ uint_fast8_t short_retries; /* RTS/CTS retries */
+ uint_fast8_t long_retries; /* ACK retries */
+ int8_t rssi; /* ACK RSSI */
+
+ uint8_t spare[15]; /* for future use */
+};
+
+/* used fields for tx_update() events */
+#define IEEE80211_RATECTL_TX_STATS_NODE 0x00000001
+#define IEEE80211_RATECTL_TX_STATS_RETRIES 0x00000002
+
+struct ieee80211_ratectl_tx_stats {
+ uint32_t flags; /* mark used fields */
+
+ struct ieee80211_node *ni; /* receiver */
+ int nframes; /* transmitted frames */
+ int nsuccess; /* ACKed frames */
+ int nretries; /* number of retries */
+};
struct ieee80211_ratectl {
const char *ir_name;
@@ -46,13 +87,12 @@ struct ieee80211_ratectl {
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_tx_complete)(const struct ieee80211_node *,
+ const struct ieee80211_ratectl_tx_status *);
+ void (*ir_tx_update)(struct ieee80211vap *,
+ struct ieee80211_ratectl_tx_stats *);
void (*ir_setinterval)(const struct ieee80211vap *, int);
+ void (*ir_node_stats)(struct ieee80211_node *ni, struct sbuf *s);
};
void ieee80211_ratectl_register(int, const struct ieee80211_ratectl *);
@@ -62,13 +102,13 @@ void ieee80211_ratectl_set(struct ieee80211vap *, int);
MALLOC_DECLARE(M_80211_RATECTL);
-static void __inline
+static __inline void
ieee80211_ratectl_deinit(struct ieee80211vap *vap)
{
vap->iv_rate->ir_deinit(vap);
}
-static void __inline
+static __inline void
ieee80211_ratectl_node_init(struct ieee80211_node *ni)
{
const struct ieee80211vap *vap = ni->ni_vap;
@@ -76,7 +116,7 @@ ieee80211_ratectl_node_init(struct ieee80211_node *ni)
vap->iv_rate->ir_node_init(ni);
}
-static void __inline
+static __inline void
ieee80211_ratectl_node_deinit(struct ieee80211_node *ni)
{
const struct ieee80211vap *vap = ni->ni_vap;
@@ -92,26 +132,38 @@ ieee80211_ratectl_rate(struct ieee80211_node *ni, void *arg, uint32_t iarg)
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)
+static __inline void
+ieee80211_ratectl_tx_complete(const struct ieee80211_node *ni,
+ const struct ieee80211_ratectl_tx_status *status)
{
- vap->iv_rate->ir_tx_complete(vap, ni, status, arg1, arg2);
+ const struct ieee80211vap *vap = ni->ni_vap;
+
+ vap->iv_rate->ir_tx_complete(ni, status);
}
-static void __inline
-ieee80211_ratectl_tx_update(const struct ieee80211vap *vap,
- const struct ieee80211_node *ni, void *arg1, void *arg2, void *arg3)
+static __inline void
+ieee80211_ratectl_tx_update(struct ieee80211vap *vap,
+ struct ieee80211_ratectl_tx_stats *stats)
{
if (vap->iv_rate->ir_tx_update == NULL)
return;
- vap->iv_rate->ir_tx_update(vap, ni, arg1, arg2, arg3);
+ vap->iv_rate->ir_tx_update(vap, stats);
}
-static void __inline
+static __inline void
ieee80211_ratectl_setinterval(const struct ieee80211vap *vap, int msecs)
{
if (vap->iv_rate->ir_setinterval == NULL)
return;
vap->iv_rate->ir_setinterval(vap, msecs);
}
+
+static __inline void
+ieee80211_ratectl_node_stats(struct ieee80211_node *ni, struct sbuf *s)
+{
+ const struct ieee80211vap *vap = ni->ni_vap;
+
+ if (vap->iv_rate->ir_node_stats == NULL)
+ return;
+ vap->iv_rate->ir_node_stats(ni, s);
+}
diff --git a/freebsd/sys/net80211/ieee80211_ratectl_none.c b/freebsd/sys/net80211/ieee80211_ratectl_none.c
index 7d6743dd..24324c5f 100644
--- a/freebsd/sys/net80211/ieee80211_ratectl_none.c
+++ b/freebsd/sys/net80211/ieee80211_ratectl_none.c
@@ -31,13 +31,16 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/local/opt_wlan.h>
#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_media.h>
+#include <net/ethernet.h>
#ifdef INET
#include <netinet/in.h>
@@ -55,7 +58,7 @@ none_init(struct ieee80211vap *vap)
static void
none_deinit(struct ieee80211vap *vap)
{
- free(vap->iv_rs, M_80211_RATECTL);
+ IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
}
static void
@@ -79,15 +82,14 @@ none_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
}
static void
-none_tx_complete(const struct ieee80211vap *vap,
- const struct ieee80211_node *ni, int ok,
- void *arg1, void *arg2 __unused)
+none_tx_complete(const struct ieee80211_node *ni,
+ const struct ieee80211_ratectl_tx_status *status)
{
}
static void
-none_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni,
- void *arg1, void *arg2, void *arg3)
+none_tx_update(struct ieee80211vap *vap,
+ struct ieee80211_ratectl_tx_stats *stats)
{
}
diff --git a/freebsd/sys/net80211/ieee80211_regdomain.c b/freebsd/sys/net80211/ieee80211_regdomain.c
index a6696d95..a1de5e41 100644
--- a/freebsd/sys/net80211/ieee80211_regdomain.c
+++ b/freebsd/sys/net80211/ieee80211_regdomain.c
@@ -36,11 +36,13 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
-
+#include <sys/malloc.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
+#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
@@ -69,10 +71,7 @@ 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;
@@ -83,7 +82,7 @@ void
ieee80211_regdomain_detach(struct ieee80211com *ic)
{
if (ic->ic_countryie != NULL) {
- free(ic->ic_countryie, M_80211_NODE_IE);
+ IEEE80211_FREE(ic->ic_countryie, M_80211_NODE_IE);
ic->ic_countryie = NULL;
}
}
@@ -98,17 +97,14 @@ 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;
-}
+static const uint8_t def_chan_2ghz[] =
+ { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
+static const uint8_t def_chan_5ghz_band1[] =
+ { 36, 40, 44, 48, 52, 56, 60, 64 };
+static const uint8_t def_chan_5ghz_band2[] =
+ { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 };
+static const uint8_t def_chan_5ghz_band3[] =
+ { 149, 153, 157, 161 };
/*
* Setup the channel list for the specified regulatory domain,
@@ -120,29 +116,34 @@ int
ieee80211_init_channels(struct ieee80211com *ic,
const struct ieee80211_regdomain *rd, const uint8_t bands[])
{
- int i;
+ struct ieee80211_channel *chans = ic->ic_channels;
+ int *nchans = &ic->ic_nchans;
+ int ht40;
/* XXX just do something for now */
- ic->ic_nchans = 0;
+ ht40 = !!(ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40);
+ *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);
- }
+ isset(bands, IEEE80211_MODE_11G) ||
+ isset(bands, IEEE80211_MODE_11NG)) {
+ int nchan = nitems(def_chan_2ghz);
+ if (!(rd != NULL && rd->ecm))
+ nchan -= 3;
+
+ ieee80211_add_channel_list_2ghz(chans, IEEE80211_CHAN_MAX,
+ nchans, def_chan_2ghz, nchan, bands, ht40);
}
- 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 (isset(bands, IEEE80211_MODE_11A) ||
+ isset(bands, IEEE80211_MODE_11NA)) {
+ ieee80211_add_channel_list_5ghz(chans, IEEE80211_CHAN_MAX,
+ nchans, def_chan_5ghz_band1, nitems(def_chan_5ghz_band1),
+ bands, ht40);
+ ieee80211_add_channel_list_5ghz(chans, IEEE80211_CHAN_MAX,
+ nchans, def_chan_5ghz_band2, nitems(def_chan_5ghz_band2),
+ bands, ht40);
+ ieee80211_add_channel_list_5ghz(chans, IEEE80211_CHAN_MAX,
+ nchans, def_chan_5ghz_band3, nitems(def_chan_5ghz_band3),
+ bands, ht40);
}
if (rd != NULL)
ic->ic_regdomain = *rd;
@@ -242,19 +243,19 @@ ieee80211_alloc_countryie(struct ieee80211com *ic)
struct ieee80211_country_ie *ie;
int i, skip, nruns;
- aie = malloc(IEEE80211_COUNTRY_MAX_SIZE, M_80211_NODE_IE,
- M_NOWAIT | M_ZERO);
+ aie = IEEE80211_MALLOC(IEEE80211_COUNTRY_MAX_SIZE, M_80211_NODE_IE,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (aie == NULL) {
- if_printf(ic->ic_ifp,
- "%s: unable to allocate memory for country ie\n", __func__);
+ ic_printf(ic, "%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);
+ ic_printf(ic, "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];
@@ -264,7 +265,7 @@ ieee80211_alloc_countryie(struct ieee80211com *ic)
* Indoor/Outdoor portion of country string:
* 'I' indoor only
* 'O' outdoor only
- * ' ' all enviroments
+ * ' ' all environments
*/
ie->cc[2] = (rd->location == 'I' ? 'I' :
rd->location == 'O' ? 'O' : ' ');
@@ -291,7 +292,7 @@ ieee80211_alloc_countryie(struct ieee80211com *ic)
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, "
+ ic_printf(ic, "%s: country ie too big, "
"runs > max %d, truncating\n",
__func__, IEEE80211_COUNTRY_MAX_BANDS);
/* XXX stat? fail? */
@@ -428,13 +429,13 @@ ieee80211_setregdomain(struct ieee80211vap *vap,
memset(&ic->ic_channels[ic->ic_nchans], 0,
(IEEE80211_CHAN_MAX - ic->ic_nchans) *
sizeof(struct ieee80211_channel));
- ieee80211_media_init(ic);
+ ieee80211_chan_init(ic);
/*
* Invalidate channel-related state.
*/
if (ic->ic_countryie != NULL) {
- free(ic->ic_countryie, M_80211_NODE_IE);
+ IEEE80211_FREE(ic->ic_countryie, M_80211_NODE_IE);
ic->ic_countryie = NULL;
}
ieee80211_scan_flush(vap);
diff --git a/freebsd/sys/net80211/ieee80211_regdomain.h b/freebsd/sys/net80211/ieee80211_regdomain.h
index 8942dd98..cfb31463 100644
--- a/freebsd/sys/net80211/ieee80211_regdomain.h
+++ b/freebsd/sys/net80211/ieee80211_regdomain.h
@@ -258,6 +258,17 @@ enum RegdomainCode {
SKU_SR9 = 0x0298, /* Ubiquiti SR9 (900MHz/GSM) */
SKU_XR9 = 0x0299, /* Ubiquiti XR9 (900MHz/GSM) */
SKU_GZ901 = 0x029a, /* Zcomax GZ-901 (900MHz/GSM) */
+ SKU_XC900M = 0x029b, /* Xagyl XC900M (900MHz/GSM) */
+ /*
+ * The XC900M by default uses the
+ * same mapping as the XR9. It
+ * can optionally use a slightly
+ * offset channel spacing (905MHz-
+ * 925MHz) versus the XR9 (907MHz-
+ * 922MHz), giving an extra channel.
+ * This requires a jumper on the
+ * NIC to be changed.
+ */
};
#if defined(__KERNEL__) || defined(_KERNEL)
diff --git a/freebsd/sys/net80211/ieee80211_rssadapt.c b/freebsd/sys/net80211/ieee80211_rssadapt.c
index cc3af217..80d06256 100644
--- a/freebsd/sys/net80211/ieee80211_rssadapt.c
+++ b/freebsd/sys/net80211/ieee80211_rssadapt.c
@@ -35,13 +35,17 @@
#include <rtems/bsd/local/opt_wlan.h>
#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
+#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_rssadapt.h>
@@ -85,9 +89,8 @@ 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_tx_complete(const struct ieee80211_node *,
+ const struct ieee80211_ratectl_tx_status *);
static void rssadapt_sysctlattach(struct ieee80211vap *,
struct sysctl_ctx_list *, struct sysctl_oid *);
@@ -130,8 +133,8 @@ rssadapt_init(struct ieee80211vap *vap)
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);
+ vap->iv_rs = rs = IEEE80211_MALLOC(sizeof(struct ieee80211_rssadapt),
+ M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (rs == NULL) {
if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
return;
@@ -144,7 +147,7 @@ rssadapt_init(struct ieee80211vap *vap)
static void
rssadapt_deinit(struct ieee80211vap *vap)
{
- free(vap->iv_rs, M_80211_RATECTL);
+ IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
}
static void
@@ -173,8 +176,8 @@ rssadapt_node_init(struct ieee80211_node *ni)
if (ni->ni_rctls == NULL) {
ni->ni_rctls = ra =
- malloc(sizeof(struct ieee80211_rssadapt_node),
- M_80211_RATECTL, M_NOWAIT|M_ZERO);
+ IEEE80211_MALLOC(sizeof(struct ieee80211_rssadapt_node),
+ M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (ra == NULL) {
if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
"structure\n");
@@ -202,7 +205,7 @@ static void
rssadapt_node_deinit(struct ieee80211_node *ni)
{
- free(ni->ni_rctls, M_80211_RATECTL);
+ IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
}
static __inline int
@@ -308,13 +311,21 @@ rssadapt_raise_rate(struct ieee80211_rssadapt_node *ra, int pktlen, int rssi)
}
static void
-rssadapt_tx_complete(const struct ieee80211vap *vap,
- const struct ieee80211_node *ni, int success, void *arg1, void *arg2)
+rssadapt_tx_complete(const struct ieee80211_node *ni,
+ const struct ieee80211_ratectl_tx_status *status)
{
struct ieee80211_rssadapt_node *ra = ni->ni_rctls;
- int pktlen = *(int *)arg1, rssi = *(int *)arg2;
+ int pktlen, rssi;
- if (success) {
+ if ((status->flags &
+ (IEEE80211_RATECTL_STATUS_PKTLEN|IEEE80211_RATECTL_STATUS_RSSI)) !=
+ (IEEE80211_RATECTL_STATUS_PKTLEN|IEEE80211_RATECTL_STATUS_RSSI))
+ return;
+
+ pktlen = status->pktlen;
+ rssi = status->rssi;
+
+ if (status->status == IEEE80211_RATECTL_TX_SUCCESS) {
ra->ra_nok++;
if ((ra->ra_rix + 1) < ra->ra_rates.rs_nrates &&
(ticks - ra->ra_last_raise) >= ra->ra_raise_interval)
diff --git a/freebsd/sys/net80211/ieee80211_scan.c b/freebsd/sys/net80211/ieee80211_scan.c
index 19becf8f..c3dfdb5e 100644
--- a/freebsd/sys/net80211/ieee80211_scan.c
+++ b/freebsd/sys/net80211/ieee80211_scan.c
@@ -37,45 +37,22 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/condvar.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
-#include <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)
+/* XXX until it's implemented as attach ops */
+#include <net80211/ieee80211_scan_sw.h>
-/*
- * 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)
+#include <net/bpf.h>
/*
* Roaming-related defaults. RSSI thresholds are as returned by the
@@ -93,56 +70,32 @@ struct scan_state {
#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");
+#define ROAM_MCS_11AC_DEFAULT (1 | IEEE80211_RATE_MCS) /* 11ac bss; XXX not used yet */
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;
+ /*
+ * If there's no scan method pointer, attach the
+ * swscan set as a default.
+ */
+ if (ic->ic_scan_methods == NULL)
+ ieee80211_swscan_attach(ic);
+ else
+ ic->ic_scan_methods->sc_attach(ic);
}
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);
- }
+ /*
+ * Ideally we'd do the ss_ops detach call here;
+ * but then sc_detach() would need to be split in two.
+ *
+ * I'll do that later.
+ */
+ ic->ic_scan_methods->sc_detach(ic);
}
static const struct ieee80211_roamparam defroam[IEEE80211_MODE_MAX] = {
@@ -166,17 +119,26 @@ static const struct ieee80211_roamparam defroam[IEEE80211_MODE_MAX] = {
.rate = ROAM_MCS_11N_DEFAULT },
[IEEE80211_MODE_11NG] = { .rssi = ROAM_RSSI_11B_DEFAULT,
.rate = ROAM_MCS_11N_DEFAULT },
+ [IEEE80211_MODE_VHT_2GHZ] = { .rssi = ROAM_RSSI_11B_DEFAULT,
+ .rate = ROAM_MCS_11AC_DEFAULT },
+ [IEEE80211_MODE_VHT_5GHZ] = { .rssi = ROAM_RSSI_11A_DEFAULT,
+ .rate = ROAM_MCS_11AC_DEFAULT },
+
};
void
ieee80211_scan_vattach(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
+
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));
+
+ ic->ic_scan_methods->sc_vattach(vap);
}
void
@@ -187,11 +149,10 @@ ieee80211_scan_vdetach(struct ieee80211vap *vap)
IEEE80211_LOCK(ic);
ss = ic->ic_scan;
+
+ ic->ic_scan_methods->sc_vdetach(vap);
+
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;
@@ -262,8 +223,8 @@ ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
* ensure later callbacks find ss_ops set to properly
* reflect current operating mode.
*/
-static void
-scan_update_locked(struct ieee80211vap *vap,
+void
+ieee80211_scan_update_locked(struct ieee80211vap *vap,
const struct ieee80211_scanner *scan)
{
struct ieee80211com *ic = vap->iv_ic;
@@ -308,26 +269,6 @@ scan_update_locked(struct ieee80211vap *vap,
}
}
-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)
{
@@ -340,14 +281,14 @@ ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
const struct ieee80211_channel *c = ss->ss_chans[i];
printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c),
- channel_type(c));
+ ieee80211_channel_type_char(c));
sep = ", ";
}
}
#ifdef IEEE80211_DEBUG
-static void
-scan_dump(struct ieee80211_scan_state *ss)
+void
+ieee80211_scan_dump(struct ieee80211_scan_state *ss)
{
struct ieee80211vap *vap = ss->ss_vap;
@@ -358,8 +299,8 @@ scan_dump(struct ieee80211_scan_state *ss)
}
#endif /* IEEE80211_DEBUG */
-static void
-copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss,
+void
+ieee80211_scan_copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss,
int nssid, const struct ieee80211_scan_ssid ssids[])
{
if (nssid > IEEE80211_SCAN_MAX_SSID) {
@@ -376,86 +317,13 @@ copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss,
/*
* 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);
- if (flags & IEEE80211_SCAN_BGSCAN)
- ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
-
- /* 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);
- }
- return 1;
- } else {
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: %s scan already in progress\n", __func__,
- ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
- }
- return 0;
-}
-
-/*
- * 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;
+ struct ieee80211com *ic = vap->iv_ic;
scan = ieee80211_scanner_get(vap->iv_opmode);
if (scan == NULL) {
@@ -466,12 +334,8 @@ ieee80211_start_scan(struct ieee80211vap *vap, int flags,
return 0;
}
- IEEE80211_LOCK(ic);
- result = start_scan_locked(scan, vap, flags, duration,
+ return ic->ic_scan_methods->sc_start_scan(scan, vap, flags, duration,
mindwell, maxdwell, nssid, ssids);
- IEEE80211_UNLOCK(ic);
-
- return result;
}
/*
@@ -518,49 +382,18 @@ ieee80211_check_scan(struct ieee80211vap *vap, int flags,
/* 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,
+
+ /*
+ * XXX TODO: separate things out a bit better.
+ */
+ ieee80211_scan_update_locked(vap, scan);
+
+ result = ic->ic_scan_methods->sc_check_scan(scan, vap, flags, duration,
mindwell, maxdwell, nssid, ssids);
+
IEEE80211_UNLOCK(ic);
- return result;
+ return (result);
}
/*
@@ -584,9 +417,10 @@ 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;
+ // IEEE80211_UNLOCK_ASSERT(sc);
+
scan = ieee80211_scanner_get(vap->iv_opmode);
if (scan == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
@@ -596,84 +430,12 @@ ieee80211_bg_scan(struct ieee80211vap *vap, int flags)
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);
+ /*
+ * XXX TODO: pull apart the bgscan logic into whatever
+ * belongs here and whatever belongs in the software
+ * scanner.
+ */
+ return (ic->ic_scan_methods->sc_bg_scan(scan, vap, flags));
}
/*
@@ -683,71 +445,41 @@ 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);
+ ic->ic_scan_methods->sc_cancel_scan(vap);
}
/*
* Cancel any scan currently going on.
+ *
+ * This is called during normal 802.11 data path to cancel
+ * a scan so a newly arrived normal data packet can be sent.
*/
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);
+ ic->ic_scan_methods->sc_cancel_anyscan(vap);
}
/*
- * Public access to scan_next for drivers that manage
- * scanning themselves (e.g. for firmware-based devices).
+ * Manually switch to the next channel in the channel list.
+ * Provided 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);
+ ic->ic_scan_methods->sc_scan_next(vap);
}
/*
- * Public access to scan_next for drivers that are not able to scan single
- * channels (e.g. for firmware-based devices).
+ * Manually stop a scan that is currently running.
+ * Provided for drivers that are not able to scan single channels
+ * (e.g. for firmware-based devices).
*/
void
ieee80211_scan_done(struct ieee80211vap *vap)
@@ -755,15 +487,19 @@ ieee80211_scan_done(struct ieee80211vap *vap)
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__);
+
IEEE80211_LOCK(ic);
ss = ic->ic_scan;
ss->ss_next = ss->ss_last; /* all channels are complete */
- scan_signal(ss);
+
+ ic->ic_scan_methods->sc_scan_done(vap);
+
IEEE80211_UNLOCK(ic);
}
/*
- * Probe the curent channel, if allowed, while scanning.
+ * Probe the current 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
@@ -773,279 +509,13 @@ 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
+ ic->ic_scan_methods->sc_scan_probe_curchan(vap, force);
}
#ifdef IEEE80211_DEBUG
@@ -1074,16 +544,15 @@ dump_country(const uint8_t *ie)
printf("]");
}
-static void
-dump_probe_beacon(uint8_t subtype, int isnew,
+void
+ieee80211_scan_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_mgt_subtype_name(subtype), sp->chan, sp->bchan);
ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
printf(" rssi %d\n", rssi);
@@ -1102,49 +571,15 @@ dump_probe_beacon(uint8_t subtype, int isnew,
*/
void
ieee80211_add_scan(struct ieee80211vap *vap,
+ struct ieee80211_channel *curchan,
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);
- }
- }
+ return (ic->ic_scan_methods->sc_add_scan(vap, curchan, sp, wh, subtype,
+ rssi, noise));
}
/*
diff --git a/freebsd/sys/net80211/ieee80211_scan.h b/freebsd/sys/net80211/ieee80211_scan.h
index 4c5e869a..a3c873df 100644
--- a/freebsd/sys/net80211/ieee80211_scan.h
+++ b/freebsd/sys/net80211/ieee80211_scan.h
@@ -80,6 +80,39 @@ struct ieee80211_scan_ssid {
#define IEEE80211_SCAN_MAX_SSID 1 /* max # ssid's to probe */
/*
+ * High-level implementation visible to ieee80211_scan.[ch].
+ *
+ * The default scanner (ieee80211_scan_sw.[ch]) implements a software
+ * driven scanner. Firmware driven scanning needs a different set of
+ * behaviours.
+ */
+struct ieee80211_scan_methods {
+ void (*sc_attach)(struct ieee80211com *);
+ void (*sc_detach)(struct ieee80211com *);
+ void (*sc_vattach)(struct ieee80211vap *);
+ void (*sc_vdetach)(struct ieee80211vap *);
+ void (*sc_set_scan_duration)(struct ieee80211vap *, u_int);
+ int (*sc_start_scan)(const struct ieee80211_scanner *,
+ struct ieee80211vap *, int, u_int, u_int, u_int, u_int,
+ const struct ieee80211_scan_ssid ssids[]);
+ int (*sc_check_scan)(const struct ieee80211_scanner *,
+ struct ieee80211vap *, int, u_int, u_int, u_int, u_int,
+ const struct ieee80211_scan_ssid ssids[]);
+ int (*sc_bg_scan)(const struct ieee80211_scanner *,
+ struct ieee80211vap *, int);
+ void (*sc_cancel_scan)(struct ieee80211vap *);
+ void (*sc_cancel_anyscan)(struct ieee80211vap *);
+ void (*sc_scan_next)(struct ieee80211vap *);
+ void (*sc_scan_done)(struct ieee80211vap *);
+ void (*sc_scan_probe_curchan)(struct ieee80211vap *, int);
+ void (*sc_add_scan)(struct ieee80211vap *,
+ struct ieee80211_channel *,
+ const struct ieee80211_scanparams *,
+ const struct ieee80211_frame *,
+ int, int, int);
+};
+
+/*
* 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
@@ -148,6 +181,7 @@ struct ieee80211_channel *ieee80211_scan_pickchannel(struct ieee80211com *, int)
struct ieee80211_scanparams;
void ieee80211_add_scan(struct ieee80211vap *,
+ struct ieee80211_channel *,
const struct ieee80211_scanparams *,
const struct ieee80211_frame *,
int subtype, int rssi, int noise);
@@ -213,9 +247,12 @@ struct ieee80211_scanparams {
uint8_t *ath;
uint8_t *tdma;
uint8_t *csa;
+ uint8_t *quiet;
uint8_t *meshid;
uint8_t *meshconf;
- uint8_t *spare[3];
+ uint8_t *vhtcap;
+ uint8_t *vhtopmode;
+ uint8_t *spare[1];
};
/*
@@ -272,6 +309,7 @@ struct ieee80211_scanner {
struct ieee80211_scan_state *, int);
/* add an entry to the cache */
int (*scan_add)(struct ieee80211_scan_state *,
+ struct ieee80211_channel *,
const struct ieee80211_scanparams *,
const struct ieee80211_frame *,
int subtype, int rssi, int noise);
@@ -298,4 +336,14 @@ 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);
+void ieee80211_scan_update_locked(struct ieee80211vap *vap,
+ const struct ieee80211_scanner *scan);
+void ieee80211_scan_copy_ssid(struct ieee80211vap *vap,
+ struct ieee80211_scan_state *ss,
+ int nssid, const struct ieee80211_scan_ssid ssids[]);
+void ieee80211_scan_dump_probe_beacon(uint8_t subtype, int isnew,
+ const uint8_t mac[IEEE80211_ADDR_LEN],
+ const struct ieee80211_scanparams *sp, int rssi);
+void ieee80211_scan_dump(struct ieee80211_scan_state *ss);
+
#endif /* _NET80211_IEEE80211_SCAN_H_ */
diff --git a/freebsd/sys/net80211/ieee80211_scan_sta.c b/freebsd/sys/net80211/ieee80211_scan_sta.c
index 7cf485c8..faaaf8a3 100644
--- a/freebsd/sys/net80211/ieee80211_scan_sta.c
+++ b/freebsd/sys/net80211/ieee80211_scan_sta.c
@@ -36,11 +36,14 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
@@ -53,6 +56,7 @@ __FBSDID("$FreeBSD$");
#ifdef IEEE80211_SUPPORT_MESH
#include <net80211/ieee80211_mesh.h>
#endif
+#include <net80211/ieee80211_ratectl.h>
#include <net/bpf.h>
@@ -102,7 +106,7 @@ 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 */
+ ieee80211_scan_iter_lock_t st_scanlock; /* on st_scaniter */
u_int st_scaniter; /* gen# for iterator */
u_int st_scangen; /* scan generation # */
int st_newscan;
@@ -127,11 +131,13 @@ static void sta_flush_table(struct sta_table *);
#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 */
+#ifdef IEEE80211_SUPPORT_TDMA
#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 */
+#endif
#define MATCH_MESH_NOID 0x10000 /* no MESHID ie */
#define MATCH_MESHID 0x20000 /* meshid mismatch */
static int match_bss(struct ieee80211vap *,
@@ -159,12 +165,13 @@ 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);
+ st = (struct sta_table *) IEEE80211_MALLOC(sizeof(struct sta_table),
+ M_80211_SCAN,
+ IEEE80211_M_NOWAIT | IEEE80211_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);
+ IEEE80211_SCAN_ITER_LOCK_INIT(st, "scangen");
TAILQ_INIT(&st->st_entry);
ss->ss_priv = st;
nrefs++; /* NB: we assume caller locking */
@@ -182,8 +189,8 @@ sta_detach(struct ieee80211_scan_state *ss)
if (st != NULL) {
sta_flush_table(st);
IEEE80211_SCAN_TABLE_LOCK_DESTROY(st);
- mtx_destroy(&st->st_scanlock);
- free(st, M_80211_SCAN);
+ IEEE80211_SCAN_ITER_LOCK_DESTROY(st);
+ IEEE80211_FREE(st, M_80211_SCAN);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -217,7 +224,7 @@ sta_flush_table(struct sta_table *st)
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_FREE(se, M_80211_SCAN);
}
memset(st->st_maxrssi, 0, sizeof(st->st_maxrssi));
}
@@ -228,6 +235,7 @@ sta_flush_table(struct sta_table *st)
*/
static int
sta_add(struct ieee80211_scan_state *ss,
+ struct ieee80211_channel *curchan,
const struct ieee80211_scanparams *sp,
const struct ieee80211_frame *wh,
int subtype, int rssi, int noise)
@@ -251,8 +259,8 @@ sta_add(struct ieee80211_scan_state *ss,
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);
+ se = (struct sta_entry *) IEEE80211_MALLOC(sizeof(struct sta_entry),
+ M_80211_SCAN, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (se == NULL) {
IEEE80211_SCAN_TABLE_UNLOCK(st);
return 0;
@@ -310,15 +318,15 @@ found:
* IEEE80211_BPARSE_OFFCHAN.
*/
c = ieee80211_find_channel_byieee(ic, sp->chan,
- ic->ic_curchan->ic_flags);
+ 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;
+ ise->se_chan = curchan;
}
} else
- ise->se_chan = ic->ic_curchan;
+ ise->se_chan = curchan;
if (IEEE80211_IS_CHAN_HT(ise->se_chan) && sp->htcap == NULL) {
/* Demote legacy networks to a non-HT channel. */
c = ieee80211_find_channel(ic, ise->se_chan->ic_freq,
@@ -451,13 +459,12 @@ 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));
+ KASSERT(mode < nitems(chanflags), ("Unexpected mode %u", mode));
modeflags = chanflags[mode];
for (i = 0; i < nfreq; i++) {
if (ss->ss_last >= IEEE80211_SCAN_MAX)
@@ -477,7 +484,6 @@ add_channels(struct ieee80211vap *vap,
}
ss->ss_chans[ss->ss_last++] = c;
}
-#undef N
}
struct scanlist {
@@ -602,10 +608,12 @@ makescanlist(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
* 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 */
+ if (vap->iv_des_mode == IEEE80211_MODE_11G) {
+ if (mode == IEEE80211_MODE_11G) /* Skip the G check */
+ continue;
+ else if (mode == IEEE80211_MODE_11B)
+ mode = IEEE80211_MODE_11G; /* upgrade */
+ }
}
} else {
/*
@@ -735,12 +743,6 @@ 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...
@@ -788,7 +790,7 @@ maxrate(const struct ieee80211_scan_entry *se)
} else
for (i = 31; i >= 0 && isclr(htcap->hc_mcsset, i); i--);
if (i >= 0) {
- caps = LE_READ_2(&htcap->hc_cap);
+ caps = le16dec(&htcap->hc_cap);
if ((caps & IEEE80211_HTCAP_CHWIDTH40) &&
(caps & IEEE80211_HTCAP_SHORTGI40))
rmax = ieee80211_htrates[i].ht40_rate_400ns;
@@ -874,7 +876,6 @@ 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;
@@ -889,7 +890,7 @@ check_rate(struct ieee80211vap *vap, const struct ieee80211_channel *chan,
fixedrate = IEEE80211_FIXED_RATE_NONE;
again:
for (i = 0; i < nrs; i++) {
- r = RV(rs[i]);
+ r = IEEE80211_RV(rs[i]);
badrate = r;
/*
* Check any fixed rate is included.
@@ -900,7 +901,7 @@ again:
* Check against our supported rates.
*/
for (j = 0; j < srs->rs_nrates; j++)
- if (r == RV(srs->rs_rates[j])) {
+ if (r == IEEE80211_RV(srs->rs_rates[j])) {
if (r > okrate) /* NB: track max */
okrate = r;
break;
@@ -926,8 +927,7 @@ back:
if (okrate == 0 || ucastrate != fixedrate)
return badrate | IEEE80211_RATE_BASIC;
else
- return RV(okrate);
-#undef RV
+ return IEEE80211_RV(okrate);
}
static __inline int
@@ -1328,7 +1328,7 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
* XXX deauth current ap
*/
if (curRate < roamRate || curRssi < roamRssi) {
- if (time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
+ if (ieee80211_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
@@ -1338,7 +1338,8 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
* XXX force immediate switch on scan complete
*/
if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
- time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle))
+ ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) ||
+ ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)))
ieee80211_bg_scan(vap, 0);
return;
}
@@ -1401,7 +1402,7 @@ sta_iterate(struct ieee80211_scan_state *ss,
struct sta_entry *se;
u_int gen;
- mtx_lock(&st->st_scanlock);
+ IEEE80211_SCAN_ITER_LOCK(st);
gen = st->st_scaniter++;
restart:
IEEE80211_SCAN_TABLE_LOCK(st);
@@ -1417,7 +1418,7 @@ restart:
}
IEEE80211_SCAN_TABLE_UNLOCK(st);
- mtx_unlock(&st->st_scanlock);
+ IEEE80211_SCAN_ITER_UNLOCK(st);
}
static void
@@ -1569,6 +1570,7 @@ 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;
+ struct ieee80211com *ic = vap->iv_ic;
KASSERT(vap->iv_opmode == IEEE80211_M_IBSS ||
vap->iv_opmode == IEEE80211_M_AHDEMO ||
@@ -1614,15 +1616,18 @@ notfound:
*/
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) {
+ /*
+ * Create a HT capable IBSS; the per-node
+ * probe request/response will result in
+ * "correct" rate control capabilities being
+ * negotiated.
+ */
+ chan = ieee80211_ht_adjust_channel(ic,
+ chan, vap->iv_flags_ht);
ieee80211_create_ibss(vap, chan);
return 1;
}
@@ -1646,6 +1651,14 @@ notfound:
chan = selbs->base.se_chan;
if (selbs->se_flags & STA_DEMOTE11B)
chan = demote11b(vap, chan);
+ /*
+ * If HT is available, make it a possibility here.
+ * The intent is to enable HT20/HT40 when joining a non-HT
+ * IBSS node; we can then advertise HT IEs and speak HT
+ * to any subsequent nodes that support it.
+ */
+ chan = ieee80211_ht_adjust_channel(ic,
+ chan, vap->iv_flags_ht);
if (!ieee80211_sta_join(vap, chan, &selbs->base))
goto notfound;
return 1; /* terminate scan */
@@ -1666,7 +1679,7 @@ adhoc_age(struct ieee80211_scan_state *ss)
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_FREE(se, M_80211_SCAN);
}
}
IEEE80211_SCAN_TABLE_UNLOCK(st);
@@ -1691,26 +1704,6 @@ static const struct ieee80211_scanner adhoc_default = {
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)
{
@@ -1726,7 +1719,6 @@ ap_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
st->st_scangen++;
st->st_newscan = 1;
- ap_force_promisc(vap->iv_ic);
return 0;
}
@@ -1736,7 +1728,6 @@ ap_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
static int
ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
- ap_reset_promisc(vap->iv_ic);
return 0;
}
@@ -1810,7 +1801,6 @@ ap_end(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
return 0;
}
}
- ap_reset_promisc(ic);
if (ss->ss_flags & (IEEE80211_SCAN_NOPICK | IEEE80211_SCAN_NOJOIN)) {
/*
* Manual/background scan, don't select+join the
diff --git a/freebsd/sys/net80211/ieee80211_scan_sw.c b/freebsd/sys/net80211/ieee80211_scan_sw.c
new file mode 100644
index 00000000..ca6d9136
--- /dev/null
+++ b/freebsd/sys/net80211/ieee80211_scan_sw.c
@@ -0,0 +1,1016 @@
+#include <machine/rtems-bsd-kernel-space.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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11 scanning support.
+ */
+#include <rtems/bsd/local/opt_wlan.h>
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/condvar.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <net80211/ieee80211_scan_sw.h>
+
+#include <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_INTERRUPT 0x0004 /* interrupt current scan */
+#define ISCAN_CANCEL 0x0008 /* cancel current scan */
+#define ISCAN_PAUSE (ISCAN_INTERRUPT | ISCAN_CANCEL)
+#define ISCAN_ABORT 0x0010 /* end the scan immediately */
+#define ISCAN_RUNNING 0x0020 /* scan was started */
+
+ 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_start; /* scan start */
+ struct timeout_task ss_scan_curchan; /* scan execution */
+};
+#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)
+
+static void scan_curchan(struct ieee80211_scan_state *, unsigned long);
+static void scan_mindwell(struct ieee80211_scan_state *);
+static void scan_signal(struct ieee80211_scan_state *, int);
+static void scan_signal_locked(struct ieee80211_scan_state *, int);
+static void scan_start(void *, int);
+static void scan_curchan_task(void *, int);
+static void scan_end(struct ieee80211_scan_state *, int);
+static void scan_done(struct ieee80211_scan_state *, int);
+
+MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
+
+static void
+ieee80211_swscan_detach(struct ieee80211com *ic)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ if (ss != NULL) {
+ scan_signal(ss, ISCAN_ABORT);
+ ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_start);
+ taskqueue_drain_timeout(ic->ic_tq,
+ &SCAN_PRIVATE(ss)->ss_scan_curchan);
+ KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0,
+ ("scan still running"));
+
+ /*
+ * For now, do the ss_ops detach here rather
+ * than ieee80211_scan_detach().
+ *
+ * I'll figure out how to cleanly split things up
+ * at a later date.
+ */
+ if (ss->ss_ops != NULL) {
+ ss->ss_ops->scan_detach(ss);
+ ss->ss_ops = NULL;
+ }
+ ic->ic_scan = NULL;
+ IEEE80211_FREE(SCAN_PRIVATE(ss), M_80211_SCAN);
+ }
+}
+
+static void
+ieee80211_swscan_vattach(struct ieee80211vap *vap)
+{
+ /* nothing to do for now */
+ /*
+ * TODO: all of the vap scan calls should be methods!
+ */
+
+}
+
+static void
+ieee80211_swscan_vdetach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if (ss != NULL && ss->ss_vap == vap &&
+ (ic->ic_flags & IEEE80211_F_SCAN))
+ scan_signal_locked(ss, ISCAN_ABORT);
+}
+
+static void
+ieee80211_swscan_set_scan_duration(struct ieee80211vap *vap, u_int duration)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ /* NB: flush frames rx'd before 1st channel change */
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
+ SCAN_PRIVATE(ss)->ss_duration = duration;
+}
+
+/*
+ * Start a scan unless one is already going.
+ */
+static int
+ieee80211_swscan_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" : ""
+ );
+
+ ieee80211_scan_update_locked(vap, scan);
+ if (ss->ss_ops != NULL) {
+ if ((flags & IEEE80211_SCAN_NOSSID) == 0)
+ ieee80211_scan_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);
+ if (flags & IEEE80211_SCAN_BGSCAN)
+ ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
+
+ /* Set duration for this particular scan */
+ ieee80211_swscan_set_scan_duration(vap, 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))
+ ieee80211_scan_dump(ss);
+#endif /* IEEE80211_DEBUG */
+ ic->ic_flags |= IEEE80211_F_SCAN;
+
+ /* Start scan task */
+ ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_start);
+ }
+ return 1;
+ } else {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: %s scan already in progress\n", __func__,
+ ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
+ }
+ return 0;
+}
+
+
+/*
+ * Start a scan unless one is already going.
+ *
+ * Called without the comlock held; grab the comlock as appropriate.
+ */
+static int
+ieee80211_swscan_start_scan(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;
+ int result;
+
+ IEEE80211_UNLOCK_ASSERT(ic);
+
+ IEEE80211_LOCK(ic);
+ result = ieee80211_swscan_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.
+ *
+ * Called with the comlock held.
+ *
+ * XXX TODO: split out!
+ */
+static int
+ieee80211_swscan_check_scan(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;
+ int result;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ 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.
+ */
+ ieee80211_scan_copy_ssid(vap, ss, nssid, ssids);
+ flags |= IEEE80211_SCAN_NOSSID;
+ }
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
+ (flags & IEEE80211_SCAN_FLUSH) == 0 &&
+ ieee80211_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);
+ return 1;
+ }
+ }
+ }
+ result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration,
+ mindwell, maxdwell, nssid, ssids);
+
+ return result;
+}
+
+/*
+ * Restart a previous scan. If the previous scan completed
+ * then we start again using the existing channel list.
+ */
+static int
+ieee80211_swscan_bg_scan(const struct ieee80211_scanner *scan,
+ struct ieee80211vap *vap, int flags)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ /* XXX assert unlocked? */
+ // IEEE80211_UNLOCK_ASSERT(ic);
+
+ 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 %u\n", __func__,
+ ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
+ ticks, duration);
+
+ ieee80211_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))
+ ieee80211_scan_dump(ss);
+#endif /* IEEE80211_DEBUG */
+ }
+ }
+ ieee80211_swscan_set_scan_duration(vap, 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_start);
+ } 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);
+}
+
+/*
+ * Taskqueue work to cancel a scan.
+ *
+ * Note: for offload scan devices, we may want to call into the
+ * driver to try and cancel scanning, however it may not be cancelable.
+ */
+static void
+cancel_scan(struct ieee80211vap *vap, int any, const char *func)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
+ int signal;
+
+ IEEE80211_LOCK(ic);
+ signal = any ? ISCAN_PAUSE : ISCAN_CANCEL;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) &&
+ (any || ss->ss_vap == vap) &&
+ (ss_priv->ss_iflags & signal) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: %s %s scan\n", func,
+ any ? "pause" : "cancel",
+ ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
+ "active" : "passive");
+
+ /* clear bg scan NOPICK */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ /* mark request and wake up the scan task */
+ scan_signal_locked(ss, signal);
+ } else {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: called; F_SCAN=%d, vap=%s, signal=%d\n",
+ func,
+ !! (ic->ic_flags & IEEE80211_F_SCAN),
+ (ss->ss_vap == vap ? "match" : "nomatch"),
+ !! (ss_priv->ss_iflags & signal));
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Cancel any scan currently going on for the specified vap.
+ */
+static void
+ieee80211_swscan_cancel_scan(struct ieee80211vap *vap)
+{
+ cancel_scan(vap, 0, __func__);
+}
+
+/*
+ * Cancel any scan currently going on.
+ */
+static void
+ieee80211_swscan_cancel_anyscan(struct ieee80211vap *vap)
+{
+
+ /* XXX for now - just don't do this per packet. */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD)
+ return;
+
+ cancel_scan(vap, 1, __func__);
+}
+
+/*
+ * Manually switch to the next channel in the channel list.
+ * Provided for drivers that manage scanning themselves
+ * (e.g. for firmware-based devices).
+ */
+static void
+ieee80211_swscan_scan_next(struct ieee80211vap *vap)
+{
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__);
+
+ /* wake up the scan task */
+ scan_signal(ss, 0);
+}
+
+/*
+ * Manually stop a scan that is currently running.
+ * Provided for drivers that are not able to scan single channels
+ * (e.g. for firmware-based devices).
+ */
+static void
+ieee80211_swscan_scan_done(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ scan_signal_locked(ss, 0);
+}
+
+/*
+ * Probe the current 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.
+ */
+static void
+ieee80211_swscan_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;
+
+ /*
+ * Full-offload scan devices don't require this.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD)
+ 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;
+ struct ieee80211com *ic = ss->ss_ic;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: calling; maxdwell=%lu\n",
+ __func__,
+ maxdwell);
+ IEEE80211_LOCK(ic);
+ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+ ieee80211_probe_curchan(vap, 0);
+ taskqueue_enqueue_timeout(ic->ic_tq,
+ &SCAN_PRIVATE(ss)->ss_scan_curchan, maxdwell);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+scan_signal(struct ieee80211_scan_state *ss, int iflags)
+{
+ struct ieee80211com *ic = ss->ss_ic;
+
+ IEEE80211_UNLOCK_ASSERT(ic);
+
+ IEEE80211_LOCK(ic);
+ scan_signal_locked(ss, iflags);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+scan_signal_locked(struct ieee80211_scan_state *ss, int iflags)
+{
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
+ struct timeout_task *scan_task = &ss_priv->ss_scan_curchan;
+ struct ieee80211com *ic = ss->ss_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ss_priv->ss_iflags |= iflags;
+ if (ss_priv->ss_iflags & ISCAN_RUNNING) {
+ if (taskqueue_cancel_timeout(ic->ic_tq, scan_task, NULL) == 0)
+ taskqueue_enqueue_timeout(ic->ic_tq, scan_task, 0);
+ }
+}
+
+/*
+ * Handle mindwell requirements completed; initiate a channel
+ * change to the next channel asap.
+ */
+static void
+scan_mindwell(struct ieee80211_scan_state *ss)
+{
+
+ IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: called\n",
+ __func__);
+
+ scan_signal(ss, 0);
+}
+
+static void
+scan_start(void *arg, int pending)
+{
+#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD)
+ struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = ss->ss_ic;
+
+ IEEE80211_LOCK(ic);
+ if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
+ (ss_priv->ss_iflags & ISCAN_ABORT)) {
+ /* Cancelled before we started */
+ scan_done(ss, 0);
+ return;
+ }
+
+ if (ss->ss_next == ss->ss_last) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no channels to scan\n", __func__);
+ scan_done(ss, 1);
+ return;
+ }
+
+ /*
+ * Put the station into power save mode.
+ *
+ * This is only required if we're not a full-offload devices;
+ * those devices manage scan/traffic differently.
+ */
+ if (((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0) &&
+ 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 */
+ vap->iv_sta_ps(vap, 1);
+ /* Wait until null data frame will be ACK'ed */
+ mtx_sleep(vap, IEEE80211_LOCK_OBJ(ic), PCATCH,
+ "sta_ps", msecs_to_ticks(10));
+ if (ss_priv->ss_iflags & ISCAN_ABORT) {
+ scan_done(ss, 0);
+ return;
+ }
+ }
+ }
+
+ ss_priv->ss_scanend = ticks + ss_priv->ss_duration;
+
+ /* XXX scan state can change! Re-validate scan state! */
+
+ IEEE80211_UNLOCK(ic);
+
+ ic->ic_scan_start(ic); /* notify driver */
+
+ scan_curchan_task(ss, 0);
+}
+
+static void
+scan_curchan_task(void *arg, int pending)
+{
+ struct ieee80211_scan_state *ss = arg;
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
+ struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211_channel *chan;
+ unsigned long maxdwell;
+ int scandone;
+
+ IEEE80211_LOCK(ic);
+end:
+ scandone = (ss->ss_next >= ss->ss_last) ||
+ (ss_priv->ss_iflags & ISCAN_CANCEL) != 0;
+
+ IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
+ "%s: loop start; scandone=%d\n",
+ __func__,
+ scandone);
+
+ if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
+ (ss_priv->ss_iflags & ISCAN_ABORT) ||
+ ieee80211_time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
+ ss_priv->ss_iflags &= ~ISCAN_RUNNING;
+ scan_end(ss, scandone);
+ return;
+ } else
+ ss_priv->ss_iflags |= ISCAN_RUNNING;
+
+ chan = ss->ss_chans[ss->ss_next++];
+
+ /*
+ * Watch for truncation due to the scan end time.
+ */
+ if (ieee80211_time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend))
+ maxdwell = ss_priv->ss_scanend - ticks;
+ else
+ maxdwell = ss->ss_maxdwell;
+
+ IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
+ "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
+ __func__,
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ ieee80211_channel_type_char(ic->ic_curchan),
+ ieee80211_chan2ieee(ic, chan),
+ ieee80211_channel_type_char(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 ourselves;
+ * 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).
+ */
+ ic->ic_scan_curchan(ss, maxdwell);
+ IEEE80211_LOCK(ic);
+
+ /* XXX scan state can change! Re-validate scan state! */
+
+ ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell;
+ /* clear mindwell lock and initial channel change flush */
+ ss_priv->ss_iflags &= ~ISCAN_REP;
+
+ if (ss_priv->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)) {
+ taskqueue_cancel_timeout(ic->ic_tq, &ss_priv->ss_scan_curchan,
+ NULL);
+ goto end;
+ }
+
+ IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: waiting\n",
+ __func__);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+scan_end(struct ieee80211_scan_state *ss, int scandone)
+{
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = ss->ss_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: out\n", __func__);
+
+ if (ss_priv->ss_iflags & ISCAN_ABORT) {
+ scan_done(ss, scandone);
+ return;
+ }
+
+ IEEE80211_UNLOCK(ic);
+ ic->ic_scan_end(ic); /* notify driver */
+ IEEE80211_LOCK(ic);
+ /* XXX scan state can change! Re-validate scan state! */
+
+ /*
+ * Since a cancellation may have occurred during one of the
+ * driver calls (whilst unlocked), update scandone.
+ */
+ if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) {
+ /* XXX printf? */
+ if_printf(vap->iv_ifp,
+ "%s: OOPS! scan cancelled during driver call (1)!\n",
+ __func__);
+ scandone = 1;
+ }
+
+ /*
+ * 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 */
+ ss_priv->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 ((ss_priv->ss_iflags & ISCAN_CANCEL) == 0 &&
+ !ss->ss_ops->scan_end(ss, vap) &&
+ (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
+ ieee80211_time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: done, restart "
+ "[ticks %u, dwell min %lu scanend %lu]\n",
+ __func__,
+ ticks, ss->ss_mindwell, ss_priv->ss_scanend);
+ ss->ss_next = 0; /* reset to beginning */
+ 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, &ss_priv->ss_scan_start);
+ 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, ss_priv->ss_scanend);
+
+ /*
+ * Since a cancellation may have occurred during one of the
+ * driver calls (whilst unlocked), update scandone.
+ */
+ if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) {
+ /* XXX printf? */
+ if_printf(vap->iv_ifp,
+ "%s: OOPS! scan cancelled during driver call (2)!\n",
+ __func__);
+ scandone = 1;
+ }
+
+ scan_done(ss, scandone);
+}
+
+static void
+scan_done(struct ieee80211_scan_state *ss, int scandone)
+{
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
+ struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211vap *vap = ss->ss_vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ /*
+ * 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.
+ */
+ 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) {
+ /*
+ * If we're not a scan offload device, come back out of
+ * station powersave. Offload devices handle this themselves.
+ */
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0)
+ vap->iv_sta_ps(vap, 0);
+ if (ss->ss_next >= ss->ss_last)
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
+
+ /* send 'scan done' event if not interrupted due to traffic. */
+ if (!(ss_priv->ss_iflags & ISCAN_INTERRUPT))
+ ieee80211_notify_scan_done(vap);
+ }
+ ss_priv->ss_iflags &= ~(ISCAN_PAUSE | ISCAN_ABORT);
+ ss_priv->ss_scanend = 0;
+ ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
+ IEEE80211_UNLOCK(ic);
+#undef ISCAN_REP
+}
+
+/*
+ * Process a beacon or probe response frame.
+ */
+static void
+ieee80211_swscan_add_scan(struct ieee80211vap *vap,
+ struct ieee80211_channel *curchan,
+ 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))
+ ieee80211_scan_dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi);
+#endif
+ if (ss->ss_ops != NULL &&
+ ss->ss_ops->scan_add(ss, curchan, 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 &&
+ ieee80211_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),
+ ieee80211_channel_type_char(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);
+ }
+ }
+}
+
+static struct ieee80211_scan_methods swscan_methods = {
+ .sc_attach = ieee80211_swscan_attach,
+ .sc_detach = ieee80211_swscan_detach,
+ .sc_vattach = ieee80211_swscan_vattach,
+ .sc_vdetach = ieee80211_swscan_vdetach,
+ .sc_set_scan_duration = ieee80211_swscan_set_scan_duration,
+ .sc_start_scan = ieee80211_swscan_start_scan,
+ .sc_check_scan = ieee80211_swscan_check_scan,
+ .sc_bg_scan = ieee80211_swscan_bg_scan,
+ .sc_cancel_scan = ieee80211_swscan_cancel_scan,
+ .sc_cancel_anyscan = ieee80211_swscan_cancel_anyscan,
+ .sc_scan_next = ieee80211_swscan_scan_next,
+ .sc_scan_done = ieee80211_swscan_scan_done,
+ .sc_scan_probe_curchan = ieee80211_swscan_probe_curchan,
+ .sc_add_scan = ieee80211_swscan_add_scan
+};
+
+/*
+ * Default scan attach method.
+ */
+void
+ieee80211_swscan_attach(struct ieee80211com *ic)
+{
+ struct scan_state *ss;
+
+ /*
+ * Setup the default methods
+ */
+ ic->ic_scan_methods = &swscan_methods;
+
+ /* Allocate initial scan state */
+ ss = (struct scan_state *) IEEE80211_MALLOC(sizeof(struct scan_state),
+ M_80211_SCAN, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (ss == NULL) {
+ ic->ic_scan = NULL;
+ return;
+ }
+ TASK_INIT(&ss->ss_scan_start, 0, scan_start, ss);
+ TIMEOUT_TASK_INIT(ic->ic_tq, &ss->ss_scan_curchan, 0,
+ scan_curchan_task, ss);
+
+ ic->ic_scan = &ss->base;
+ ss->base.ss_ic = ic;
+
+ ic->ic_scan_curchan = scan_curchan;
+ ic->ic_scan_mindwell = scan_mindwell;
+}
diff --git a/freebsd/sys/net80211/ieee80211_scan_sw.h b/freebsd/sys/net80211/ieee80211_scan_sw.h
new file mode 100644
index 00000000..8408933b
--- /dev/null
+++ b/freebsd/sys/net80211/ieee80211_scan_sw.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2015 Adrian Chadd <adrian@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$
+ */
+#ifndef __NET80211_IEEE80211_SCAN_SW_H__
+#define __NET80211_IEEE80211_SCAN_SW_H__
+
+extern void ieee80211_swscan_attach(struct ieee80211com *ic);
+
+#endif /* __NET80211_IEEE80211_SCAN_SW_H__ */
diff --git a/freebsd/sys/net80211/ieee80211_sta.c b/freebsd/sys/net80211/ieee80211_sta.c
index c96eb0b5..68441806 100644
--- a/freebsd/sys/net80211/ieee80211_sta.c
+++ b/freebsd/sys/net80211/ieee80211_sta.c
@@ -52,6 +52,8 @@ __FBSDID("$FreeBSD$");
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_llc.h>
+#include <net/if_dl.h>
+#include <net/if_var.h>
#include <net/ethernet.h>
#include <net/bpf.h>
@@ -63,15 +65,17 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_superg.h>
#endif
#include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_sta.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 int sta_input(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_rx_stats *, int, int);
static void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *,
- int subtype, int rssi, int nf);
+ int subtype, const struct ieee80211_rx_stats *, int rssi, int nf);
static void sta_recv_ctl(struct ieee80211_node *, struct mbuf *, int subtype);
void
@@ -111,6 +115,8 @@ sta_beacon_miss(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
+ IEEE80211_LOCK_ASSERT(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]));
@@ -150,7 +156,6 @@ sta_beacon_miss(struct ieee80211vap *vap)
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
@@ -202,6 +207,24 @@ sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason)
}
}
+static void
+sta_swbmiss_start(struct ieee80211vap *vap)
+{
+
+ if (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 * vap->iv_bss->ni_intval);
+ vap->iv_swbmiss_count = 0;
+ callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
+ ieee80211_swbmiss, vap);
+ }
+}
+
/*
* IEEE80211_M_STA vap state machine handler.
* This routine handles the main states in the 802.11 protocol.
@@ -231,6 +254,7 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
switch (ostate) {
case IEEE80211_S_SLEEP:
/* XXX wakeup */
+ /* XXX driver hook to wakeup the hardware? */
case IEEE80211_S_RUN:
IEEE80211_SEND_MGMT(ni,
IEEE80211_FC0_SUBTYPE_DISASSOC,
@@ -246,7 +270,7 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
ieee80211_cancel_scan(vap);
break;
default:
- goto invalid;
+ break;
}
if (ostate != IEEE80211_S_INIT) {
/* NB: optimize INIT -> INIT case */
@@ -296,12 +320,18 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
ieee80211_check_scan_current(vap);
break;
+ case IEEE80211_S_SLEEP: /* beacon miss */
+ /*
+ * XXX if in sleep we need to wakeup the hardware.
+ */
+ /* FALLTHROUGH */
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);
@@ -330,12 +360,13 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
break;
}
break;
+ case IEEE80211_S_SLEEP:
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 */
+ vap->iv_state = IEEE80211_S_RUN; /* stay RUN */
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
ieee80211_sta_leave(ni);
@@ -400,25 +431,15 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
break;
case IEEE80211_S_SLEEP:
- ieee80211_sta_pwrsave(vap, 0);
+ /* Wake up from sleep */
+ vap->iv_sta_ps(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);
- }
+ if (ostate != IEEE80211_S_RUN)
+ sta_swbmiss_start(vap);
/*
* When 802.1x is not in use mark the port authorized
* at this point so traffic can flow.
@@ -427,16 +448,19 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
ieee80211_node_authorize(ni);
/*
* Fake association when joining an existing bss.
+ *
+ * Don't do this if we're doing SLEEP->RUN.
*/
- if (ic->ic_newassoc != NULL)
- ic->ic_newassoc(vap->iv_bss, ostate != IEEE80211_S_RUN);
+ if (ic->ic_newassoc != NULL && ostate != IEEE80211_S_SLEEP)
+ 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, 1);
+ sta_swbmiss_start(vap);
+ vap->iv_sta_ps(vap, 1);
break;
default:
invalid:
@@ -512,9 +536,9 @@ doprint(struct ieee80211vap *vap, int subtype)
* by the 802.11 layer.
*/
static int
-sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
+sta_input(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
-#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;
@@ -524,7 +548,16 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
int hdrspace, need_tap = 1; /* mbuf need to be tapped. */
uint8_t dir, type, subtype, qos;
uint8_t *bssid;
- uint16_t rxseq;
+ int is_hw_decrypted = 0;
+ int has_decrypted = 0;
+
+ /*
+ * Some devices do hardware decryption all the way through
+ * to pretending the frame wasn't encrypted in the first place.
+ * So, tag it appropriately so it isn't discarded inappropriately.
+ */
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED))
+ is_hw_decrypted = 1;
if (m->m_flags & M_AMPDU_MPDU) {
/*
@@ -584,31 +617,40 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
vap->iv_stats.is_rx_wrongbss++;
goto out;
}
+
+ /*
+ * Some devices may be in a promiscuous mode
+ * where they receive frames for multiple station
+ * addresses.
+ *
+ * If we receive a data frame that isn't
+ * destined to our VAP MAC, drop it.
+ *
+ * XXX TODO: This is only enforced when not scanning;
+ * XXX it assumes a software-driven scan will put the NIC
+ * XXX into a "no data frames" mode before setting this
+ * XXX flag. Otherwise it may be possible that we'll still
+ * XXX process data frames whilst scanning.
+ */
+ if ((! IEEE80211_IS_MULTICAST(wh->i_addr1))
+ && (! IEEE80211_ADDR_EQ(wh->i_addr1, IF_LLADDR(ifp)))) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "not to cur sta: lladdr=%6D, addr1=%6D",
+ IF_LLADDR(ifp), ":", wh->i_addr1, ":");
+ 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)) {
+ if ( IEEE80211_HAS_SEQ(type, subtype) &&
+ !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 (! ieee80211_check_rxseq(ni, wh)) {
- /* 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);
+ if (! ieee80211_check_rxseq(ni, wh, bssid))
goto out;
- }
- ni->ni_rxseqs[tid] = rxseq;
}
}
@@ -694,6 +736,21 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
}
/*
+ * Handle privacy requirements for hardware decryption
+ * devices.
+ *
+ * For those devices, a handful of things happen.
+ *
+ * + If IV has been stripped, then we can't run
+ * ieee80211_crypto_decap() - none of the key
+ * + If MIC has been stripped, we can't validate
+ * MIC here.
+ * + If MIC fails, then we need to communicate a
+ * MIC failure up to the stack - but we don't know
+ * which key was used.
+ */
+
+ /*
* Handle privacy requirements. Note that we
* must not be preempted from here until after
* we (potentially) call ieee80211_crypto_demic;
@@ -701,7 +758,7 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
* crypto cipher modules used to do delayed update
* of replay sequence numbers.
*/
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (is_hw_decrypted || wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
/*
* Discard encrypted frames when privacy is off.
@@ -712,14 +769,14 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
IEEE80211_NODE_STAT(ni, rx_noprivacy);
goto out;
}
- key = ieee80211_crypto_decap(ni, m, hdrspace);
- if (key == NULL) {
+ if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
/* 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;
+ wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+ has_decrypted = 1;
} else {
/* XXX M_WEP and IEEE80211_F_PRIVACY */
key = NULL;
@@ -749,8 +806,13 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
/*
* Next strip any MSDU crypto bits.
+ *
+ * Note: we can't do MIC stripping/verification if the
+ * upper layer has stripped it. We have to check MIC
+ * ourselves. So, key may be NULL, but we have to check
+ * the RX status.
*/
- if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ if (!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++;
@@ -804,7 +866,8 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
* any non-PAE frames received without encryption.
*/
if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
- (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) &&
+ (is_hw_decrypted == 0) &&
eh->ether_type != htons(ETHERTYPE_PAE)) {
/*
* Drop unencrypted frames.
@@ -849,20 +912,28 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
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],
+ ieee80211_mgt_subtype_name(subtype),
ether_sprintf(wh->i_addr2), rssi);
}
#endif
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+
+ /*
+ * Note: See above for hardware offload privacy requirements.
+ * It also applies here.
+ */
+
+ /*
+ * Again, having encrypted flag set check would be good, but
+ * then we have to also handle crypto_decap() like above.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
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],
+ wh, ieee80211_mgt_subtype_name(subtype),
"%s", "WEP set but not permitted");
vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
goto out;
@@ -877,15 +948,20 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
goto out;
}
hdrspace = ieee80211_hdrspace(ic, wh);
- key = ieee80211_crypto_decap(ni, m, hdrspace);
- if (key == NULL) {
+
+ /*
+ * Again, if IV/MIC was stripped, then this whole
+ * setup will fail. That's going to need some poking.
+ */
+ if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
/* NB: stats+msgs handled in crypto_decap */
goto out;
}
+ has_decrypted = 1;
wh = mtod(m, struct ieee80211_frame *);
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
}
- vap->iv_recv_mgmt(ni, m, subtype, rssi, nf);
+ vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
goto out;
case IEEE80211_FC0_TYPE_CTL:
@@ -901,7 +977,7 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
break;
}
err:
- ifp->if_ierrors++;
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
out:
if (m != NULL) {
if (need_tap && ieee80211_radiotap_active_vap(vap))
@@ -947,7 +1023,6 @@ sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
{
struct ieee80211vap *vap = ni->ni_vap;
uint8_t *challenge;
- int estatus;
/*
* NB: this can happen as we allow pre-shared key
@@ -961,7 +1036,6 @@ sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
ni->ni_macaddr, "shared key auth",
"%s", " PRIVACY is disabled");
- estatus = IEEE80211_STATUS_ALG;
goto bad;
}
/*
@@ -975,7 +1049,6 @@ sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
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;
}
@@ -987,7 +1060,6 @@ sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
"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)
@@ -1002,7 +1074,6 @@ sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
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) {
@@ -1010,7 +1081,6 @@ sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
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:
@@ -1021,7 +1091,7 @@ sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
switch (seq) {
case IEEE80211_AUTH_SHARED_PASS:
if (ni->ni_challenge != NULL) {
- free(ni->ni_challenge, M_80211_NODE);
+ IEEE80211_FREE(ni->ni_challenge, M_80211_NODE);
ni->ni_challenge = NULL;
}
if (status != 0) {
@@ -1060,7 +1130,7 @@ bad:
IEEE80211_SCAN_FAIL_STATUS);
}
-static int
+int
ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
const struct ieee80211_frame *wh)
{
@@ -1089,7 +1159,7 @@ ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
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);
+ wmep->wmep_txopLimit = le16dec(frm+2);
frm += 4;
}
wme->wme_wmeChanParams.cap_info = qosinfo;
@@ -1208,6 +1278,7 @@ done:
* o bg scan is active
* o no channel switch is pending
* o there has not been any traffic recently
+ * o no full-offload scan support (no need for explicitly continuing scan then)
*
* Note we do not check if there is an administrative enable;
* this is only done to start the scan. We assume that any
@@ -1221,8 +1292,9 @@ contbgscan(struct ieee80211vap *vap)
return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
(ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
+ !(vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) &&
vap->iv_state == IEEE80211_S_RUN && /* XXX? */
- time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
+ ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
}
/*
@@ -1231,7 +1303,7 @@ contbgscan(struct ieee80211vap *vap)
* 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
+ * o there has not been any traffic recently (don't check if full-offload scan)
*/
static __inline int
startbgscan(struct ieee80211vap *vap)
@@ -1243,22 +1315,25 @@ startbgscan(struct ieee80211vap *vap)
#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));
+ ieee80211_time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) &&
+ ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) ||
+ ieee80211_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)
+sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
+ const struct ieee80211_rx_stats *rxs,
+ 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_channel *rxchan = ic->ic_curchan;
struct ieee80211_frame *wh;
uint8_t *frm, *efrm;
uint8_t *rates, *xrates, *wme, *htcap, *htinfo;
uint8_t rate;
+ int ht_state_change = 0;
wh = mtod(m0, struct ieee80211_frame *);
frm = (uint8_t *)&wh[1];
@@ -1267,6 +1342,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
case IEEE80211_FC0_SUBTYPE_BEACON: {
struct ieee80211_scanparams scan;
+ struct ieee80211_channel *c;
/*
* We process beacon/probe response frames:
* o when scanning, or
@@ -1278,9 +1354,21 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
vap->iv_stats.is_rx_mgtdiscard++;
return;
}
+
+ /* Override RX channel as appropriate */
+ if (rxs != NULL) {
+ c = ieee80211_lookup_channel_rxstatus(vap, rxs);
+ if (c != NULL)
+ rxchan = c;
+ }
+
/* XXX probe response in sta mode when !scanning? */
- if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ if (ieee80211_parse_beacon(ni, m0, rxchan, &scan) != 0) {
+ if (! (ic->ic_flags & IEEE80211_F_SCAN))
+ vap->iv_stats.is_beacon_bad++;
return;
+ }
+
/*
* Count frame now that we know it's to be processed.
*/
@@ -1343,29 +1431,73 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
#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 (ieee80211_ht_updateparams(ni,
+ scan.htcap, scan.htinfo))
+ ht_state_change = 1;
}
+ if (scan.quiet)
+ ic->ic_set_quiet(ni, scan.quiet);
+
if (scan.tim != NULL) {
struct ieee80211_tim_ie *tim =
(struct ieee80211_tim_ie *) scan.tim;
-#if 0
+ /*
+ * XXX Check/debug this code; see if it's about
+ * the right time to force the VAP awake if we
+ * receive a frame destined for us?
+ */
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.
+ int tim_ucast = 0, tim_mcast = 0;
+
+ /*
+ * Only do this for unicast traffic in the TIM
+ * The multicast traffic notification for
+ * the scan notification stuff should occur
+ * differently.
+ */
+ if (min <= ix && ix <= max &&
+ isset(tim->tim_bitmap - min, aid)) {
+ tim_ucast = 1;
+ }
+
+ /*
+ * Do a separate notification
+ * for the multicast bit being set.
+ */
+ if (tim->tim_bitctl & 1) {
+ tim_mcast = 1;
+ }
+
+ /*
+ * If the TIM indicates there's traffic for
+ * us then get us out of STA mode powersave.
+ */
+ if (tim_ucast == 1) {
+
+ /*
+ * Wake us out of SLEEP state if we're
+ * in it; and if we're doing bgscan
+ * then wake us out of STA powersave.
+ */
+ ieee80211_sta_tim_notify(vap, 1);
+
+ /*
+ * This is preventing us from
+ * continuing a bgscan; because it
+ * tricks the contbgscan()
+ * routine to think there's always
+ * traffic for us.
+ *
+ * I think we need both an RX and
+ * TX ic_lastdata field.
*/
ic->ic_lastdata = ticks;
- ieee80211_sta_pwrsave(vap, 0);
}
-#endif
+
ni->ni_dtim_count = tim->tim_count;
ni->ni_dtim_period = tim->tim_period;
}
@@ -1398,8 +1530,8 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
* our ap.
*/
if (ic->ic_flags & IEEE80211_F_SCAN) {
- ieee80211_add_scan(vap, &scan, wh,
- subtype, rssi, nf);
+ ieee80211_add_scan(vap, rxchan,
+ &scan, wh, subtype, rssi, nf);
} else if (contbgscan(vap)) {
ieee80211_bg_scan(vap, 0);
} else if (startbgscan(vap)) {
@@ -1410,6 +1542,21 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
#endif
ieee80211_bg_scan(vap, 0);
}
+
+ /*
+ * Put the station to sleep if we haven't seen
+ * traffic in a while.
+ */
+ IEEE80211_LOCK(ic);
+ ieee80211_sta_ps_timer_check(vap);
+ IEEE80211_UNLOCK(ic);
+
+ /*
+ * If we've had a channel width change (eg HT20<->HT40)
+ * then schedule a delayed driver notification.
+ */
+ if (ht_state_change)
+ ieee80211_update_chw(ic);
return;
}
/*
@@ -1428,7 +1575,8 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
ieee80211_probe_curchan(vap, 1);
ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
}
- ieee80211_add_scan(vap, &scan, wh, subtype, rssi, nf);
+ ieee80211_add_scan(vap, rxchan, &scan, wh,
+ subtype, rssi, nf);
return;
}
break;
@@ -1596,12 +1744,16 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
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
}
+
+ /*
+ * Always initialise FF/superg state; we can use this
+ * for doing A-MSDU encapsulation as well.
+ */
+#ifdef IEEE80211_SUPPORT_SUPERG
+ ieee80211_ff_node_init(ni);
+#endif
+
/*
* Configure state now that we are associated.
*
@@ -1677,7 +1829,8 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
IEEE80211_NODE_STAT(ni, rx_deauth);
IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
- "recv deauthenticate (reason %d)", reason);
+ "recv deauthenticate (reason: %d (%s))", reason,
+ ieee80211_reason_to_string(reason));
ieee80211_new_state(vap, IEEE80211_S_AUTH,
(reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
break;
@@ -1710,7 +1863,8 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
IEEE80211_NODE_STAT(ni, rx_disassoc);
IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
- "recv disassociate (reason %d)", reason);
+ "recv disassociate (reason: %d (%s))", reason,
+ ieee80211_reason_to_string(reason));
ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
break;
}
@@ -1736,6 +1890,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
case IEEE80211_FC0_SUBTYPE_ATIM:
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
wh, NULL, "%s", "not handled");
@@ -1749,7 +1904,6 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
break;
}
#undef ISREASSOC
-#undef ISPROBE
}
static void
diff --git a/freebsd/sys/net80211/ieee80211_sta.h b/freebsd/sys/net80211/ieee80211_sta.h
index 1508a7c7..e97e181e 100644
--- a/freebsd/sys/net80211/ieee80211_sta.h
+++ b/freebsd/sys/net80211/ieee80211_sta.h
@@ -33,4 +33,10 @@
void ieee80211_sta_attach(struct ieee80211com *);
void ieee80211_sta_detach(struct ieee80211com *);
void ieee80211_sta_vattach(struct ieee80211vap *);
+
+/*
+ * Used by the adhoc/mesh/tdma paths.
+ */
+extern int ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
+ const struct ieee80211_frame *wh);
#endif /* !_NET80211_IEEE80211_STA_H_ */
diff --git a/freebsd/sys/net80211/ieee80211_superg.c b/freebsd/sys/net80211/ieee80211_superg.c
index 6323fb26..2ca3e61a 100644
--- a/freebsd/sys/net80211/ieee80211_superg.c
+++ b/freebsd/sys/net80211/ieee80211_superg.c
@@ -30,6 +30,8 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/local/opt_wlan.h>
+#ifdef IEEE80211_SUPPORT_SUPERG
+
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -38,11 +40,12 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
-#include <net/bpf.h>
-#include <net/ethernet.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_llc.h>
#include <net/if_media.h>
+#include <net/bpf.h>
+#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_input.h>
@@ -86,7 +89,7 @@ __FBSDID("$FreeBSD$");
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,
+SYSCTL_INT(_net_wlan, OID_AUTO, ffppsmin, 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,
@@ -98,25 +101,33 @@ 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_FF_LOCK_INIT(ic, ic->ic_name);
+
+ sg = (struct ieee80211_superg *) IEEE80211_MALLOC(
+ sizeof(struct ieee80211_superg), M_80211_VAP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
+ if (sg == NULL) {
+ printf("%s: cannot allocate SuperG state block\n",
+ __func__);
+ return;
}
- ieee80211_ffagemax = msecs_to_ticks(150);
+ ic->ic_superg = sg;
+
+ /*
+ * Default to not being so aggressive for FF/AMSDU
+ * aging, otherwise we may hold a frame around
+ * for way too long before we expire it out.
+ */
+ ieee80211_ffagemax = msecs_to_ticks(2);
}
void
ieee80211_superg_detach(struct ieee80211com *ic)
{
+ IEEE80211_FF_LOCK_DESTROY(ic);
+
if (ic->ic_superg != NULL) {
- free(ic->ic_superg, M_80211_VAP);
+ IEEE80211_FREE(ic->ic_superg, M_80211_VAP);
ic->ic_superg = NULL;
}
}
@@ -190,7 +201,7 @@ ieee80211_parse_ath(struct ieee80211_node *ni, uint8_t *ie)
(const struct ieee80211_ath_ie *) ie;
ni->ni_ath_flags = ath->ath_capability;
- ni->ni_ath_defkeyix = LE_READ_2(&ath->ath_defkeyix);
+ ni->ni_ath_defkeyix = le16dec(&ath->ath_defkeyix);
}
int
@@ -211,7 +222,7 @@ ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
}
ath = (const struct ieee80211_ath_ie *)frm;
capschanged = (ni->ni_ath_flags != ath->ath_capability);
- defkeyix = LE_READ_2(ath->ath_defkeyix);
+ defkeyix = le16dec(ath->ath_defkeyix);
if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
ni->ni_ath_flags = ath->ath_capability;
ni->ni_ath_defkeyix = defkeyix;
@@ -330,43 +341,6 @@ ieee80211_ff_decap(struct ieee80211_node *ni, struct mbuf *m)
}
/*
- * 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
@@ -389,16 +363,15 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace,
goto bad;
}
m1->m_nextpkt = NULL;
+
/*
- * Include fast frame headers in adjusting header layout.
+ * Adjust to include 802.11 header requirement.
*/
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);
+ m1 = ieee80211_mbuf_adjust(vap, hdrspace, key, m1);
if (m1 == NULL) {
+ printf("%s: failed initial mbuf_adjust\n", __func__);
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
m_freem(m2);
goto bad;
@@ -406,17 +379,15 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace,
/*
* 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
+ * and adjust for possible 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);
+ m2 = ieee80211_mbuf_adjust(vap, 4, NULL, m2);
if (m2 == NULL) {
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
+ printf("%s: failed second \n", __func__);
goto bad;
}
@@ -424,10 +395,10 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace,
* Now do tunnel encapsulation. First, each
* frame gets a standard encapsulation.
*/
- m1 = ff_encap1(vap, m1, &eh1);
+ m1 = ieee80211_ff_encap1(vap, m1, &eh1);
if (m1 == NULL)
goto bad;
- m2 = ff_encap1(vap, m2, &eh2);
+ m2 = ieee80211_ff_encap1(vap, m2, &eh2);
if (m2 == NULL)
goto bad;
@@ -454,15 +425,14 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace,
}
/*
- * 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.
+ * A-MSDU's are just appended; the "I'm A-MSDU!" bit is in the
+ * QoS header.
*
* 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);
+ M_PREPEND(m1, sizeof(uint32_t)+2, M_NOWAIT);
if (m1 == NULL) { /* XXX cannot happen */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for tunnel header\n", __func__);
@@ -471,7 +441,7 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace,
}
memset(mtod(m1, void *), 0, sizeof(uint32_t)+2);
- M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT);
+ M_PREPEND(m1, sizeof(struct llc), M_NOWAIT);
if (m1 == NULL) { /* XXX cannot happen */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for llc header\n", __func__);
@@ -490,6 +460,7 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace,
return m1;
bad:
+ vap->iv_stats.is_ff_encapfail++;
if (m1 != NULL)
m_freem(m1);
if (m2 != NULL)
@@ -497,26 +468,127 @@ bad:
return NULL;
}
+/*
+ * A-MSDU encapsulation.
+ *
+ * This assumes just two frames for now, since we're borrowing the
+ * same queuing code and infrastructure as fast-frames.
+ *
+ * There must be two packets chained with m_nextpkt.
+ * We do header adjustment for each, and then concatenate the mbuf chains
+ * to form a single frame for transmission.
+ */
+struct mbuf *
+ieee80211_amsdu_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace,
+ struct ieee80211_key *key)
+{
+ struct mbuf *m2;
+ struct ether_header eh1, eh2;
+ 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 A-MSDU header 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) +
+ 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, 4, 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 = ieee80211_ff_encap1(vap, m1, &eh1);
+ if (m1 == NULL)
+ goto bad;
+ m2 = ieee80211_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.
+ */
+ m->m_next = m2; /* NB: last mbuf from above */
+ m1->m_pkthdr.len += m2->m_pkthdr.len;
+
+ vap->iv_stats.is_amsdu_encap++;
+
+ return m1;
+bad:
+ vap->iv_stats.is_amsdu_encapfail++;
+ 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;
+ struct ieee80211com *ic = ni->ni_ic;
+
+ IEEE80211_TX_LOCK_ASSERT(ic);
/* 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
+ if (m != NULL)
+ (void) ieee80211_parent_xmitpkt(ic, m);
+ else
ieee80211_free_node(ni);
}
@@ -554,39 +626,47 @@ 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;
+
+ IEEE80211_FF_LOCK(ic);
+ if (sq->depth == 0) {
+ IEEE80211_FF_UNLOCK(ic);
+ return; /* nothing to do */
+ }
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 */
+ int tid = WME_AC_TO_TID(M_WME_GETAC(m));
+
+ /* clear staging 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;
+ KASSERT(ni->ni_tx_superg[tid] == m, ("staging queue empty"));
+ ni->ni_tx_superg[tid] = 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);
+ IEEE80211_FF_UNLOCK(ic);
+ IEEE80211_TX_LOCK(ic);
ff_flush(head, m);
+ IEEE80211_TX_UNLOCK(ic);
}
static void
-stageq_add(struct ieee80211_stageq *sq, struct mbuf *m)
+stageq_add(struct ieee80211com *ic, struct ieee80211_stageq *sq, struct mbuf *m)
{
int age = ieee80211_ffagemax;
+
+ IEEE80211_FF_LOCK_ASSERT(ic);
+
if (sq->tail != NULL) {
sq->tail->m_nextpkt = m;
age -= M_AGE_GET(sq->head);
@@ -600,10 +680,12 @@ stageq_add(struct ieee80211_stageq *sq, struct mbuf *m)
}
static void
-stageq_remove(struct ieee80211_stageq *sq, struct mbuf *mstaged)
+stageq_remove(struct ieee80211com *ic, struct ieee80211_stageq *sq, struct mbuf *mstaged)
{
struct mbuf *m, *mprev;
+ IEEE80211_FF_LOCK_ASSERT(ic);
+
mprev = NULL;
for (m = sq->head; m != NULL; m = m->m_nextpkt) {
if (m == mstaged) {
@@ -628,6 +710,7 @@ ff_approx_txtime(struct ieee80211_node *ni,
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
uint32_t framelen;
+ uint32_t frame_time;
/*
* Approximate the frame length to be transmitted. A swag to add
@@ -644,7 +727,21 @@ ff_approx_txtime(struct ieee80211_node *ni,
framelen += 24;
if (m2 != NULL)
framelen += m2->m_pkthdr.len;
- return ieee80211_compute_duration(ic->ic_rt, framelen, ni->ni_txrate, 0);
+
+ /*
+ * For now, we assume non-shortgi, 20MHz, just because I want to
+ * at least test 802.11n.
+ */
+ if (ni->ni_txrate & IEEE80211_RATE_MCS)
+ frame_time = ieee80211_compute_duration_ht(framelen,
+ ni->ni_txrate,
+ IEEE80211_HT_RC_2_STREAMS(ni->ni_txrate),
+ 0, /* isht40 */
+ 0); /* isshortgi */
+ else
+ frame_time = ieee80211_compute_duration(ic->ic_rt, framelen,
+ ni->ni_txrate, 0);
+ return (frame_time);
}
/*
@@ -664,6 +761,13 @@ ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m)
struct mbuf *mstaged;
uint32_t txtime, limit;
+ IEEE80211_TX_UNLOCK_ASSERT(ic);
+
+ IEEE80211_LOCK(ic);
+ limit = IEEE80211_TXOP_TO_US(
+ ic->ic_wme.wme_chanParams.cap_wmeParams[pri].wmep_txopLimit);
+ IEEE80211_UNLOCK(ic);
+
/*
* Check if the supplied frame can be aggregated.
*
@@ -671,9 +775,14 @@ ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m)
* 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_FF_LOCK(ic);
+ tap = &ni->ni_tx_ampdu[WME_AC_TO_TID(pri)];
+ mstaged = ni->ni_tx_superg[WME_AC_TO_TID(pri)];
+ /* XXX NOTE: reusing packet counter state from A-MPDU */
+ /*
+ * XXX NOTE: this means we're double-counting; it should just
+ * be done in ieee80211_output.c once for both superg and A-MPDU.
+ */
ieee80211_txampdu_count_packet(tap);
/*
@@ -684,7 +793,7 @@ ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m)
if (vap->iv_opmode != IEEE80211_M_STA &&
ETHER_IS_MULTICAST(mtod(m, struct ether_header *)->ether_dhost)) {
/* XXX flush staged frame? */
- IEEE80211_UNLOCK(ic);
+ IEEE80211_FF_UNLOCK(ic);
return m;
}
/*
@@ -693,15 +802,13 @@ ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m)
*/
if (mstaged == NULL &&
ieee80211_txampdu_getpps(tap) < ieee80211_ffppsmin) {
- IEEE80211_UNLOCK(ic);
+ IEEE80211_FF_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) {
/*
@@ -713,16 +820,18 @@ ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m)
"%s: txtime %u exceeds txop limit %u\n",
__func__, txtime, limit);
- tap->txa_private = NULL;
+ ni->ni_tx_superg[WME_AC_TO_TID(pri)] = NULL;
if (mstaged != NULL)
- stageq_remove(sq, mstaged);
- IEEE80211_UNLOCK(ic);
+ stageq_remove(ic, sq, mstaged);
+ IEEE80211_FF_UNLOCK(ic);
if (mstaged != NULL) {
+ IEEE80211_TX_LOCK(ic);
IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
"%s: flush staged frame", __func__);
/* encap and xmit */
ff_transmit(ni, mstaged);
+ IEEE80211_TX_UNLOCK(ic);
}
return m; /* NB: original frame */
}
@@ -734,9 +843,9 @@ ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m)
* hold their node reference.
*/
if (mstaged != NULL) {
- tap->txa_private = NULL;
- stageq_remove(sq, mstaged);
- IEEE80211_UNLOCK(ic);
+ ni->ni_tx_superg[WME_AC_TO_TID(pri)] = NULL;
+ stageq_remove(ic, sq, mstaged);
+ IEEE80211_FF_UNLOCK(ic);
IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
"%s: aggregate fast-frame", __func__);
@@ -752,13 +861,13 @@ ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m)
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;
+ KASSERT(ni->ni_tx_superg[WME_AC_TO_TID(pri)] == NULL,
+ ("ni_tx_superg[]: %p",
+ ni->ni_tx_superg[WME_AC_TO_TID(pri)]));
+ ni->ni_tx_superg[WME_AC_TO_TID(pri)] = m;
- stageq_add(sq, m);
- sg->ff_stageqdepth++;
- IEEE80211_UNLOCK(ic);
+ stageq_add(ic, sq, m);
+ IEEE80211_FF_UNLOCK(ic);
IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
"%s: stage frame, %u queued", __func__, sq->depth);
@@ -767,6 +876,30 @@ ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m)
return mstaged;
}
+struct mbuf *
+ieee80211_amsdu_check(struct ieee80211_node *ni, struct mbuf *m)
+{
+ /*
+ * XXX TODO: actually enforce the node support
+ * and HTCAP requirements for the maximum A-MSDU
+ * size.
+ */
+
+ /* First: software A-MSDU transmit? */
+ if (! ieee80211_amsdu_tx_ok(ni))
+ return (m);
+
+ /* Next - EAPOL? Nope, don't aggregate; we don't QoS encap them */
+ if (m->m_flags & (M_EAPOL | M_MCAST | M_BCAST))
+ return (m);
+
+ /* Next - needs to be a data frame, non-broadcast, etc */
+ if (ETHER_IS_MULTICAST(mtod(m, struct ether_header *)->ether_dhost))
+ return (m);
+
+ return (ieee80211_ff_check(ni, m));
+}
+
void
ieee80211_ff_node_init(struct ieee80211_node *ni)
{
@@ -783,27 +916,40 @@ 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;
+ struct mbuf *m, *next_m, *head;
+ int tid;
- IEEE80211_LOCK(ic);
+ IEEE80211_FF_LOCK(ic);
head = NULL;
- for (ac = 0; ac < WME_NUM_AC; ac++) {
- tap = &ni->ni_tx_ampdu[ac];
- m = tap->txa_private;
+ for (tid = 0; tid < WME_NUM_TID; tid++) {
+ int ac = TID_TO_WME_AC(tid);
+ /*
+ * XXX Initialise the packet counter.
+ *
+ * This may be double-work for 11n stations;
+ * but without it we never setup things.
+ */
+ ieee80211_txampdu_init_pps(&ni->ni_tx_ampdu[tid]);
+ m = ni->ni_tx_superg[tid];
if (m != NULL) {
- tap->txa_private = NULL;
- stageq_remove(&sg->ff_stageq[ac], m);
+ ni->ni_tx_superg[tid] = NULL;
+ stageq_remove(ic, &sg->ff_stageq[ac], m);
m->m_nextpkt = head;
head = m;
}
}
- IEEE80211_UNLOCK(ic);
+ IEEE80211_FF_UNLOCK(ic);
- for (m = head; m != NULL; m = m->m_nextpkt) {
+ /*
+ * Free mbufs, taking care to not dereference the mbuf after
+ * we free it (hence grabbing m_nextpkt before we free it.)
+ */
+ m = head;
+ while (m != NULL) {
+ next_m = m->m_nextpkt;
m_freem(m);
ieee80211_free_node(ni);
+ m = next_m;
}
}
@@ -897,6 +1043,7 @@ superg_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
default:
return ENOSYS;
}
- return 0;
}
IEEE80211_IOCTL_SET(superg, superg_ioctl_set80211);
+
+#endif /* IEEE80211_SUPPORT_SUPERG */
diff --git a/freebsd/sys/net80211/ieee80211_superg.h b/freebsd/sys/net80211/ieee80211_superg.h
index 970a5fef..2f8628c3 100644
--- a/freebsd/sys/net80211/ieee80211_superg.h
+++ b/freebsd/sys/net80211/ieee80211_superg.h
@@ -66,7 +66,6 @@ struct ieee80211_stageq {
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 *);
@@ -83,38 +82,63 @@ int ieee80211_parse_athparams(struct ieee80211_node *, uint8_t *,
void ieee80211_ff_node_init(struct ieee80211_node *);
void ieee80211_ff_node_cleanup(struct ieee80211_node *);
+static inline int
+ieee80211_amsdu_tx_ok(struct ieee80211_node *ni)
+{
+
+ /* First: software A-MSDU transmit? */
+ if ((ni->ni_ic->ic_caps & IEEE80211_C_SWAMSDUTX) == 0)
+ return (0);
+
+ /* Next: does the VAP have AMSDU TX enabled? */
+ if ((ni->ni_vap->iv_flags_ht & IEEE80211_FHT_AMSDU_TX) == 0)
+ return (0);
+
+ /* Next: 11n node? (assumed that A-MSDU TX to HT nodes is ok */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0)
+ return (0);
+
+ /* ok, we can at least /do/ AMSDU to this node */
+ return (1);
+}
+
+struct mbuf * ieee80211_amsdu_check(struct ieee80211_node *ni, struct mbuf *m);
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)
+ieee80211_ff_age_all(struct ieee80211com *ic, int quanta)
{
struct ieee80211_superg *sg = ic->ic_superg;
- if (sg != NULL && sg->ff_stageq[ac].depth)
- ieee80211_ff_age(ic, &sg->ff_stageq[ac], 0x7fffffff);
+ if (sg != NULL) {
+ ieee80211_ff_age(ic, &sg->ff_stageq[WME_AC_VO], quanta);
+ ieee80211_ff_age(ic, &sg->ff_stageq[WME_AC_VI], quanta);
+ ieee80211_ff_age(ic, &sg->ff_stageq[WME_AC_BE], quanta);
+ ieee80211_ff_age(ic, &sg->ff_stageq[WME_AC_BK], quanta);
+ }
}
static __inline void
-ieee80211_ff_age_all(struct ieee80211com *ic, int quanta)
+ieee80211_ff_flush(struct ieee80211com *ic, int ac)
{
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);
- }
+ if (sg != NULL)
+ ieee80211_ff_age(ic, &sg->ff_stageq[ac], 0x7fffffff);
+}
+
+static __inline void
+ieee80211_ff_flush_all(struct ieee80211com *ic)
+{
+ ieee80211_ff_age_all(ic, 0x7fffffff);
}
struct mbuf *ieee80211_ff_encap(struct ieee80211vap *, struct mbuf *,
int, struct ieee80211_key *);
+struct mbuf * ieee80211_amsdu_encap(struct ieee80211vap *vap, struct mbuf *m1,
+ int hdrspace, struct ieee80211_key *key);
struct mbuf *ieee80211_ff_decap(struct ieee80211_node *, struct mbuf *);
diff --git a/freebsd/sys/net80211/ieee80211_tdma.c b/freebsd/sys/net80211/ieee80211_tdma.c
index 98fdd5e4..c14ccab5 100644
--- a/freebsd/sys/net80211/ieee80211_tdma.c
+++ b/freebsd/sys/net80211/ieee80211_tdma.c
@@ -38,6 +38,8 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/local/opt_tdma.h>
#include <rtems/bsd/local/opt_wlan.h>
+#ifdef IEEE80211_SUPPORT_TDMA
+
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -115,7 +117,7 @@ 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);
+ int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf);
static int tdma_update(struct ieee80211vap *vap,
const struct ieee80211_tdma_param *tdma, struct ieee80211_node *ni,
int pickslot);
@@ -149,8 +151,9 @@ ieee80211_tdma_vattach(struct ieee80211vap *vap)
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);
+ ts = (struct ieee80211_tdma_state *) IEEE80211_MALLOC(
+ sizeof(struct ieee80211_tdma_state), M_80211_VAP,
+ IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
if (ts == NULL) {
printf("%s: cannot allocate TDMA state block\n", __func__);
/* NB: fall back to adhdemo mode */
@@ -199,7 +202,7 @@ tdma_vdetach(struct ieee80211vap *vap)
return;
}
ts->tdma_opdetach(vap);
- free(vap->iv_tdma, M_80211_VAP);
+ IEEE80211_FREE(vap->iv_tdma, M_80211_VAP);
vap->iv_tdma = NULL;
setackpolicy(vap->iv_ic, 0); /* enable ACK's */
@@ -208,9 +211,9 @@ tdma_vdetach(struct ieee80211vap *vap)
static void
sta_leave(void *arg, struct ieee80211_node *ni)
{
- struct ieee80211vap *vap = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
- if (ni->ni_vap == vap && ni != vap->iv_bss)
+ if (ni != vap->iv_bss)
ieee80211_node_leave(ni);
}
@@ -245,7 +248,8 @@ tdma_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
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);
+ ieee80211_iterate_nodes_vap(&ic->ic_sta, vap,
+ sta_leave, NULL);
}
if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
ieee80211_check_scan(vap,
@@ -288,6 +292,8 @@ tdma_beacon_miss(struct ieee80211vap *vap)
{
struct ieee80211_tdma_state *ts = vap->iv_tdma;
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
KASSERT((vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
KASSERT(vap->iv_state == IEEE80211_S_RUN,
("wrong state %d", vap->iv_state));
@@ -318,7 +324,7 @@ tdma_beacon_miss(struct ieee80211vap *vap)
static void
tdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
- int subtype, int rssi, int nf)
+ int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
@@ -329,7 +335,8 @@ tdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *);
struct ieee80211_scanparams scan;
- if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ /* XXX TODO: use rxstatus to determine off-channel beacons */
+ if (ieee80211_parse_beacon(ni, m0, ic->ic_curchan, &scan) != 0)
return;
if (scan.tdma == NULL) {
/*
@@ -339,8 +346,7 @@ tdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
*/
IEEE80211_DISCARD(vap,
IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
+ wh, ieee80211_mgt_subtype_name(subtype),
"%s", "no TDMA ie");
vap->iv_stats.is_rx_mgtdiscard++;
return;
@@ -389,7 +395,7 @@ tdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
* 2x parsing of the frame but should happen infrequently
*/
}
- ts->tdma_recv_mgmt(ni, m0, subtype, rssi, nf);
+ ts->tdma_recv_mgmt(ni, m0, subtype, rxs, rssi, nf);
}
/*
@@ -742,7 +748,7 @@ 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;
+ return ENOSYS;
switch (ireq->i_type) {
case IEEE80211_IOC_TDMA_SLOT:
@@ -770,7 +776,7 @@ 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;
+ return ENOSYS;
switch (ireq->i_type) {
case IEEE80211_IOC_TDMA_SLOT:
@@ -820,3 +826,5 @@ restart:
return ERESTART;
}
IEEE80211_IOCTL_SET(tdma, tdma_ioctl_set80211);
+
+#endif /* IEEE80211_SUPPORT_TDMA */
diff --git a/freebsd/sys/net80211/ieee80211_tdma.h b/freebsd/sys/net80211/ieee80211_tdma.h
index 2fe591f7..76b9ed1c 100644
--- a/freebsd/sys/net80211/ieee80211_tdma.h
+++ b/freebsd/sys/net80211/ieee80211_tdma.h
@@ -81,7 +81,8 @@ struct ieee80211_tdma_state {
int (*tdma_newstate)(struct ieee80211vap *, enum ieee80211_state,
int arg);
void (*tdma_recv_mgmt)(struct ieee80211_node *,
- struct mbuf *, int, int, int);
+ struct mbuf *, int,
+ const struct ieee80211_rx_stats *rxs, int, int);
void (*tdma_opdetach)(struct ieee80211vap *);
};
diff --git a/freebsd/sys/net80211/ieee80211_var.h b/freebsd/sys/net80211/ieee80211_var.h
index e4b00099..1b73d392 100644
--- a/freebsd/sys/net80211/ieee80211_var.h
+++ b/freebsd/sys/net80211/ieee80211_var.h
@@ -55,7 +55,7 @@
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_scan.h>
-#define IEEE80211_TXPOWER_MAX 100 /* .5 dbM (XXX units?) */
+#define IEEE80211_TXPOWER_MAX 100 /* .5 dBm (XXX units?) */
#define IEEE80211_TXPOWER_MIN 0 /* kill radio */
#define IEEE80211_DTIM_DEFAULT 1 /* default DTIM period */
@@ -84,7 +84,16 @@
#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))
+/* XXX TODO: cap this at 1, in case hz is not 1000 */
+#define IEEE80211_TU_TO_TICKS(x)(((uint64_t)(x) * 1024 * hz) / (1000 * 1000))
+
+/*
+ * Technically, vhtflags may be 0 /and/ 11ac is enabled.
+ * At some point ic should just grow a flag somewhere that
+ * says that VHT is supported - and then this macro can be
+ * changed.
+ */
+#define IEEE80211_CONF_VHT(ic) ((ic)->ic_vhtcaps != 0)
/*
* 802.11 control state is split into a common portion that maps
@@ -116,13 +125,16 @@ struct ieee80211_superg;
struct ieee80211_frame;
struct ieee80211com {
- struct ifnet *ic_ifp; /* associated device */
+ void *ic_softc; /* driver softc */
+ const char *ic_name; /* usually device name */
ieee80211_com_lock_t ic_comlock; /* state update lock */
+ ieee80211_tx_lock_t ic_txlock; /* ic/vap TX lock */
+ ieee80211_ff_lock_t ic_fflock; /* stageq/ni_tx_superg lock */
+ LIST_ENTRY(ieee80211com) ic_next; /* on global list */
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 */
@@ -130,6 +142,12 @@ struct ieee80211com {
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 */
+ struct task ic_chw_task; /* deferred HT CHW update */
+ struct task ic_wme_task; /* deferred WME update */
+ struct task ic_restart_task; /* deferred device restart */
+
+ counter_u64_t ic_ierrors; /* input errors */
+ counter_u64_t ic_oerrors; /* output errors */
uint32_t ic_flags; /* state flags */
uint32_t ic_flags_ext; /* extended state flags */
@@ -139,11 +157,13 @@ struct ieee80211com {
uint32_t ic_htcaps; /* HT capabilities */
uint32_t ic_htextcaps; /* HT extended capabilities */
uint32_t ic_cryptocaps; /* crypto capabilities */
- uint8_t ic_modecaps[2]; /* set of mode capabilities */
+ /* set of mode capabilities */
+ uint8_t ic_modecaps[IEEE80211_MODE_BYTES];
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 */
+ uint8_t ic_macaddr[IEEE80211_ADDR_LEN];
uint16_t ic_bintval; /* beacon interval */
uint16_t ic_lintval; /* listen interval */
uint16_t ic_holdover; /* PM hold over duration */
@@ -190,6 +210,7 @@ struct ieee80211com {
struct ieee80211_dfs_state ic_dfs; /* DFS state */
struct ieee80211_scan_state *ic_scan; /* scan state */
+ struct ieee80211_scan_methods *ic_scan_methods; /* scan methods */
int ic_lastdata; /* time of last data frame */
int ic_lastscan; /* time last scan completed */
@@ -216,6 +237,13 @@ struct ieee80211com {
uint8_t ic_rxstream; /* # RX streams */
uint8_t ic_txstream; /* # TX streams */
+ /* VHT information */
+ uint32_t ic_vhtcaps; /* VHT capabilities */
+ uint32_t ic_vhtextcaps; /* VHT extended capabilities (TODO) */
+ struct ieee80211_vht_mcs_info ic_vht_mcsinfo; /* Support TX/RX VHT MCS */
+ uint32_t ic_flags_vht; /* VHT state flags */
+ uint32_t ic_vht_spare[3];
+
/* optional state for Atheros SuperG protocol extensions */
struct ieee80211_superg *ic_superg;
@@ -233,6 +261,11 @@ struct ieee80211com {
const uint8_t [IEEE80211_ADDR_LEN],
const uint8_t [IEEE80211_ADDR_LEN]);
void (*ic_vap_delete)(struct ieee80211vap *);
+ /* device specific ioctls */
+ int (*ic_ioctl)(struct ieee80211com *,
+ u_long, void *);
+ /* start/stop device */
+ void (*ic_parent)(struct ieee80211com *);
/* operating mode attachment */
ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX];
/* return hardware/radio capabilities */
@@ -242,6 +275,13 @@ struct ieee80211com {
int (*ic_setregdomain)(struct ieee80211com *,
struct ieee80211_regdomain *,
int, struct ieee80211_channel []);
+
+ int (*ic_set_quiet)(struct ieee80211_node *,
+ u_int8_t *quiet_elm);
+
+ /* regular transmit */
+ int (*ic_transmit)(struct ieee80211com *,
+ struct mbuf *);
/* send/recv 802.11 management frame */
int (*ic_send_mgmt)(struct ieee80211_node *,
int, int);
@@ -250,11 +290,11 @@ struct ieee80211com {
struct mbuf *,
const struct ieee80211_bpf_params *);
/* update device state for 802.11 slot time change */
- void (*ic_updateslot)(struct ifnet *);
+ void (*ic_updateslot)(struct ieee80211com *);
/* handle multicast state changes */
- void (*ic_update_mcast)(struct ifnet *);
+ void (*ic_update_mcast)(struct ieee80211com *);
/* handle promiscuous mode changes */
- void (*ic_update_promisc)(struct ifnet *);
+ void (*ic_update_promisc)(struct ieee80211com *);
/* new station association callback/notification */
void (*ic_newassoc)(struct ieee80211_node *, int);
/* TDMA update notification */
@@ -318,6 +358,10 @@ struct ieee80211com {
int batimeout, int baseqctl);
void (*ic_ampdu_rx_stop)(struct ieee80211_node *,
struct ieee80211_rx_ampdu *);
+
+ /* The channel width has changed (20<->2040) */
+ void (*ic_update_chw)(struct ieee80211com *);
+
uint64_t ic_spare[7];
};
@@ -335,14 +379,16 @@ struct ieee80211vap {
TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */
struct ieee80211com *iv_ic; /* back ptr to common state */
+ /* MAC address: ifp or ic */
+ uint8_t iv_myaddr[IEEE80211_ADDR_LEN];
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_ifflags; /* ifnet flags */
uint32_t iv_caps; /* capabilities */
uint32_t iv_htcaps; /* HT capabilities */
uint32_t iv_htextcaps; /* HT extended capabilities */
@@ -359,6 +405,13 @@ struct ieee80211vap {
int iv_inact_run; /* authorized setting */
int iv_inact_probe; /* inactive probe time */
+ /* VHT flags */
+ uint32_t iv_flags_vht; /* VHT state flags */
+ uint32_t iv_vhtcaps; /* VHT capabilities */
+ uint32_t iv_vhtextcaps; /* VHT extended capabilities (TODO) */
+ struct ieee80211_vht_mcs_info iv_vht_mcsinfo;
+ uint32_t iv_vht_spare[4];
+
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];
@@ -392,6 +445,7 @@ struct ieee80211vap {
int iv_amsdu_limit; /* A-MSDU tx limit (bytes) */
u_int iv_ampdu_mintraffic[WME_NUM_AC];
+ struct ieee80211_beacon_offsets iv_bcn_off;
uint32_t *iv_aid_bitmap; /* association id map */
uint16_t iv_max_aid;
uint16_t iv_sta_assoc; /* stations associated */
@@ -403,6 +457,12 @@ struct ieee80211vap {
uint8_t iv_dtim_period; /* DTIM period */
uint8_t iv_dtim_count; /* DTIM count from last bcn */
/* set/unset aid pwrsav state */
+ uint8_t iv_quiet; /* Quiet Element */
+ uint8_t iv_quiet_count; /* constant count for Quiet Element */
+ uint8_t iv_quiet_count_value; /* variable count for Quiet Element */
+ uint8_t iv_quiet_period; /* period for Quiet Element */
+ uint16_t iv_quiet_duration; /* duration for Quiet Element */
+ uint16_t iv_quiet_offset; /* offset for Quiet Element */
int iv_csa_count; /* count for doing CSA */
struct ieee80211_node *iv_bss; /* information for this node */
@@ -419,6 +479,8 @@ struct ieee80211vap {
struct ieee80211_appie *iv_appie_wpa;
uint8_t *iv_wpa_ie;
uint8_t *iv_rsn_ie;
+
+ /* Key management */
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];
@@ -428,10 +490,11 @@ struct ieee80211vap {
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]);
+ const struct ieee80211_key *);
void (*iv_key_update_begin)(struct ieee80211vap *);
void (*iv_key_update_end)(struct ieee80211vap *);
+ void (*iv_update_deftxkey)(struct ieee80211vap *,
+ ieee80211_keyix deftxkey);
const struct ieee80211_authenticator *iv_auth; /* authenticator glue */
void *iv_ec; /* private auth state */
@@ -450,9 +513,13 @@ struct ieee80211vap {
void (*iv_opdetach)(struct ieee80211vap *);
/* receive processing */
int (*iv_input)(struct ieee80211_node *,
- struct mbuf *, int, int);
+ struct mbuf *,
+ const struct ieee80211_rx_stats *,
+ int, int);
void (*iv_recv_mgmt)(struct ieee80211_node *,
- struct mbuf *, int, int, int);
+ struct mbuf *, int,
+ const struct ieee80211_rx_stats *,
+ int, int);
void (*iv_recv_ctl)(struct ieee80211_node *,
struct mbuf *, int);
void (*iv_deliver_data)(struct ieee80211vap *,
@@ -471,12 +538,18 @@ struct ieee80211vap {
/* power save handling */
void (*iv_update_ps)(struct ieee80211vap *, int);
int (*iv_set_tim)(struct ieee80211_node *, int);
+ void (*iv_node_ps)(struct ieee80211_node *, int);
+ void (*iv_sta_ps)(struct ieee80211vap *, int);
+ void (*iv_recv_pspoll)(struct ieee80211_node *,
+ struct mbuf *);
+
/* 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 *);
+ const struct sockaddr *, struct route *);
+
uint64_t iv_spare[6];
};
MALLOC_DECLARE(M_80211_VAP);
@@ -555,11 +628,12 @@ MALLOC_DECLARE(M_80211_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_SCAN_OFFLOAD 0x00080000 /* CONF: scan is fully offloaded */
#define IEEE80211_FEXT_BITS \
"\20\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\10RESUME" \
"\0114ADDR\12NONEPR_PR\13SWBMISS\14DFS\15DOTD\16STATEWAIT\17REINIT" \
- "\20BPF\21WDSLEGACY\22PROBECHAN\23UNIQMAC"
+ "\20BPF\21WDSLEGACY\22PROBECHAN\23UNIQMAC\24SCAN_OFFLOAD"
/* ic_flags_ht/iv_flags_ht */
#define IEEE80211_FHT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */
@@ -586,86 +660,35 @@ MALLOC_DECLARE(M_80211_VAP);
#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"
+#define IEEE80211_FVHT_VHT 0x000000001 /* CONF: VHT supported */
+#define IEEE80211_FVHT_USEVHT40 0x000000002 /* CONF: Use VHT40 */
+#define IEEE80211_FVHT_USEVHT80 0x000000004 /* CONF: Use VHT80 */
+#define IEEE80211_FVHT_USEVHT80P80 0x000000008 /* CONF: Use VHT 80+80 */
+#define IEEE80211_FVHT_USEVHT160 0x000000010 /* CONF: Use VHT160 */
+#define IEEE80211_VFHT_BITS \
+ "\20\1VHT\2VHT40\3VHT80\4VHT80P80\5VHT160"
-/*
- * 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_HTC_RXUNEQUAL 0x00200000 /* CAPABILITY: RX unequal MCS */
-#define IEEE80211_HTC_RXMCS32 0x00400000 /* CAPABILITY: MCS32 support */
-#define IEEE80211_HTC_TXUNEQUAL 0x00800000 /* CAPABILITY: TX unequal MCS */
-#define IEEE80211_HTC_TXMCS32 0x01000000 /* CAPABILITY: MCS32 suport */
-
-#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]);
+int ic_printf(struct ieee80211com *, const char *, ...) __printflike(2, 3);
+void ieee80211_ifattach(struct ieee80211com *);
void ieee80211_ifdetach(struct ieee80211com *);
int ieee80211_vap_setup(struct ieee80211com *, struct ieee80211vap *,
const char name[IFNAMSIZ], int unit,
enum ieee80211_opmode opmode, int flags,
- const uint8_t bssid[IEEE80211_ADDR_LEN],
- const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+ const uint8_t bssid[IEEE80211_ADDR_LEN]);
int ieee80211_vap_attach(struct ieee80211vap *,
- ifm_change_cb_t, ifm_stat_cb_t);
+ ifm_change_cb_t, ifm_stat_cb_t,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
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 *);
+void ieee80211_chan_init(struct ieee80211com *);
struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]);
+struct ieee80211com *ieee80211_find_com(const char *name);
+typedef void ieee80211_com_iter_func(void *, struct ieee80211com *);
+void ieee80211_iterate_coms(ieee80211_com_iter_func *, void *);
int ieee80211_media_change(struct ifnet *);
void ieee80211_media_status(struct ifnet *, struct ifmediareq *);
int ieee80211_ioctl(struct ifnet *, u_long, caddr_t);
@@ -676,20 +699,43 @@ int ieee80211_mhz2ieee(u_int, u_int);
int ieee80211_chan2ieee(struct ieee80211com *,
const struct ieee80211_channel *);
u_int ieee80211_ieee2mhz(u_int, u_int);
+int ieee80211_add_channel(struct ieee80211_channel[], int, int *,
+ uint8_t, uint16_t, int8_t, uint32_t, const uint8_t[]);
+int ieee80211_add_channel_ht40(struct ieee80211_channel[], int, int *,
+ uint8_t, int8_t, uint32_t);
+uint32_t ieee80211_get_channel_center_freq(const struct ieee80211_channel *);
+uint32_t ieee80211_get_channel_center_freq1(const struct ieee80211_channel *);
+uint32_t ieee80211_get_channel_center_freq2(const struct ieee80211_channel *);
+int ieee80211_add_channel_list_2ghz(struct ieee80211_channel[], int, int *,
+ const uint8_t[], int, const uint8_t[], int);
+int ieee80211_add_channel_list_5ghz(struct ieee80211_channel[], int, int *,
+ const uint8_t[], int, const uint8_t[], 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);
+struct ieee80211_channel *ieee80211_lookup_channel_rxstatus(struct ieee80211vap *,
+ const struct ieee80211_rx_stats *);
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]);
+char ieee80211_channel_type_char(const struct ieee80211_channel *c);
+
+#define ieee80211_get_current_channel(_ic) ((_ic)->ic_curchan)
+#define ieee80211_get_home_channel(_ic) ((_ic)->ic_bsschan)
+#define ieee80211_get_vap_desired_channel(_iv) ((_iv)->iv_des_chan)
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_attachv(struct ieee80211com *,
+ struct ieee80211_radiotap_header *th,
+ int tlen, int n_tx_v, uint32_t tx_radiotap,
+ struct ieee80211_radiotap_header *rh,
+ int rlen, int n_rx_v, uint32_t rx_radiotap);
void ieee80211_radiotap_detach(struct ieee80211com *);
void ieee80211_radiotap_vattach(struct ieee80211vap *);
void ieee80211_radiotap_vdetach(struct ieee80211vap *);
@@ -796,6 +842,49 @@ ieee80211_htchanflags(const struct ieee80211_channel *c)
}
/*
+ * Calculate VHT channel promotion flags for a channel.
+ * XXX belongs in ieee80211_vht.h but needs IEEE80211_FVHT_*
+ */
+static __inline int
+ieee80211_vhtchanflags(const struct ieee80211_channel *c)
+{
+
+ if (IEEE80211_IS_CHAN_VHT160(c))
+ return IEEE80211_FVHT_USEVHT160;
+ if (IEEE80211_IS_CHAN_VHT80_80(c))
+ return IEEE80211_FVHT_USEVHT80P80;
+ if (IEEE80211_IS_CHAN_VHT80(c))
+ return IEEE80211_FVHT_USEVHT80;
+ if (IEEE80211_IS_CHAN_VHT40(c))
+ return IEEE80211_FVHT_USEVHT40;
+ if (IEEE80211_IS_CHAN_VHT(c))
+ return IEEE80211_FVHT_VHT;
+ return (0);
+}
+
+/*
+ * Fetch the current TX power (cap) for the given node.
+ *
+ * This includes the node and ic/vap TX power limit as needed,
+ * but it doesn't take into account any per-rate limit.
+ */
+static __inline uint16_t
+ieee80211_get_node_txpower(struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ uint16_t txpower;
+
+ txpower = ni->ni_txpower;
+ txpower = MIN(txpower, ic->ic_txpowlimit);
+ if (ic->ic_curchan != NULL) {
+ txpower = MIN(txpower, 2 * ic->ic_curchan->ic_maxregpower);
+ txpower = MIN(txpower, ic->ic_curchan->ic_maxpower);
+ }
+
+ return (txpower);
+}
+
+/*
* Debugging facilities compiled in when IEEE80211_DEBUG is defined.
*
* The intent is that any problem in the net80211 layer can be
diff --git a/freebsd/sys/net80211/ieee80211_wds.c b/freebsd/sys/net80211/ieee80211_wds.c
index 0c5ea68b..49f1df2e 100644
--- a/freebsd/sys/net80211/ieee80211_wds.c
+++ b/freebsd/sys/net80211/ieee80211_wds.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_media.h>
#include <net/if_llc.h>
#include <net/ethernet.h>
@@ -65,9 +66,10 @@ __FBSDID("$FreeBSD$");
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);
+static int wds_input(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_rx_stats *rxs, int, int);
+static void wds_recv_mgmt(struct ieee80211_node *, struct mbuf *, int subtype,
+ const struct ieee80211_rx_stats *, int, int);
void
ieee80211_wds_attach(struct ieee80211com *ic)
@@ -234,7 +236,6 @@ 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;
@@ -258,16 +259,16 @@ ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
/*
* Duplicate the frame and send it.
*/
- mcopy = m_copypacket(m, M_DONTWAIT);
+ mcopy = m_copypacket(m, M_NOWAIT);
if (mcopy == NULL) {
- ifp->if_oerrors++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
/* 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++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
m_freem(mcopy);
continue;
}
@@ -278,7 +279,7 @@ ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
eh->ether_dhost, NULL,
"%s", "classification failure");
vap->iv_stats.is_tx_classify++;
- ifp->if_oerrors++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
m_freem(mcopy);
ieee80211_free_node(ni);
continue;
@@ -289,22 +290,25 @@ ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
/*
* Encapsulate the packet in prep for transmission.
*/
+ IEEE80211_TX_LOCK(ic);
mcopy = ieee80211_encap(vap, ni, mcopy);
if (mcopy == NULL) {
/* NB: stat+msg handled in ieee80211_encap */
+ IEEE80211_TX_UNLOCK(ic);
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++;
+ err = ieee80211_parent_xmitpkt(ic, mcopy);
+ IEEE80211_TX_UNLOCK(ic);
+ if (!err) {
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);
+ if_inc_counter(ifp, IFCOUNTER_OBYTES,
+ m->m_pkthdr.len);
+ }
}
}
@@ -342,7 +346,6 @@ 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;
@@ -355,7 +358,6 @@ wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
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:
@@ -406,9 +408,9 @@ wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* by the 802.11 layer.
*/
static int
-wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
+wds_input(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
-#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;
@@ -417,7 +419,16 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
struct ether_header *eh;
int hdrspace, need_tap = 1; /* mbuf need to be tapped. */
uint8_t dir, type, subtype, qos;
- uint16_t rxseq;
+ int is_hw_decrypted = 0;
+ int has_decrypted = 0;
+
+ /*
+ * Some devices do hardware decryption all the way through
+ * to pretending the frame wasn't encrypted in the first place.
+ * So, tag it appropriately so it isn't discarded inappropriately.
+ */
+ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED))
+ is_hw_decrypted = 1;
if (m->m_flags & M_AMPDU_MPDU) {
/*
@@ -490,27 +501,13 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
}
IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
ni->ni_noise = nf;
- if (HAS_SEQ(type)) {
+ if (IEEE80211_HAS_SEQ(type, subtype)) {
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 (! ieee80211_check_rxseq(ni, wh)) {
- /* 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);
+ if (! ieee80211_check_rxseq(ni, wh, wh->i_addr1))
goto out;
- }
- ni->ni_rxseqs[tid] = rxseq;
}
switch (type) {
case IEEE80211_FC0_TYPE_DATA:
@@ -559,7 +556,7 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
* crypto cipher modules used to do delayed update
* of replay sequence numbers.
*/
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (is_hw_decrypted || wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
/*
* Discard encrypted frames when privacy is off.
@@ -570,14 +567,14 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
IEEE80211_NODE_STAT(ni, rx_noprivacy);
goto out;
}
- key = ieee80211_crypto_decap(ni, m, hdrspace);
- if (key == NULL) {
+ if (ieee80211_crypto_decap(ni, m, hdrspace, &key) == 0) {
/* 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;
+ wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+ has_decrypted = 1;
} else {
/* XXX M_WEP and IEEE80211_F_PRIVACY */
key = NULL;
@@ -608,7 +605,7 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
/*
* Next strip any MSDU crypto bits.
*/
- if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ if (!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++;
@@ -662,7 +659,8 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
* any non-PAE frames received without encryption.
*/
if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
- (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ ((has_decrypted == 0) && (m->m_flags & M_WEP) == 0) &&
+ (is_hw_decrypted == 0) &&
eh->ether_type != htons(ETHERTYPE_PAE)) {
/*
* Drop unencrypted frames.
@@ -706,18 +704,17 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
#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],
+ ieee80211_mgt_subtype_name(subtype),
ether_sprintf(wh->i_addr2), rssi);
}
#endif
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
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);
+ vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
goto out;
case IEEE80211_FC0_TYPE_CTL:
@@ -732,7 +729,7 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf)
break;
}
err:
- ifp->if_ierrors++;
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
out:
if (m != NULL) {
if (need_tap && ieee80211_radiotap_active_vap(vap))
@@ -743,8 +740,8 @@ out:
}
static void
-wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
- int subtype, int rssi, int nf)
+wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
+ const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
@@ -783,6 +780,7 @@ wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_TIMING_ADV:
case IEEE80211_FC0_SUBTYPE_BEACON:
case IEEE80211_FC0_SUBTYPE_ATIM:
case IEEE80211_FC0_SUBTYPE_DISASSOC:
diff --git a/freebsd/sys/net80211/ieee80211_xauth.c b/freebsd/sys/net80211/ieee80211_xauth.c
index 6640fcff..755cd40b 100644
--- a/freebsd/sys/net80211/ieee80211_xauth.c
+++ b/freebsd/sys/net80211/ieee80211_xauth.c
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>